Changing Directions
Sometimes you want to switch it up and move the other way (on a coaster track that is). In this tutorial, you will learn how to make a coaster train switch directions from forwards to backwards and vice versa. We will be specifically looking at two examples: from a stop (i.e. via a track switch) and from a rollback. Additionally, we will be looking at how to change the direction the train is facing.
Goal
- Learn how to have the train change directions mid-ride from either a stop or by rolling back.
- Learn how to manage
PhysicsState.moveDirection
in order to change directions. - Learn how to set
modelDirection
to have a train turn around on the same track
Requirements
- Know how to record train physics via the
physics.v1
module and how to setupTrackSectionsData
. - Know how to record animation segments sequentially.
How to Change Directions
The two properties we will be looking at when changing directions are moveDirection
and stopWhenDirectionChanges
.
PhysicsState.moveDirection
is a boolean value which denotes the direction the train is moving. true
means currentSpeed > 0
and false
means currentSpeed < 0
. Being separate from currentSpeed
means it will stay the same when speed is 0
.
The stopWhenDirectionChanges
flag on physics v1's SimulationState stops the simulation when train changes move direction, as in, when the train rolls back. Specifically, keeps track of the PhysicsState.moveDirection
at the start of the simulation and looks to see if has changed between update frames. On the physics v1's SimulationStateBuilder, you set this by calling the :withStopWhenDirectionChanges()
method.
When you stop a train, the moveDirection
will (usually) be the same as it was before it stopped. However, when you try to move it backwards, it will stop after the first update as stopWhenDirectionChanges
will see that the moveDirection
it started at, the moveDirection
when the train stopped, will change.
How do you solve this?
The easy way would be to just comment out or remove :withStopWhenDirectionChanges()
to prevent it from being called on the SimulationStateBuilder
. However, enabling stopWhenDirectionChanges
still useful as it will stop it early if the train begins to rollback or valley, preventing the simulation from timing out (and saving precious CPU time).
What you should do instead is preset the currentSpeed
and moveDirection
in the physicsState
. Here's how you would do it if you were moving from forwards (moveDirection = true
) to backwards (moveDirection = false
):
physicsState.currentSpeed = 0
physicsState.moveDirection = false
It's that simple!
You may notice that we also manually set currentSpeed
to 0
. Since it updates based on currentSpeed
, this is just to make sure the physics will be able to keep it the direction we want during the first update (in this case, have currentSpeed < 0
).
Rollbacks
A rollback, in coaster terms, is when a coaster train changes directions mid-course while it is in motion (so not from a stop) and on an inclined part of the track. Rollbacks can be found on shuttle coasters or swing launches.
To setup our rollback, we first need a few things: the track the train will use and two TrackSectionsData
, one for forwards direction and one for backwards direction. The track should ideally have a spike where we will be rolling back. Additionally, make sure both of your TrackSectionsData
will allow the train to rollback in the area you want it to (the track spike). You should have no acceleration that will make it continue to move in the current direction (though you can push it in the opposite direction).
We will record our forwards segment as usual with our forwards TrackSectionsData
but with a few changes. First, we will not be adding a stopPosition
. Next, like the previous section of this tutorial, we will be enabling stopWhenDirectionChanges
. We want stopWhenDirectionChanges
to catch our train rolling back and stop the simulation on purpose.
Here's what it should look like (using parts from the default template):
-- record forwards
mainAnimationRecorder:recordSegment(keyframeRecorder, triggerRecorder)
local START_POSITION = mainKeyframeRecorder:getLastPosition()
local STOP_POSITION = 0
local STOP_SLOW_OFFSET = 10
local TRACK = mainTrack
local TRACK_SECTIONS_DATA = forwardsTrackSectionsData
local MODEL_DIRECTION = true
local PHYSICS_STATE = mainPhysicsState
PHYSICS_STATE.currentSpeed = 0
PHYSICS_STATE.moveDirection = true
local physicsSimulationState = physicsSimulatorV1.SimulationStateBuilder.new()
:withCFrameTrack(TRACK)
:withTrackSectionsData(TRACK_SECTIONS_DATA)
:withCurrentPosition(keyframeRecorder:getLastPosition())
:withModelDirection(MODEL_DIRECTION)
:withClampPositionToTrackLength(true)
:withGravity(GRAVITY)
:withFriction(FRICTION)
:withTimeInterval(TIME_INTERVAL)
-- :withStopPosition(STOP_POSITION - STOP_SLOW_OFFSET, "Stop", false)
:withStopWhenDirectionChanges() -- stop immediately when rolling back
:withUpdateType(PhysicsUpdateType.WithPositionOffsets(mainWheelSetOffsets))
:build() -- builds the simulation state
:unwrap()
-- [REST OF THE CODE]
end)
Next, we will be recording our backwards section in a different segment.
First, we will setup our physicsState
similarly to how we did it at the end of the How to Change Directions tutorial above. Because the train rolled back, direction should already be switched (set to false
in this case).
Next, we need to set our START_POSITION
. We want the change in sections to look seamless so we want to get the last position we recorded from our forwards segment. We can use the KeyframeRecorder:getLastPosition()
method to do so. However, if you remember from the Record Animation Segments Tutorial, the first two arguments passed in the record function are an KeyframeRecorder
and TriggerRecorder
. Thus we must add the extra argument to get the mainKeyframeRecorder
.
-- record backwards
mainAnimationRecorder:recordSegment(keyframeRecorder, triggerRecorder, mainKeyframeRecorder, _mainTriggerRecorder)
local START_POSITION = mainKeyframeRecorder:getLastPosition()
local STOP_POSITION = 0
local STOP_SLOW_OFFSET = 10
local TRACK = mainTrack
local TRACK_SECTIONS_DATA = mainTrackSectionsData
local MODEL_DIRECTION = true
local PHYSICS_STATE = mainPhysicsState
PHYSICS_STATE.currentSpeed = 0
PHYSICS_STATE.moveDirection = false
-- [REST OF THE CODE]
end)
After, we set the to our TrackSectionsData
to the backwards one. Finally, we can set it to record to a stop position or do another rollback.
Once you are done you can run the program and our rollback should be working.
Model Direction
modelDirection
is a keyframe property which allows you to flip a train's model on the track around the train's MidPoint
. It was originally added so that you didn't need to rotate the points around on your track in order for your train to move in both directions, say, if you had a turntable like Sandy's Bucking Bronco. Thus, this means you can reuse the same track for forwards and backwards train.
Here's an example of what the train looks like with modelDirection
true
and false
:
image here
image here
As a keyframe property, you can manually modelDirection
like so:
keyframe.modelDirection = true
Many of the SimulationStates used by the simulator modules have a property to set the modelDirection
when simulating and a builder method :withModelDirection(direction: boolean)
.