Splines
A spline is a smooth curve through points you place: the route of a patrol boat, a rail a camera glides along, the line of a race track. Other entities can follow it, and scripts can ask where along it something is.
Drawing a spline
Section titled “Drawing a spline”- Add an entity and give it Add Component > Spline. It starts as a short straight line of two points.
- With the entity selected, turn on Spline in the scene page’s toolbar.
- Shape it in the viewport:
- Drag a point to move it.
- Select a point to show its two handles, and drag a handle to bend the curve there. Shift while dragging lets the two handles point different ways, for a sharp corner.
- Ctrl+click on the curve to add a point there.
- Point at a point and press Delete (or X) to remove it. A spline keeps at least two.
Each change is one undo step. Closed in the inspector joins the last point back to the first, for a loop.
The points belong to the entity: move, turn or scale the entity, and the whole curve goes with it.
Following a spline
Section titled “Following a spline”Give another entity a Path Follow component and set its Spline to the entity with the curve:
- Speed: metres a second along the curve.
- Distance: where along the curve it is, in metres. Set it to start part way along.
- Playing: whether it moves.
- Loop: at the end of an open curve, go back to the start. Without it, the follower stops at the end and Playing turns off. A closed curve always goes round.
- Align To Tangent: face along the curve as it goes.
It moves only while the game plays. The follower should not have a parent.
function Boat:onUpdate(dt) local follow = PathFollowComponent.of(self.entity) if Input.wasPressed("Fire") then follow.playing = not follow.playing endendvoid onUpdate(double dt){ PathFollowComponent@ follow = PathFollowComponent::of(self); if (Input::wasPressed("Fire")) { follow.playing = !follow.playing; }}Asking about a curve
Section titled “Asking about a curve”SceneSplines measures a curve and finds points on it. Each call takes the entity with the
spline:
function Rival:onUpdate(dt) local track = self.entity.scene:find("RacingLine") -- the entity with the spline local splines = SceneSplines.of(self.entity.scene) local here = splines:closestPoint(track, self.entity:worldPosition()) local ahead = splines:sampleAtDistance(track, here.distance + 10.0) Log.info("lap " .. here.distance / splines:length(track)) -- ahead.position is where to steer; ahead.tangent the way the track runs thereend[null, "The racing line"] Entity@ track;
void onUpdate(double dt){ SceneSplines@ splines = SceneSplines::of(self.scene); SplineHit@ here = splines.closestPoint(track, self.worldPosition()); SplineHit@ ahead = splines.sampleAtDistance(track, here.distance + 10.0f); Log::info("lap " + here.distance / splines.length(track)); // ahead.position is where to steer; ahead.tangent the way the track runs there}length(entity)is the curve’s length in metres.sampleAtDistance(entity, distance)is the point that far along.closestPoint(entity, position)is the point on the curve nearest a position, and how far along it is.sampleAt(entity, t)takes a position by segment instead: 0 is the first point, 1 the second, and so on.
Each gives back valid, position, tangent (the direction there), distance and t, in
world space. Scripts read curves; they cannot move a curve’s points.