Movement - theRAPTLab/gsgo GitHub Wiki
See !116 (among others)
[[TOC]]
In general, there are two ways to move a character:
- Set x/y directly
- Use a Movement Feature
If you set agent x/y directly, then you are responsible for updating and monitoring all agent movement, including:
- set new moved x/y position with each frame update
- edge / boundary detection
- edge wrapping
- calculating movement-related statistics like speed and whether the agent is moving
The Movement feature can take care of the basics of agent movement for you. But it means you need to route ALL movement requests through the Movement feature, otherwise the best case is your request is ignored, worse case it causes glitchy Movement.
(see a list of movement features below)
(Raw Notes)
Movement has been almost completely rewritten to properly make use of gameloop phases for FEATURES_UPDATE, FEATURES_THINK, and FEATURES_EXEC. Now instead of actions executing immediately upon request, they are queued and executed during a specific phase. This is more efficient (reduces extra looping) and better coordinates features.
Typically, movement is split into three phases:
- A movement request might come in at any time. It typically results in a
QueuePosition
call where the new position of the agent is requested. -
AGENTS_UPDATE
is fired, during which we process any vision updates, e.g. which agent can see which other agent. -
FEATURES_UPDATE
is fired, during which we pre-process information for FEATURES_THINK. Currently this includes calculating distances between agents. -
FEATURES_THINK
is fired, during which we find target agents if we are supposed to seek one out (this depends the vision update above) and the selected movement type is calculated. e.g. if the movement type iswander
then we pick a random position/direction and queue the position call. -
FEATURES_EXEC
is fired, during which we calculate any derived properties from the movement. e.g.isMoving
, and orientation of the sprite. -
VIS_UPDATE
is fired, during which we actually apply the movement updates to the agent. This is when position is actually set. We do this during VIS_UPDATE and not FEATURES_EXEC because during the PRE_RUN loop (before GO is pushed) we need to be actively processing and updating input positions or else charcontrol (and PTrack and Pozyx) agents would not display. FEATURES_EXEC is NOT run during PRE_RUN, but VIS_UPDATE is.
There are a number of movement related properties that are worth noting.
-
direction
vsagent.prop.Movement.\_orientation
vsagent.orientation
- direction is movement related, orientation is purely visual.
-
direction
is the angle (in degrees) the agent thinks it wants to move toward. Duringwander
, the agent will tend to continue moving in its current direction. You can set this directly via script, e.g.featCall Movement direction setTo 90
. Note that setting this tells the agent the direction you want the agent to move in. The agent will not face this direction until it actually moves. -
agent.prop.Movement.\_orientation
is the angle (in radians) the agent should face.\_orientation
is intended to be a private variable used only by the Movement and Vision features during internal calculations. It is derived by calculating the angle from the last position of the agent to its current intended position. Note that an agent can potentially be facing in one direction while moving in another (though this is not currently supported). -
agent.orientation
is the angle (in radians) the agent will face. Generally,agent.prop.Movement.\_orientation
andagent.orientation
are set at the same time duringFEATURES_EXEC
. The agent property is used to set the rotation of the sprite visuals. Since this is an agent property, you can set it directly, e.g. during an initScript, but during run time, it may be overwritten by Movement calculations.
-
distance
-
distance
is the number of world units the sprite will travel each frame. It is used by the variousseek
,wander
andmoveEdgeToEdge
movement types. The default is 0.5, but you can set this individually for any agent. - At a typical 30 fps:
-
0.25
is a snail's pace -
1
is leisurely -
4
is medium -
8
is quick -
20
is fast - much more than 20 will feel hitchy
-
-
-
useAutoOrientation
- Agents no longer rotate automatically in the direction they are traveling by default. If you want agents to auto-orient, you need to set the
useAutoOrientation
property to true. - e.g.
featProp Movement useAutoOrientation setTo true
- Agents no longer rotate automatically in the direction they are traveling by default. If you want agents to auto-orient, you need to set the
-
compassDirection
- This Movement feature property will return "E" or "W" depending on which direction the agent is facing.
- The feature is customizable, so theoretically we can also add "N" and "S" if they are useful. The ANGLES library just needs to be initialized appropriately.
- Typically this would be used in an expression, e.g.
ifExpr {{ agent.propMovement.compassDirection.value === "E" }} [[ featCall Costume setPose 2 ]]
- This is read-only.
- DO NOT set x/y directly. e.g. do not use
agent.x
to set position. This bypasses any movement processing such as bounds testing andisMoving
detection. The main exception to this is during the init script it's OK to set position directly since that is just executed once before the sim starts. There may be other similar circumstances where it's kosher to do this. - DO use
queuePosition
to set movement. See description below. - DO use movementType to set movement. In general, if you want an agent to move in a particular way, use a movement feature method. This results in better coordination of properties and derived property updates during the correct phase. The
setPosition
Movement feature method has been removed for this reason.
isMoving
is a Movement feature property that you can use in expressions, e.g.
ifExpr {{ Moth.getFeatProp('Movement', 'isMoving').value }} [[
// show flying pose
featCall Moth.Costume setPose 4
]]
- The property is calculated during
FEATURES_EXEC
based on the new position set duringFEATURES_THINK
. - To deal with jitter, an agent must move more than a minimum distance before it is considered a move. Currently this value is set to 5. This will probably need to be adjusted once we have real world PTrack and Pozyx input systems to test with.
- Currently we are not protecting the property so you can technically write to it, but the value will be overwritten with the next frame update.
Types:
static
wander
edgeToEdge
jitter
seekAgent
When set, the character will move in its current orientation at the speed indicated by distance. If the "doRandomOnWander" property is set to true (the default value), the character will randomly pick a new direction on occasion. If this is set explicitly to false, the character will move in a straight line. When it reaches the border of the screen it will either wrap or bounce depending on the project settings for wrapping and bouncing.
<distance>
is optional.
You can also set the distance value via a separate featProp call. e.g.
featCall Movement setMovementType wander
featProp Movement distance setTo 0.5
Tells the agent to move directly toward the nearest agent of the target type, regardless of whether or not the target can be "seen". If a target cannot be found, the agent will sit idly.
featCall Movement seekNearest <blueprint>
where <blueprint>
is the name of another blueprint, e.g. Moth
.
The speed at which the agent is pursued is set via the distance
property. The nearest agent is selected with each loop update, so if another agent comes nearer, the seek will be revised to the nearer agent.
Example:
# BLUEPRINT Predator
# PROGRAM DEFINE
useFeature Movement
# PROGRAM INIT
// set the blueprint type of the target
featCall Movement seekNearest Moth
// set speed at which predator pursues target
featProp Movement distance setTo 1
Tells the agent to move directly toward the nearest agent of the target type that is within the vision cone of the agent. If a target cannot be found, the agent will revert to wandering until another agent is visible.
featCall Movement seekNearestVisible <blueprint>
where <blueprint>
is the name of another blueprint, e.g. Moth
.
The speed at which the agent is pursued is set via the distance
property. The nearest agent is selected with each loop update, so if another agent comes nearer, the seek will be revised to the nearer agent. See the Vision Feature for more details about how the vision cone is implemented.
Example:
# BLUEPRINT Predator
# PROGRAM DEFINE
useFeature Movement
# PROGRAM INIT
// set the blueprint type of the target
featCall Movement seekNearestVisible Moth
// set speed at which predator pursues target
featProp Movement distance setTo 1
Note that the visibility of the target is derived programmatically, so a target with an alpha of 0, while not visible to students, is still visible to the agent.
If you need to hide a target from an agent, one way to implement this is with an extra property, e.g.:
# BLUEPRINT Moth
addProp isVisibleToPredator Boolean false
# BLUEPRINT Predator
when Predator sees Moth [[
ifExpr {{ Moth.getProp('isVisibleToPredator').value }} [[
// eat
]]
]]
If you want to take advantage of any Movement feature (such as isMoving
and automatic sprite orientation), you should not set the agent position directly (e.g. via prop x setTo nnn
). Instead use queuePosition
. This will queue the position request until the proper phase and calculate any derived properties.
featCall Movement queuePosition <x> <y>
where <x>
and <y>
are numeric position values.
(We may have to add separate x and y queuePosition methods if stack operations are needed to use calculcated values.)
This will trigger a single random rotation of the sprite. It's designed to be used within a conditional so you can trigger a series of random triggers.
featCall Movement jitterRotate
when Predator touchesCenterOf Moth [[
featCall Moth.Movement jitterRotate
]]
Sets a random position for an agent within a rectangle centered around 0,0.
featCall Movement setRandomStartPosition <width> <height>
where <width>
and <height>
define a rectangle centered on 0,0. For example featCall Movement setRandomStartPosition 100 100
will randomly place the agent somewhere within 50 units of 0,0.
Agent will wander until their bounds are inside the bounds of the target agent type.
wanderUntilInside
reliese on a new Physics feature method isBoundedBy
featCall Movement wanderUntilInside
where <blueprint>
is the name of an agent blueprint, e.g. featCall Movement wanderUntilInside TreeFoliage
The "isInside" test runs during Movement's FEATURES_THINK phase. This test relies on the touch properties set by Touches.
Like seekNearestVisible
, this will tell an agent to seek the nearest non-camouflaged agent of the specified blueprint type.
Set the color\*DetectionThreshold
Vision values of the viewing agent before using this. These thresholds determine the level of color difference necessary before a color can be discerned against a background.
- Physics
- Touches
- Costume
- Vision
featCall Movement seekNearestVisibleColor <blueprint>
where <blueprint>
is a blueprint name, e.g. Moth
.