Feature Reference

Complete breakdown of every feature in simple-3d — current status, configuration options, known limitations, and links to detailed documentation. Tasks #1–#16 are complete and not listed here; see git log for history.

Status Legend

✅ Done

Implemented, tested, and in the stable public API. Safe to use in production game code.

🔄 In Progress

Partially implemented. API exists but may have edge-case bugs or missing options. Check task notes before relying on it.

📋 Planned

Designed and scheduled but not yet started. API may be drafted in headers (guarded with #if 0) for planning purposes only.

Core & Entity

The foundation of every simple-3d game. All other systems depend on these primitives. See C++ API → Game and C++ API → Entity for full method signatures.

FeatureStatusDetails & Notes
Game base class ✅ Subclass Simple3D::Game and override Start(), Update(float dt), Stop(). The engine calls these on the main thread. dt is clamped to 0.1 s so a debugger breakpoint will not cause a physics explosion.
Single-include header ✅ #include <Simple3D/Simple3D.h> pulls in every public type. Internal Urho3D headers are never exposed — your project's compile units see no engine symbols beyond Simple3D::*.
Entity creation ✅ Entity* e = CreateEntity("name") creates a named scene node and registers it. Names do not need to be unique, but FindEntity() returns the first match.
Entity registry ✅ An internal unordered_map<Node*, Entity*> keeps every entity alive and reachable. Trigger callbacks receive a stable Entity* — never a raw Urho3D pointer. Child entities are registered automatically via CreateChild().
Entity hierarchy ✅ Entity* child = parent->CreateChild("wheel"). Children inherit world-space transform from their parent. GetParent() resolves correctly via registry. DestroyEntity(parent) recursively unregisters the entire subtree.
Entity find ✅ FindEntity("gem_1") searches both root entities and children via the node registry. Returns nullptr if not found. Use a unique name prefix (e.g. "gem_") for bulk lookups combined with GetAllEntities().
Entity destroy ✅ DestroyEntity(e) removes the node from the scene and from the entity registry. Calling DestroyEntity on a pointer that has already been destroyed is safe — it no-ops.
Transform ✅ Position: SetPosition/GetPosition. Rotation: SetRotation/GetRotation (Quaternion). Scale: SetScale. Direction shortcuts: GetForward, GetRight, GetUp. Convenience: MoveRelative(v) translates along local axes, LookAt(target).
Math types ✅ Full suite: Vector2, Vector3, Vector4, Color, Quaternion, Matrix3, Matrix4, Matrix3x4, BoundingBox, Sphere, Ray, Plane, Rect. All types are exposed in Lua (see Lua API → Math).
Window title & clear color ✅ SetWindowTitle("My Game") and SetClearColor(Color(0.1f, 0.1f, 0.12f)). Both can be called at any time, not just from Start().
Time access ✅ GetTime() returns seconds since engine start. GetDeltaTime() is the same value passed to Update(dt). Both are thread-safe reads.
Scene creation helpers ✅ CreateScene(), LoadSceneXML(path), CreateSkybox(path), CreateTerrain(heightmap, material), CreateDirectionalLight, CreateFog. All return the entity or void.
Quit ✅ Quit() shuts down the engine cleanly: calls Stop(), flushes audio, destroys all entities, closes the window.

3D Rendering

Built on Urho3D's forward renderer. All render features are accessible without writing shader code. See C++ API → Camera for camera details.

FeatureStatusDetails & Notes
Static model ✅ AddModel("Models/box.mdl", "Materials/Stone.xml"). Accepted formats: .mdl (native Urho3D), .obj, .fbx. Convert external formats to .mdl with the Urho3D AssetImporter tool for best runtime performance.
Directional light ✅ AddDirectionalLight(color, intensity). Simulates sunlight — affects the whole scene. Point the entity's rotation to control light direction. Supports shadow mapping (configure quality in engine XML).
Point light ✅ AddPointLight(color, range). Range in world units; light falls off to zero at the boundary. Use sparingly on mobile — each per-pixel light has a draw-call cost.
Spot light ✅ AddSpotLight(color, range, angle). Angle is the full cone angle in degrees (not half-angle). Spot light direction follows the entity's forward vector.
Camera — third-person follow ✅ camera.SetFollowTarget(player, height, distance). Camera stays behind the player's last movement direction. Immediately snaps on first frame; use SetFollowSpeed() for lerp smoothing.
Camera — orbit ✅ camera.SetOrbitTarget(entity). Right-mouse-drag orbits; scroll wheel changes distance. Configure pitch limits: SetOrbitPitchLimits(minDeg, maxDeg). Sensitivity: SetOrbitSensitivity(x, y).
Camera — FPS ✅ camera.SetFPSTarget(entity, headOffset). Captures the mouse (SDL_SetRelativeMouseMode). Look sensitivity: SetFPSSensitivity(x, y). Press Escape to detach: camera.Detach().
Camera — smooth follow ✅ SetFollowSpeed(lerpFactor) — 1.0 = instant snap, 0.05 = very slow. Framerate-independent via Lerp(current, target, 1 - pow(1 - speed, dt * 60)).
Camera collision avoidance ✅ A raycast is fired from the target entity toward the desired camera position. If it hits static geometry (collision layer 2), the camera is pulled in to avoid clipping. Enable: camera.SetCollisionAvoidance(true).
Camera — orthographic ✅ camera.Setup2D(pixelsPerUnit) configures orthographic projection matched to sprite scale. camera.SetOrthographic(true) + camera.SetOrthoSize(height) for manual control. Used for 2D games and minimaps.
Split-screen ✅ camera.SetViewport(index, x, y, w, h) — coordinates are 0.0–1.0 normalized. Index 0 and 1 are the two viewport slots. Each viewport has its own Camera object. See Examples → Split-Screen.
FOV, near/far clip ✅ camera.SetFOV(75.0f) (degrees, vertical). camera.SetNearClip(0.1f). camera.SetFarClip(1000.0f). Setting near clip < 0.01 causes z-fighting artifacts.
LookAt ✅ camera.LookAt(entity) or camera.LookAt(Vector3). Rotates the camera node to face the target. Useful for cut-scenes or fixed-perspective rooms.
Skybox ✅ CreateSkybox("Textures/Skybox/day.xml") — loads a Urho3D Skybox XML. The default material uses 6 face textures (cubemap). Only one skybox is active at a time.
Fog ✅ CreateFog(color, start, end). Linear fog between start and end world-unit distances. Setting start = 0 causes the clear color to merge with the fog color.
Particle effects 📋 Task #20. See Roadmap for planned API. Will expose Entity::AddParticleEmitter(xmlPath) + built-in preset XMLs (Sparkle, Smoke, Explosion, Dust, Stars).

Physics — 3D (Bullet)

Powered by Bullet Physics (via Urho3D). Simulation runs at 60 Hz fixed step on the internal physics thread, independent of render framerate. See C++ API → Entity → 3D Physics.

FeatureStatusDetails & Notes
3D rigid body ✅ AddRigidBody(mass). mass = 0 → static body (immovable). Mass in kilograms. Uses GetOrCreateComponent internally, so calling multiple times is safe (returns existing body).
Box collider (3D) ✅ AddBoxCollider(Vector3 size). Size is full extents (not half-extents). Centered on the entity origin unless offset is specified.
Capsule collider (3D) ✅ AddCapsuleCollider(radius, height). Height is the total capsule height including the two hemisphere caps. The recommended shape for characters — smoother on edges than a box.
Sphere collider (3D) ✅ AddSphereCollider(radius). Lowest-cost collision shape. Ideal for rolling objects and simple projectiles.
Mesh collider (3D) ✅ AddMeshCollider("Models/level.mdl"). Triangle mesh — accurate to the model. Static bodies only — Bullet does not support dynamic mesh colliders. For dynamic objects, approximate with primitives.
Trigger volumes ✅ AddTriggerBox/Sphere/Capsule(size) with SetOnTriggerEnter(cb) / SetOnTriggerExit(cb). Callbacks receive Entity* other resolved via the entity registry. Triggers default to collision layer 4; actors must have mask bit 4 set to receive trigger events.
ApplyImpulse / ApplyForce ✅ ApplyImpulse(Vector3) — instantaneous velocity change (use for jumps). ApplyForce(Vector3) — continuous force applied each physics step (use for thrusters). Both require an AddRigidBody(mass > 0) first.
SetLinearVelocity / GetLinearVelocity ✅ Directly sets the velocity vector in world space. Useful for character controllers before Task #21 lands. Note: setting velocity every frame overrides friction — tune as needed.
SetAngularVelocity / GetAngularVelocity ✅ Controls spin. SetAngularFactor(Vector3(0,1,0)) locks rotation to Y-axis only — commonly used for upright characters.
IsOnGround() ✅ Fires a short downward raycast (0.7 m) from the entity's position. Returns true if the ray hits anything on collision layer 2 (static geometry). False-negatives can occur on steep slopes (>45°).
Collision layers / masks ✅ SetCollisionLayer(int) — bit flags for what group this body belongs to. SetCollisionMask(int) — which layers this body collides with. Convention: 1 = actors, 2 = static geometry, 4 = trigger volumes. Player mask = 2 | 4.
Kinematic Character Controller 📋 Task #21. Replaces raw RigidBody for player characters. Will support step-up (0.4 m default), slope sliding (>45°), and IsOnGround() from the KCC state rather than a raycast.
Physics constraints / joints 📋 Task #25. Planned types: Fixed, Hinge (with limits), Slider (moving platforms), BallSocket (chains). Motor support: SetMotorEnabled, SetMotorSpeed, SetMotorMaxForce.

Physics — 2D (Box2D)

Powered by Box2D (via Urho3D). The 2D physics world operates on the XY plane. Mix 3D rendering with 2D physics by using orthographic camera + 2D rigid bodies. See C++ API → Entity → 2D Physics.

FeatureStatusDetails & Notes
2D rigid body ✅ AddRigidBody2D(BodyType2D::Dynamic). Body types: Dynamic (physics-driven), Static (immovable), Kinematic (script-driven, no gravity). Cannot mix 3D and 2D bodies on the same entity.
2D box collider ✅ AddCollisionBox2D(width, height). Center offset: optional third parameter Vector2 center. Supports rotation via entity transform.
2D circle collider ✅ AddCollisionCircle2D(radius). Lowest cost 2D shape — recommended for balls, characters, coins. Center follows entity position.
2D velocity / impulse ✅ SetLinearVelocity2D(Vector2) / ApplyLinearImpulse2D(Vector2). Gravity is applied automatically to Dynamic bodies. Disable gravity per-body: SetGravityScale2D(0.0f).
IsOnGround2D() ✅ Downward raycast 0.6 m in 2D physics space. Returns true if the ray hits a Static or Kinematic body below. Adjust threshold with the optional rayLength parameter.
2D collision categories ✅ Box2D category bits and mask bits via SetCategoryBits2D / SetMaskBits2D. Use 16-bit values (Box2D limitation). Same layer convention as 3D can be used for consistency.

Animation

Urho3D AnimationController with simple-3d convenience wrappers. Animations are stored as .ani files or embedded in exported .fbx/.mdl. See C++ API → Entity → Animation.

FeatureStatusDetails & Notes
AnimationController ✅ AddAnimationController() attaches the controller component. Call once; subsequent calls return the existing component via GetOrCreateComponent. Returns nullptr before this call — guard access in Lua.
Play ✅ Play(name, layer, looped, fadeTime). layer — animations on different layers blend additively. fadeTime seconds to cross-fade from any currently playing animation on that layer. 0 = instant cut.
PlayBlend (additive) ✅ PlayBlend(name, layer, fadeTime) — plays alongside any other animation on the same layer. Use for overlay animations (aiming, hit reactions) on top of a locomotion cycle.
Stop / StopAll ✅ Stop(name) stops a specific clip. StopAll(fadeTime) fades out every playing animation. Safe to call even if nothing is playing.
Blend weight ✅ SetWeight(name, weight) — 0.0 = invisible contribution, 1.0 = full. Interpolate manually each frame to build custom cross-fades. GetWeight(name) returns current weight.
Playback speed ✅ SetSpeed(name, multiplier). 1.0 = normal, 2.0 = double speed, 0.5 = half speed, -1.0 = reverse. Negative speed plays the clip backward.
IsPlaying ✅ IsPlaying(name) — true if the clip is currently active (weight > 0 and not stopped). Use to gate transitions: only call Play("jump") if !IsPlaying("jump").
GetTime ✅ GetTime(name) — playback position in seconds. Use to synchronize sound effects to animation frames (e.g. footstep at 0.25 s and 0.75 s of the walk cycle).
Animation state machine 📋 Task #22. Declarative AnimStateMachine with states, transitions, and conditions (SetBool, SetFloat, SetTrigger — Unity Animator style). Will drive AnimationController internally. Planned blend time support per transition.

Audio

Urho3D audio system wraps OpenAL/SDL_mixer. Streaming for music, buffer-based for SFX. See C++ API → Game → Audio.

FeatureStatusDetails & Notes
Music streaming ✅ PlayMusic("Music/theme.ogg", loop). OGG Vorbis recommended (smaller files); WAV is also accepted. Music streams from disk — not loaded into RAM. Only one music track at a time.
Stop music ✅ StopMusic(). Immediately halts streaming. To fade out, set SetMasterVolume() to 0 over several frames then call StopMusic().
One-shot SFX ✅ PlaySound("Sounds/jump.wav", volume). Volume 0.0–1.0. Sounds are cached after first load. Up to 32 simultaneous sound channels (Urho3D default).
Master volume ✅ SetMasterVolume(0.0f–1.0f). Affects all audio channels including music. Persisting the player's volume preference should be saved manually (or via Task #19 SaveData).
Missing resource logging ✅ PlayMusic() and PlaySound() log an error to the console if the file is not found, instead of crashing. Check the log if audio is silent.
Spatial (3D positional) audio 📋 Task #23. Will expose Entity::AddAudioSource() returning AudioSource*. Features: SetGain, SetRange(near, far), SetPitch, PlayAmbient. Requires Game::SetAudioListener(Entity*) to position the listener. Also needed by Speedy Blupi enemy and footstep audio.

Input

Polling-based input — call from Update(). No event queue to drain. See C++ API → Game → Input and Lua API → Input for the full Key enum.

FeatureStatusDetails & Notes
Keyboard — held ✅ IsKeyDown(Key::W) — true every frame the key is physically held. Use for movement.
Keyboard — pressed (single frame) ✅ IsKeyPressed(Key::Space) — true only on the frame the key transitioned from up → down. Use for jump and toggle actions. Does not repeat.
Mouse position ✅ GetMousePosition() → Vector2 in screen pixels (top-left origin). Not available in FPS mode while mouse is captured.
Mouse delta ✅ GetMouseDelta() → Vector2 pixels moved since last frame. Always valid, including in FPS mouse-capture mode where absolute position is undefined.
Mouse buttons ✅ IsMouseButtonDown(0) — 0=left, 1=right, 2=middle. IsMouseButtonPressed(0) for single-frame click detection.
Touch (multi-touch) ✅ GetTouchCount(), GetTouchPosition(i), GetTouchDelta(i), GetTouchPressure(i). Index 0 = first finger. Pressure is 0.0–1.0 (device-dependent; many return 1.0).
Gamepad / controller 📋 Task #24. Will expose IsGamepadConnected, GetGamepadAxis(GamepadAxis), IsGamepadButtonDown/Pressed(GamepadButton), rumble (SetGamepadRumble), and hot-plug callbacks. Targeting XInput (Windows) and SDL gamepad (Linux/Mac).

Lua Scripting

LuaJIT (Lua 5.1 compatible) via Urho3D's LuaScriptInstance. One script per entity. See Lua API for the full reference.

FeatureStatusDetails & Notes
Script attach per entity ✅ entity->AddScript("Scripts/player.lua"). The script file is loaded from the engine's resource search paths. Missing scripts log an error and skip without crashing.
Lifecycle callbacks ✅ function Start() — called once on attach. function Update(dt) — called every frame. function Stop() — called on detach/destroy. function FixedUpdate(dt) — called at physics step rate (60 Hz).
Urho3D Lua globals ✅ self.node — the entity's Urho3D Node. input — Urho3D Input (lowercase, not the C++ API). cache — ResourceCache. scene — the active Scene. time — Time subsystem. audio — Audio subsystem. renderer — Renderer.
Node extensions (simple-3d additions) ✅ Registered on every Urho3D Node via the binding layer: node:MoveRelative(v), node:IsOnGround(), node:IsOnGround2D(), node:ApplyImpulse(v), node:GetForward(), node:GetRight(). These are not standard Urho3D methods.
AnimationController from Lua ✅ local anim = self.node:GetComponent("AnimationController"). Methods: Play, Stop, StopAll, IsPlaying, SetWeight, SetSpeed, GetTime, SetTime. Guard with if anim then — returns nil before AddAnimationController() is called from C++.
RigidBody from Lua ✅ local rb = self.node:GetComponent("RigidBody"). Full Urho3D RigidBody API is available: ApplyImpulse, SetLinearVelocity, GetLinearVelocity, GetMass. 2D equivalent: "RigidBody2D".
Input from Lua ✅ Use the capitalised global: Input:IsKeyDown(KEY_W). Key constants are prefixed with KEY_. Note: Urho3D's lowercase input global uses a different API — prefer Input: (colon syntax) for consistency with simple-3d conventions.
PlaySound / PlayMusic from Lua 🔄 Part of custom bindings (Task #3, in progress). Currently accessible via Urho3D's audio global with verbose syntax. The simple-3d convenience binding (PlaySound("file.wav")) is not yet exposed.
FindEntity from Lua 🔄 Part of Task #3. Currently use scene:GetChild("name", true) to search the scene tree from Lua. The simple-3d FindEntity wrapper (which also searches the entity registry) is not yet bound to Lua.
MoveToward from Lua 📋 Task #3. Will expose the navigation MoveToward(entity, target, speed) binding so Lua AI scripts can path-find without C++ code.
Script communication ✅ Scripts communicate via Urho3D's SendEvent / SubscribeToEvent. For simpler cases, read shared state from a global Lua table or use scene:GetChild to reach other entities and read variables from their script component.

Recast (nav-mesh generation) + Detour (pathfinding), both via Urho3D. The nav-mesh is baked at runtime from all static models in the scene. See C++ API → Game → Navigation.

FeatureStatusDetails & Notes
Nav-mesh bake ✅ BuildNavMesh(agentRadius, agentHeight, cellSize). Scans all static geometry in the scene and bakes a walkable surface. Call once after loading the scene — baking is synchronous and may take 0.1–0.5 s for complex scenes. Do not call every frame.
FindPath(Entity*) ✅ Returns a vector<Vector3> of waypoints from this entity to the target. Waypoints are in world space. The result is also cached internally for use by MoveToward().
FindPath(Vector3) ✅ Same as above but the target is a world position rather than an entity. Returns empty vector if either start or end is off the nav-mesh.
MoveToward ✅ entity->MoveToward(target, speed). Follows the cached path toward the target at the given speed (units/s). Recomputes the path automatically if the entity drifts > 1 m off the expected track. Returns true when the target is reached.
StopMoving ✅ entity->StopMoving(). Clears the cached path and zeroes the horizontal velocity of the associated rigid body. Call when switching from navigation to manual input.
Nav-mesh debug overlay 📋 Planned visual debugging tool — renders the baked nav-mesh polygons as a semi-transparent overlay in debug builds. Will be toggled via a CMake option or runtime key.

Networking

ENet UDP library via Urho3D. Client/server model with automatic scene entity replication. Custom message IDs 100–32767 are reserved for game code. See C++ API → NetworkManager and Examples → Networking.

FeatureStatusDetails & Notes
Start server ✅ network.StartServer(port, maxClients). Port 27015 is a common choice. maxClients limits simultaneous connections. The server also runs a local game instance — no headless mode currently.
Client connect ✅ network.Connect("192.168.1.10", 27015). Connection is asynchronous; OnServerConnected() fires on success. If the server is unreachable, OnServerDisconnected() fires after a timeout.
Scene entity replication ✅ Automatic via Urho3D's built-in network replication. Entities created by the server are automatically cloned on all clients. Mark an entity for replication: entity->SetReplicated(true).
Custom messages ✅ Send raw byte payloads with a game-defined ID (100–32767). Pack structs into vector<uint8_t> via memcpy. Receive via OnMessage(Connection* sender, int msgId, const vector<uint8_t>& data). IDs 0–99 are reserved by Urho3D.
Broadcast to all clients ✅ network.Broadcast(msgId, data). Sends to every connected client from the server. Not available on the client side — clients send to the server only.
Send to specific client ✅ network.SendToClient(connection, msgId, data). Use the Connection* received in OnClientConnected. Store connections in a list to address specific players.
Send to server ✅ network.SendToServer(msgId, data). Client-side call; routed to the server's OnMessage. The sender in OnMessage identifies which client.
Connection callbacks ✅ OnClientConnected(Connection*), OnClientDisconnected(Connection*) — server side. OnServerConnected(), OnServerDisconnected() — client side. Override in your Game subclass.
Message callback ✅ OnMessage(Connection* sender, int msgId, vector<uint8_t> data) — override in Game. Both server and client receive messages here. Dispatch on msgId to handle different message types.

UI

Currently limited to text labels. Full widget system is planned for Task #17. See C++ API → Label.

FeatureStatusDetails & Notes
Label (text element) ✅ Label* l = CreateLabel(). Methods: SetText(str), SetPosition(x, y) (screen pixels), SetFontSize(px), SetColor(Color), SetVisible(bool). Position (0,0) = top-left. Can be used for HUD counters, debug overlays, subtitles.
UI::Button 📋 Task #17. Planned: SetText, SetSize, SetPosition, SetOnClick(callback). Required for start screen and pause menu in Speedy Blupi.
UI::Image 📋 Task #17. Screen-space texture; SetTexture(path), SetRect, SetColor. For life icons, health bar backgrounds, etc.
UI::Panel 📋 Task #17. Container for grouping UI elements. Transparent or opaque background. Used for pause overlay: show/hide the whole panel group at once.
UI::ProgressBar 📋 Task #17. Horizontal/vertical bar. SetValue(0..1), SetColor. For health, mana, loading screens.
UI::Slider 📋 Task #17. Draggable float value. SetRange(min, max), SetOnChange(callback). For options menus (volume, sensitivity).
Anchor system 📋 Task #17. SetAnchor(Anchor::TopLeft / Center / BottomRight / …) + pixel offset. Allows UI elements to adapt to different window sizes without manual recalculation.
Z-order stacking 📋 Task #17. SetZOrder(int) — higher values render on top. Necessary for modal dialogs over HUD.
Scene transitions (fade) 📋 Task #18. Game::FadeOut(duration, callback) / FadeIn(duration). Used between level loads. Implemented as a full-screen overlay panel with animated opacity.
Save / Load UI state 📋 Task #19. SaveData key-value store for persisting game progress. JSON format. Platform paths: Linux ~/.local/share/<app>/, Windows %APPDATA%\<app>\.

2D Support

2D rendering via Urho3D's sprite/Spriter2D/TileMap subsystems. Use in combination with the orthographic camera and Box2D physics. See C++ API → Entity → 2D Rendering.

FeatureStatusDetails & Notes
Static sprite ✅ AddSprite2D("Sprites/coin.png"). PNG or JPG. The sprite is rendered at the entity's world position. Scale via SetScale(Vector3). Log-error on missing file, no crash.
Animated sprite (Spriter / SCML) ✅ AddAnimatedSprite2D("Sprites/player.scml"). Spriter is an external 2D character animation tool that exports .scml files. Spritesheet must be alongside the .scml.
PlayAnimation2D ✅ PlayAnimation2D("run", loop). Animation name matches the entity name defined in Spriter. Call once to start; the engine loops until a different animation is set.
SetFlipX2D / SetFlipY2D ✅ SetFlipX2D(true) mirrors the sprite horizontally. Used for left/right character facing without separate left-facing sprite sheets.
Tile map (.tmx) ✅ LoadTileMap("Maps/world1.tmx"). Tiled map editor TMX format. Supports orthogonal tile layers. Returns an Entity* holding the TileMap2D component. Object layers are not auto-parsed — read them from the Tiled XML manually if needed.
Orthographic camera helper ✅ camera.Setup2D(pixelsPerUnit). Sets orthographic projection scaled so that 1 world unit = pixelsPerUnit pixels. Commonly 100 PPU for sprite games. Converts pixel sizes to world sizes for physics setup.

Backend Comparison

simple-3d supports two engine backends selectable at CMake configure time via -DSIMPLE3D_ENGINE=U3D or -DSIMPLE3D_ENGINE=NOVA3D. The public API is identical regardless of backend choice.

What is the difference?

U3D is the community-maintained upstream fork of Urho3D at u3d-community/U3D. NOVA3D is Robert Vokac's fork that adds an XNA-style C++ runtime layer, allowing the same game to target both desktop and (future) Android without code changes.

CapabilityU3D backendNOVA3D backend
Linux (x86_64)✅✅
Windows (MSVC)✅📋 planned
Windows (MinGW cross-compile)✅ toolchain provided📋
macOS🔄 untested📋
Android📋📋 primary target
Web / Emscripten📋📋
LuaJIT scripting✅✅
Bullet physics✅✅
Box2D physics✅✅
ENet networking✅🔄
Recast/Detour navigation✅✅
XNA-style content pipeline📋✅ core feature
Toolchain file included✅ MinGW🔄

Platforms

Target platform selection is determined by CMake toolchain files and the active engine backend. Cross-compilation uses MinGW or Android NDK.

PlatformStatusBackendNotes
Linux (x86_64) ✅ U3D or NOVA3D Primary development platform. SDL2 window, OpenGL renderer. Install packages: libsdl2-dev libgl1-mesa-dev libopenal-dev.
Windows (native MSVC) ✅ U3D Visual Studio 2022 or later. Set U3D_HOME to the built U3D directory. DX11 renderer path available.
Windows (MinGW cross-compile) ✅ U3D Build from Linux: cmake -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/MinGW.cmake …. Toolchain file provided. Produces a standalone Windows .exe + DLLs.
macOS 🔄 U3D Build path exists in CMakeLists.txt but has not been tested end-to-end. Homebrew dependencies: sdl2 openal-soft lua. Community testing welcome.
Android 📋 NOVA3D Task #8. Requires Android NDK + NOVA3D backend. Touch input is already abstracted. The primary motivation for the NOVA3D fork is Android support for Speedy Blupi mobile.
Web / Emscripten 📋 TBD Task #9. Emscripten build. Browser canvas output. LuaJIT requires Emscripten-compatible build (possible via the LLVM backend). No ETA.

Roadmap

Tasks are ordered by priority for the Speedy Blupi 3D remake prototype. All tasks after Task #17A are blocked until the acceptance test passes (build + manual run of samples/blupi_proto).

Current status: Task #17A — Stabilization (in progress)

All code changes for stabilization are written. The blocker is a manual build-and-run verification of samples/blupi_proto. Once that passes, all other tasks unblock in the order below.

TaskTitlePriorityFull Description & Planned API
#21 Character Controller 🔥 High Bullet KinematicCharacterController replacing raw RigidBody for player movement. Solves slope sliding and step climbing. API: Entity::AddCharacterController(radius, height), CharacterController::Move(vel, dt), Jump(speed), SetMaxSlope(deg), SetStepHeight(m). Entity::IsOnGround() delegates to KCC state when present.
#24 Gamepad Input 🔥 High SDL gamepad (Linux/Mac) and XInput (Windows). IsGamepadConnected(index), GetGamepadAxis(GamepadAxis, index), IsGamepadButtonDown/Pressed(btn, index). Enums: GamepadAxis::{LeftX,LeftY,RightX,RightY,LTrigger,RTrigger}, GamepadButton::{A,B,X,Y,LB,RB,Start,Select,DPadUp/Down/Left/Right}. Rumble: SetGamepadRumble(lowFreq, highFreq, durationSec). Hot-plug: SetOnGamepadConnected/Disconnected.
#17 Full UI System 🔥 High Required for start screen, pause menu, lives display, score. New types under Simple3D::UI::: Button, Image, Panel, ProgressBar, Slider. Anchor system: SetAnchor(Anchor::TopLeft / Center / BottomRight …). Z-order stacking. Factory methods on Game: CreateButton, CreateImage, CreatePanel, etc.
#18 Scene Management 🔥 High Level transitions for Speedy Blupi's ~30 levels. API: Game::LoadScene(path), UnloadScene(), SetPersistentEntity(Entity*), FadeOut(duration, callback), FadeIn(duration), GetCurrentScene(), SetLevelList(…), LoadNextLevel(), LoadLevel(index). Camera and audio survive transitions; all other entities are destroyed.
#19 Save / Load High Persistent game state (score, level index, settings). Simple3D::SaveData key-value store; types: int, float, bool, std::string. Set(key, val), Get<T>(key, default), Save(slot), Load(slot), Delete(slot), Exists(slot). JSON format. Platform paths: Linux ~/.local/share/<app>/, Windows %APPDATA%\<app>\.
#23 Spatial Audio Medium 3D positional sound for enemy and footstep audio. Entity::AddAudioSource() → AudioSource*. Methods: Play(path), Stop(), SetLooped(bool), IsPlaying(), SetGain(0..1), SetRange(near, far), SetPitch(mult), PlayAmbient(path). Listener: Game::SetAudioListener(entity) (typically camera or player entity).
#20 Particle Effects Medium Visual feedback for jumps, collect events, explosions. Entity::AddParticleEmitter(xmlPath) → ParticleEmitter*. Methods: Play(), Stop(), Burst(count), IsEmitting(), SetEmitting(bool). Built-in presets: Particles/Sparkle.xml, Particles/Smoke.xml, Particles/Explosion.xml, Particles/Dust.xml, Particles/Stars.xml. Billboard helper: Entity::AddBillboard(texturePath).
#22 Animation State Machine Medium Declarative character animation to replace manual if-else chains. Simple3D::AnimStateMachine: AddState(name, animPath, loop), AddTransition(from, to, cond, blendTime=0.2). Conditions: SetBool(key, val), SetFloat(key, val), SetTrigger(key). Update(dt) from Game::Update. GetCurrentState(). Drives the entity's AnimationController internally.
#26 Enemy AI FSM Medium Finite state machine for patrol → alert → chase → attack enemies. Simple3D::BehaviorFSM: AddState(name, onEnter, onUpdate, onExit), AddTransition(from, to, conditionFn), Update(dt), SetState(name). Built-in NavMesh-backed states: AddPatrolState(waypoints, speed), AddChaseState(target, speed, stopDist). Factory: Entity::AddBehaviorFSM().
#25 Physics Constraints Low Moving platforms, hinged doors, levers, chains. Game::CreateConstraint(type, entityA, entityB). Types: ConstraintType::{Fixed, Hinge, Slider, BallSocket}. Hinge: SetHingeAxis(v), SetHingeLimits(minDeg, maxDeg). Slider: SetSliderAxis(v), SetSliderLimits(min, max). Motor: SetMotorEnabled(bool), SetMotorSpeed(f), SetMotorMaxForce(f).
#8 Android Low (future) NOVA3D backend + Android NDK. Touch input is already abstracted. The CMake integration will mirror the desktop build. Primary motivation: Speedy Blupi mobile port. No ETA until NOVA3D backend stabilizes on desktop.
#9 Web / Emscripten Low (future) Browser build via Emscripten. Output: WebAssembly + canvas. LuaJIT requires the Emscripten LLVM path (supported). ENet networking in a browser context needs WebSocket bridging (non-trivial). No ETA.