Character movement and navigation
Examine swept character movement, support tracking, gravity frames, and snapshot-based navigation over stacked surfaces.
Asterune moves characters through a swept kinematic motor rather than treating walking as a freely simulated rigid body. The motor resolves a body box against collision surfaces, tracks floor support, and derives movement states from the result. Navigation separately searches a collision snapshot for a sequence of walkable surfaces and transitions.
The systems share collision geometry but answer different questions. The motor resolves the next displacement. Navigation searches a route under an agent's size and movement assumptions. Receiving a route does not execute its jumps, climbs, or custom links.
Local collider selection
Character collision uses a spatial grid with 32-stud cells. A collider that covers at most 64 cells is filed into those cells; larger colliders enter a separate list. A nearby query combines the current body bounds, externally applied translation, and the endpoint of velocity-based movement, with a 4-stud margin. Opposing translations do not cancel the intermediate extent.
This broad phase returns nearby collider records to the motor. It includes the large-collider list and deduplicates ordinary cell entries. A versioned collider array lets changed slots update their grid membership without rebuilding unchanged bounds. A changed array length or incompatible membership falls back to rebuilding the grid.
The motor also filters collision groups before movement. The character's collision box derives from its configured hitbox and hip offset. The inner-box mode narrows the horizontal dimensions to 70% of the outer box while retaining its height. Collision shape is therefore independent of the visible rig's individual limbs.
Velocity integration and contact sweeps
The client character path uses 240 Hz steps and caps its accumulator at 4 steps
per frame. Standalone controller advancement instead divides its elapsed time
into ceil(dt / (1 / 240)) slices, capped at 8. A large standalone-controller
update can therefore contain slices longer than one nominal motor step.
Each step converts input into a normalized horizontal wish direction, optionally relative to camera heading. On the ground, the wish direction is projected onto the current floor tangent. The resulting target velocity is compared with the existing horizontal velocity.
The motor multiplies that difference by responsiveness and caps its magnitude by the applicable acceleration limit. Ground acceleration, ground deceleration, and air acceleration are distinct. Deceleration applies when the desired speed falls below the current speed or points against existing movement. This permits momentum without changing collision resolution.
Horizontal displacement uses a box sweep to the earliest time of impact. The motor advances to a small distance before contact, removes the component of remaining displacement directed into the surface, and repeats. Up to 3 sweep iterations permit sliding across successive walls. The same projection removes velocity directed into the contact normal.
A surface steeper than the maximum walkable slope uses a horizontal wall normal for this slide. Otherwise, an upward component in a steep collision normal could convert horizontal movement into unintended climbing. Vertical movement then resolves separately, unless floor following has already established support and canceled downward velocity.
The motor can emit pushes against loose physical parts. A push depends on closing speed along the contact normal and the character's mass relative to the part's mass. Anchored, noncolliding, and massless parts do not receive this push. The resulting physical response remains part of world simulation rather than a direct relocation of the obstacle.
Floor support, steps, and penetration recovery
Floor support uses 9 downward probes: the center, 4 axial points, and 4 corners slightly inset from the body box. The motor selects the highest accepted hit within the permitted rise and drop range. Its normal must satisfy the slope limit. Multiple probes keep a single missing triangle or edge sample from removing all support.
Stepping onto a ledge requires more than a clear destination. The motor checks the upward sweep, the horizontal sweep from the raised position, and the final body overlap. A low ceiling can therefore reject a step even when the landing surface is otherwise reachable. For mesh obstacles, the candidate rise comes from the actual surface near the landing point, rather than the top of the mesh's entire bounding box.
Floor following extends support over small descending changes while the character is grounded or retains floor grace. Jumping clears that grace so floor snapping does not immediately cancel the jump. Movement states also use grace to avoid alternating between running and falling over small terrain seams. Landing is emitted when support returns from an actual jumping or freefall state.
Penetration recovery makes up to 4 passes to move an embedded body out of nearby surfaces, with a bounded correction. Mesh floors need an additional check: the downhill edge of a box can intersect a slope even when inset floor probes find valid support. The motor allows a limited upward lift only when the penetration points sufficiently upward. A wall ahead does not qualify as a floor correction.
Platform motion, jumping, and gravity frames
A supported character records its platform's transform. On the next step, the motor maps the character's offset through the platform's previous and current yaw transforms. This carries position and heading with translation and rotation. The change in position also supplies the platform's point velocity. If no previous transform exists, point velocity comes from linear velocity plus angular velocity crossed with the support offset.
A ground jump inherits that platform point velocity before adding its upward
impulse. Height-based jumping computes the impulse as sqrt(2 * gravity * height);
power-based jumping uses the configured jump power. A fresh jump press can also
consume an available air jump. Jump buffering retains a press across a short
interval before landing, while floor grace retains eligibility briefly after
support ends.
For gravity with an up direction other than world Y, the motor rotates the step into a local upright frame around the starting feet position. It transforms nearby colliders, velocity, floor normal, support transform, world-relative input, and room-scale translation into that frame. The ordinary upright motor then runs, and its resulting pose, velocity, pushes, and support data return to world space.
This reuses one slope and sweep implementation for tilted movement. The world-Y water-surface lookup is restricted to the upright path, so swimming detection does not treat an arbitrary gravity frame as a rotated horizontal water volume. Room-scale walking is resolved after platform carry and before jumping and velocity movement; it does not add another gravity step.
Navigation snapshots and worker execution
A path request copies the collision references, material labels, modifier areas, custom links, and ignored character bodies into a navigation snapshot. The search runs on a worker without borrowing the live world. Later geometry mutations cannot change the snapshot while that search evaluates it.
The scripting layer can wait briefly for a result, sharing a 4 ms inline-wait budget across requests in the frame. Longer work yields and completes through the navigation job queue. Starting another request for the same path cancels the preceding request's delivery. Cancellation checks before worker search; it does not interrupt every expansion of an already running search.
A successful result therefore describes one captured world state. Path watching and replanning are separate operations after that result is installed.
Surface columns instead of a single height field
The search region is a horizontal grid of columns spanning the endpoints and a margin. A column can contain several floor nodes, allowing a bridge, road, and roof at the same horizontal coordinates to remain distinct. Columns are built on demand as search reaches them.
To build a column, the algorithm casts downward through nearby colliders and collects all surface hits with accepted slope normals. It merges heights less than 0.2 studs apart and rejects floors without standing room for the agent. The clearance test uses a swept horizontal box, so a wall thinner than one column can still block an edge between nodes.
The cell size is the larger of the region's horizontal span divided by 128 and the agent radius clamped to 0.5 through 4 studs. Enlarging a search region can therefore coarsen the sampled surface grid. The vertical search extent reaches 96 studs below the lower endpoint and 96 studs above the higher endpoint, plus standing clearance.
A request whose endpoints differ by more than 2,048 studs horizontally returns
NoPath. Finite requests try search-region expansion factors of 1, 2, and 4.
Each search stops after at most 400,000 node expansions. Those bounds limit work
but also mean a failed search is not a proof that no route exists in an
unbounded representation of the world.
Search edges, costs, and waypoints
The search uses A* with horizontal distance multiplied by the cheapest allowed cost as its heuristic. Nodes connect through movement-specific edges. Adjacent columns can connect by a walkable height change. Larger upward changes require jump eligibility; downward changes require an allowed drop. The search can examine a few additional columns to cross the clearance gap around a ledge.
Each candidate edge checks standing clearance along its horizontal span. Diagonal walking also requires compatible surfaces in both side columns, preventing a diagonal route through a blocked corner. Truss bounds can create climbing edges when the agent permits climbing. Custom links connect the floor nodes nearest their endpoints and preserve their labels and directionality.
Ordinary edge cost combines horizontal distance and half the absolute height change, multiplied by the destination area's cost. Jumping and falling add separate penalties. Area modifiers and material costs affect route preference; an infinite cost removes that candidate. A custom link can use its own label's cost.
Backtracking reconstructs the node chain. The waypoint pass retains corners,
movement-action changes, area boundaries, and custom links, while removing
redundant points along straight walking runs. Optional waypoint spacing adds
points only along walking segments. If the search makes progress but does not
reach the goal, it can return ClosestNoPath with the closest discovered route.
Missing starting or finishing floor nodes produce their own failure states.
Blocking checks and route freshness
A path is watched when it has a successful route and either automatic replanning or a blocked/unblocked listener. Checks run at most once every 0.2 s after the initial check, and an unchanged collider and navigation key avoids repeating the same scan. Progress limits the scan to the remaining route.
The blocking scan samples walking segments with the agent's clearance box. Jump, fall, climb, and custom-link transitions are not validated by that same walking obstruction test. A newly blocked segment can emit a blocked event and trigger replanning when enabled. A fresh route still requires the game to execute its movement actions and respond to changes after that snapshot.
For the public controls and movement callbacks, use CharacterController and PathfindingService.