Movement - theRAPTLab/gsgo GitHub Wiki

See !116 (among others)


[[TOC]]


Overview

In general, there are two ways to move a character:

  1. Set x/y directly
  2. Use a Movement Feature

1. Set x/y Directly

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

2. Use Movement Feature

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)


Movement Feature Merge Request

(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:

  1. 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.
  2. AGENTS_UPDATE is fired, during which we process any vision updates, e.g. which agent can see which other agent.
  3. FEATURES_UPDATE is fired, during which we pre-process information for FEATURES_THINK. Currently this includes calculating distances between agents.
  4. 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 is wander then we pick a random position/direction and queue the position call.
  5. FEATURES_EXEC is fired, during which we calculate any derived properties from the movement. e.g. isMoving, and orientation of the sprite.
  6. 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.

Properties

There are a number of movement related properties that are worth noting.

  • direction vs agent.prop.Movement.\_orientation vs agent.orientation

    • direction is movement related, orientation is purely visual.
    • direction is the angle (in degrees) the agent thinks it wants to move toward. During wander, 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 and agent.orientation are set at the same time during FEATURES_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 various seek, wander and moveEdgeToEdge 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
  • 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's and Don'ts

  • 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 and isMoving 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 property (read-only)

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 during FEATURES_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.

setMovementType method

Types:

  • static
  • wander
  • edgeToEdge
  • jitter
  • seekAgent

setMovementType wander <distance>

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

seekNearest method

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.

Syntax

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

seekNearestVisible method

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.

Syntax

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

Visibility

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
  ]]
]]

queuePosition method

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.

Syntax

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.)

jitterRotate method

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.

Syntax

featCall Movement jitterRotate

Example

when Predator touchesCenterOf Moth [[
  featCall Moth.Movement jitterRotate
]]

setRandomStartPosition method

Sets a random position for an agent within a rectangle centered around 0,0.

Syntax

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.

wanderUntilInside method

Agent will wander until their bounds are inside the bounds of the target agent type.

wanderUntilInside reliese on a new Physics feature method isBoundedBy

Syntax

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.

seekNearestVisibleColor

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.

Requires

  • Physics
  • Touches
  • Costume
  • Vision

Syntax

featCall Movement seekNearestVisibleColor <blueprint>

where <blueprint> is a blueprint name, e.g. Moth.

⚠️ **GitHub.com Fallback** ⚠️