verified fixes · pipelines · gotchas

s&box gotchas I hit,
so you don't have to.

A growing pile of miscellaneous gotchas, fixes, and pipelines I ran into building s&box games: the kind of thing that quietly eats an afternoon. Every fix here was hit in a real project and stamped with the engine version it was verified on.

880+
gotchas logged
301
tagged fixes
19
method guides
100%
verified, not theorized

Where are you in the build?

From the bench

Player jetpacking above a voxel world
games · playable on s&box
Games

The games I build and ship on s&box, free to play. Berm drops you into a sculptable voxel world with friends. Every fix on this site came out of building it.

Glider launching off a mesa edge at dusk
field kits · s&box libraries · install via library manager
Field Kits

s&box libraries pulled straight out of shipped games: driving, accessory placement, onboarding, day and night, more as they ship. Install one and the source lands in your project, readable and yours.

Latest field notes

26.07.15a
"[GameResource] is obsolete: declare custom resources with [AssetType]"

The [GameResource("Name","ext","desc")] attribute on a GameResource subclass now raises CS0618 obsolete. It is a warning, so a green dotnet build hides it unless the project treats warnings as errors. Switch to [AssetType] with named properties. The type still derives from GameResource, still writes an .ext file, and is still found by ResourceLibrary.GetAll<T>(). Only the declaring attribute changes, and the old Icon argument has no replacement.

26.07.22
"An XML doc comment on the field's own line deletes the field"

A single-line XML doc comment that shares a line with the field it documents pulls the declaration into the comment. The compiler then sees no field at all. Nothing fails at the comment site. The break shows up later as a 'does not contain a definition' error wherever the field is used, which reads like a typo or a missing using. Put every XML doc comment on its own line, directly above the member.

26.08.05
"Bind lazily to a component created in OnStart, not from your own OnStart"

A scene-authored component cannot resolve, inside its own OnStart, a component that another component creates inside that other component's OnStart. Authored beside is not authored before, so the binder looks up the target before it exists, gets null, and holds that null for the whole session behind one warning line. The whole feature does nothing while every part it depends on works. Bind lazily instead: retry the lookup on a short interval until the target resolves, and warn once if it never does.

26.07.22
"A give-up timer gated on a control signal never fires when the control pulses through its reset value"

A timeout accumulator gated on a control-signal sample (throttle > threshold) resets to zero on frames where the signal legitimately reads zero, so it never fills. A gear change drives the throttle magnitude through zero on a fixed cadence, and an unstick routine that alternates gears makes that happen forever, so a stuck vehicle is never declared wrecked. Drive give-up timers from ground-truth state, such as position progress since the last known-good, never from a control signal that pulses through the reset value by design.

26.07.22
"A self-crossing track breaks nearest-waypoint lap position; drive it from a monotone cursor"

On a track that crosses itself (a figure-eight, or any lap that revisits the same ground), a per-frame nearest-waypoint lookup jumps about half a lap at the crossing, because two points that sit close together can be arclength-distant. Drive lap position from a monotone cursor that only advances along the committed branch, do lateral math in a left-of-travel frame, and keep the branch-discrimination cone tighter than the branches' angular separation.

26.07.22
"A green offline dotnet build misses s&box whitelist violations"

Compiling an s&box game assembly outside the editor needs the exact csproj shape the editor generates, and even a perfectly green offline build cannot surface whitelist violations. The SB1000 analyzer runs only in the editor compile pass, so code can build with 0 errors and 0 warnings offline and still be rejected in-editor. Build the editor-generated Code/<project>.csproj directly and confirm against the editor compile_status, never the offline build alone.

26.07.22
"A rotated procedural primitive spins about its own origin, not the part pivot"

A primitive authored with a rotation parameter spins about its own object origin, wherever it was placed, not about the part's intended pivot. A raked hood then see-saws about its own middle and shows half the intended wedge in each direction. Rotate the placement coordinate around the pivot first, then place the box there with the same rotation baked in, and remember that a rotated box's own bounding box is trigonometric, not its declared half-length.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord