Skip to content

Migrating to 2.0

shmellyorc edited this page Sep 18, 2026 · 3 revisions

Migrating to VOID 2.0

VOID 2.0 is a major architecture release.

The high-level game workflow is still intentionally familiar — Game, SpriteBatcher, PrimitiveBatcher, assets, fonts, cameras, coroutines, and the normal lifecycle remain recognizable — but the platform, renderer, audio, camera, and several extension contracts were rebuilt.

This page focuses on changes that can affect existing projects.

Platform and Rendering

VOID no longer depends on SFML.Net.

The 2.0 stack is:

Area VOID 2.0
Windowing, displays, events, keyboard, mouse, gamepads SDL3-CS
Built-in renderer Silk.NET.OpenGL
Audio playback Silk.NET.OpenAL
Encoded audio decoding NAudio.SoundFile

Normal game code does not need to use SDL or Silk.NET directly.

See Rendering, Window & Displays, and Audio.

Renderer Plugins

Renderer integration is now based on public renderer-neutral contracts.

A custom backend should work through:

  • IRendererBackend
  • IGraphicsDevice
  • IRendererContext
  • renderer-neutral buffer, texture, shader, and render-target contracts

Do not depend on VOID's internal SDL host.

When a renderer needs native platform handles, use IRendererContext.TryGetNativeHandle(...) and inspect PlatformBackend before interpreting the returned handle.

See Custom Renderers.

Cameras Moved and Became Matrix-Driven

Camera types now live under:

using Void.Engine.Cameras;

The standard Camera derives from BaseCamera and supports:

  • position
  • zoom
  • rotation
  • world bounds
  • view bounds
  • screen/world conversion

VOID also owns the matrix type used by the camera/render pipeline:

Void.Engine.Cameras.Matrix

If old code depended on the previous camera location or an external matrix type for VOID camera work, migrate it to the new camera/matrix path.

Cameras used by a batcher now update automatically from Begin(...). A camera shared by multiple batchers updates only once per rendered frame. Custom time-based cameras should override the protected OnUpdate(FrameTime) hook instead of exposing or manually calling a public camera update method.

See Camera and Matrix.

Input Action State Names

The action snapshot API now separates transition queries from current-state queries.

Use:

state.IsJustPressed("Jump");
state.IsPressed("Jump");
state.IsJustReleased("Jump");
state.IsReleased("Jump");

Detailed transitions are:

ActionState.JustPressed
ActionState.Pressed
ActionState.JustReleased
ActionState.Up

Old IsHeld/held-style action examples should be replaced with IsPressed.

A missing/default action is treated as released.

See Input → Input Actions.

Pathfinding Queries

Use GetIdPath when you want IDs:

List<int> ids =
    pathfinder.GetIdPath(start, target);

Use GetPointPath or GetPath when you want positions:

List<Vect2> points =
    pathfinder.GetPointPath(start, target);

Search defaults are now exposed through:

pathfinder.DefaultAlgorithm
pathfinder.DefaultHeuristic
pathfinder.DefaultDiagonalMode

Custom cost, heuristic, and neighbor-filter callbacks receive point IDs.

See Pathfinding.

Custom Atlas Packers

IAtlasPacker remains the extension point, but custom packers must follow the current geometry contract.

A packer selected through SetAtlasPacker(...) needs a public constructor:

public MyPacker(
    int width,
    int height)

Fragmentation describes external free-space fragmentation, not the percentage of the page that is unused.

Conceptually:

1 - largest allocatable free rectangle area
    ---------------------------------------
              total free area

An empty page and a completely full page both report 0.

Defragmentation is transactional: if a complete valid replacement layout cannot be produced, keep the existing layout and return no moves.

See Atlas Manager → Creating a Custom Packer.

Asset Lifecycle and Packs

Custom IAsset implementations should keep enough source data to reload after Unload() and refresh LastAccessTime when loaded/accessed.

LoadPack(...) and LoadAllPacks(...) create/return pack mounts; they do not automatically add those mounts to asset search order.

Add them explicitly:

var pack =
    AssetManager.Instance.LoadPack(
        "GameAssets.pack");

AssetManager.Instance.AddMountToStart(
    pack);

For multiple packs:

foreach (var pack in
    AssetManager.Instance.LoadAllPacks("Packs"))
{
    AssetManager.Instance.AddMountToEnd(pack);
}

See Asset Management → Mount System.

Packer CLI Command

The installed .NET tool command is:

void-packer

For example:

void-packer build -c Content/ -o Packs/

See CLI Tool.

Logging

Fatal writes a fatal entry and flushes queued logs.

Critical is stronger: it writes crash-style details, flushes, and then throws an InvalidOperationException.

Use Critical only for failures where continuing is not valid.

See Logging.

FastRandom Range Semantics

FastRandom now clearly follows two range conventions:

  • Next* methods: minimum inclusive, maximum exclusive
  • Range* methods: both minimum and maximum inclusive

Equal bounds are supported where the overload permits them.

See FastRandom.

Frame Timing

FrameTime now owns VOID's frame clock and timing state. Engine update paths receive the complete FrameTime context rather than standalone delta values.

FrameTime.TotalTime tracks accumulated real outer-frame time after the engine's maximum-delta clamp and is not multiplied by TimeScale.

In fixed timestep mode, timing is phase-aware:

  • OnUpdate sees the configured fixed step through ElapsedTime, DeltaTime, and UnscaledDeltaTime.
  • Before rendering, VOID switches those values to the actual elapsed rendered-frame time. OnDraw and automatically updated cameras therefore use render timing rather than the fixed simulation interval.
  • The accumulator still determines how many fixed updates are consumed before rendering.

In variable timestep mode, update and rendering both use the actual elapsed frame time.

See Game Class → FrameTime.

What Did Not Change Conceptually

You can still structure a normal game around:

OnEnter()
OnUpdate(FrameTime)
OnDraw(FrameTime)
OnExit()

You can still load assets through AssetManager, draw through the batchers, use render targets and shaders, and keep game code above the renderer/platform boundary.

That continuity is intentional: 2.0 changes the engine's foundations without forcing ordinary game code to become graphics-backend code.


Back to Home

Clone this wiki locally