Properties and Physics - theRAPTLab/gsgo GitHub Wiki

TLDR;

If you want to use touches, you need to use the Physics feature:

  1. Define Costume and set the default sprite BEFORE using Physics
  2. Use Physics on both the source and target blueprints you want to monitor.
  3. Use Touches on the blueprint that will be running the touch test, and register the agents you want to monitor. (You don't need to use Touches on the target blueprint).
  4. Physics will automatically define the physics body bounds based on the Costume size.
  5. If you want to change the size of the character, set size / scale via Physics so the system can maintain both the visual size and the physics body size.
  6. Touches are processed based on the Physics body size. So you can potentially have a physics body and sprite costume size that are independent of each other.
  7. In general, don't use the following agent props directly:
    • scale
    • scaleY

[[TOC]]


Example

# PROGRAM DEFINE
useFeature Costume
featCall Costume setCostume 'fish.json' 0

useFeature Physics
useFeature Touches
featCall Touches monitorTouchesWith Algae

# PROGRAM UPDATE
when Fish touches Algae [[
  every 1 runAtStart [[
    prop Fish.energyLevel add 10
    prop Algae.energyLevel sub 10
    featCall Fish.Costume setGlow 0.5
  ]]
]]

See !93 and !89 !109.

isBoundedBy method

Tests whether the current agent context's bounds is inside the targetagent bounds.

Syntax

featCall Physics isBoundedBy <targetagent>

Generally you would not call this method directly. It is used by the Touches feature to set touch conditions.

You can, however, use it in an ifExpr, keeping in mind you would need to be able to pass the target agent's context. This usually means you need to use this inside a when conditional that is doing a pairwise comparison and passes the target agent context.



[EVERYTHING BELOW THIS NEEDS TO BE REWRITTEN]

Raw notes from !89

This is a complete revamp of the Physics GFeature to calculate and set size and scale of both the physics body and agent visual during the PHYSICS phase of the game loop.

This change was needed in order to allow calculated expressions to be used to set scale.

The research team wanted to dynamically set agent sizes based on a calculated value, e.g.

featCall Physics setSize {{ (agent.getProp('energyLevel').value / 100)* 2}}

But of course expressions cannot be used like this. In order to use expressions, we needed to stack operations. And in order to use stack operations with GFeatures, we needed to be able to change scale from being a method to a featProp.

Key Points

  • scale is now a featProp instead of a featMethod, so it can be easily updated via a featPropPop call.

  • Dimensions are now split into three types of featProps:

    1. User-defined width and height -- These are set via GEMSCRIPT. By default they are set to the costume width and height.
    2. Costume costumeWidth and costumeHeight -- These are the dimensions of the first frame of the currently selected Costume. They are the base size from which every other calculation is made. Students should not need to touch this.
    3. Physics body bodyWidth and bodyHeight -- These are calculated dimensions based on an application of scale to the user-define width and height. These dimensions are used for calculating touches. Students should not need to touch this.
  • scale and width/heightwork harmoniously together. Scale is applied to the user-defined width and height. In general, set the base size you want to work with via width and height, then use scale to dynamically resize the agent during runtime.

  • The calculation of physics bounds and the application of sizing and scaling to the agent visual happen during the PHYSICS phase of the gameloop (and pre-run loop).

  • setSize is a convenience function that allows users to set width and height with one method call.

  • getBounds now explicitly uses physics body bounds

How to Use It

  1. Define the costume first:
# PROGRAM DEFINE
useFeature Costume
featCall Costume setCostume 'algae.json' 0
  1. Add Physics feature:
useFeature Physics

3. Init the Physics feature to read the costume size.
This is necessary any time the costume size changes. All sizing and scaling operations rely on the base costume size.

~~featCall Physics init~~

[As of 10/14/2021 this is no longer necessary. init is automatically called on decorate and setCostume.]

  1. Set the size (before setting scale). Scale will be applied to the base size. You can change the shape of the sprite by using specific sizes, e.g. in this example we set the Algae to be taller than it is wide.
featCall Physics setSize 16 32
  1. Set the scale via stack operations. e.g. this will update the size based on energyLevel every second:
every 1 runAtStart [[
  prop energyLevel sub 1

  // update size
  exprPush {{ agent.getProp('energyLevel').value / 100 * 2}}
  featPropPop agent.Physics scale
]]

Testing

  1. git fetch && git checkout dev-bl/physics-scale
  2. npm run bootstrap && npm run gem
  3. Load Main: http://localhost/app/missioncontrol?model=aquatic
  4. Load CharControl: http://localhost/app/charcontrol
  5. Click "GO".

The following should work:

  • Algae should be tall and narrow.
  • Lightbeam should be tall and narrow.
  • When you press GO, Algae should not suddenly change size.
  • Lightbeam goes across the screen, as it hits Algae, Algae should light up and increase its energyLevel. As its energyLevel increases, it should increase in size.
  • As fish touch Algae, algae should decrease in energyLevel and decrease in size.

Under the Hood: How it Works

When an agent is instantiated, as it adds the featProps during decorate, it also registers with the feature as an agent that needs to be updated during the PHYSICS phase of the game loop. Then during each phase (which occurs early in the game loop), the current settings for size and scale are calculated and applied to the physics body and the agent visual.

In addition:

  • featProp was fixed to pass the proper context. It should support featProp Physics ..., featProp agent.Physics ... and featProp Algae Physics ... calls now. Previously it had been broken.
  • featPropPop was also fixed to pass the proper context. Previously it had been broken.

Addresses #145.


Raw notes from !64

Physics Feature

In order to have a proper "touches" test, we needed to implement a rudimentary Physics feature. This establishes a physics body boundary to define the agent size so we can properly test for collisions. (The old hack was using a simple distance test).

Physics is somewhat complicated by the fact that students might want to change the size of agents. So we have to coordinate the size of the physics body with the size of the visual sprite. In order to make the experience as simple as possible for the student, we coordinate all of that through the Physics feature. We also add a few simple methods to make it easier for students to interact with physics.

  1. To use Physics:
useFeature Physics
  1. To set up the initial physics body boundaries to match the sprite size:
useFeature Costume
featCall Costume setCostume 'bunny.json' 0
featCall Physics init

The init call will automatically set the shape of the Physics body to a rectangle and size it to match the sprite. NOTE: You need to set the Costume before initializing Physics, otherwise the Physics feature doesn't know how to size the body.

  1. To change the size of the sprite and physics body:
featCall Physics setScale 2

where 2 would be twice as large. Scale is set relative to the current size rather than the original size. So if you call setScale twice, the agent will transform twice.

  1. To set the exact size of the sprite and physics body:
featCall Physics setSize 100 200

where the first parameter is the width and the second the height. You can also omit the second parameter and the system will treat it as a square. e.g. featCall Physics setSize 200 works the same as featCall Physics setSize 200 200

  1. If you have a circular sprite, you can set Physics to use a circular boundary:
featCall Physics setShape 'circle'
featCall Physics setRadius 5

NOTE: This is the radius, not the diameter. We do not yet support polygons.

  1. The "touches" test now performs a check on the intersection of the two agents' Physics body boundaries. So it should behave like a real touch. However, the physics bodies are either a rectangle or a circle, so sprites with some alpha transparency (like the Fish) might register a touch in the corners even though the sprite image does not show it.

Setting Sizes

You can now use GEM-SCRIPT to set sprite sizes.

Recommended Method: Physics Feature setScale

If you are using touches with any agents, you need to initialize the Physics feature with any agent that can be touched. See the Physics writeup above for details.

Advanced Agent Scaling

In order to implement sizing, we had to add scaling to the GAgent class. This opens up a number of GEMSCRIPT commands that allow you to manipulate scale directly, but YOU SHOULD NOT DO THIS if you are using Physics and "touches". The Physics feature needs to manage scaling and sizing for both the visual and the physics body bounds. So use featCall Physics setSize x y instead, this is only for reference.

There are multiple ways of setting scale.

Feature Costume setScale Call

You can call the setScale Costume method:

featCall Costume setScale 0.5

Default scale is 1. scale is a relative scale parameter. So scale=2 will result in a sprite that is twice as large as the default sprite. Scale can be as small as 0.1 and as large as 10. We can adjust the default min/max in class-gagents.ts:73-74.
You can also temporarily override the limits with GEMSCRIPT:

prop scale setMax 50
featCall Costume setScale 50

Agent Prop scale Call

Technically you can set the scale directly with prop scale setTo 10 without going through the Costume Feature, but we want to discourage that in case we need to do more processing with the Costume Feature in the future.

prop scale setTo 2

You can also set the height and width separately:

prop scale setTo 2
prop scaleY setTo 4

This would result in a sprite that is four times as tall and twice as wide.

To set scale for specific instances, set the scale prop in the init script. (This somewhat contradicts the statement above since we are considering not allowing featCalls in the init script. Skin suffers from the same conundrum.)

exprPush

The prop ... setTo ... command can only be used to assign numbers to props, not expressions. So a call like this...

prop scale setTo {{ agent.getProp('energyLevel').value / 1000 }}

...will not work. props ... setTo ... accepts GVars, which are simple object classes that cannot process an expression.

We introduce a new stack command as a workaround for this by taking advantage of expression assignment via stack operations. In order to do the equivalent of: prop clone setTo {{ agent.getProp('energyLevel').value / 1000 }} you can do this:

exprPush {{ agent.getProp('energyLevel').value / 1000 }}
propPop clone

exprPush pushes the value of the javascript expression to the stack, and propPop clone pops the value off the stack and assigns clone to the value.

This can be pretty powerful as we can use javascript in the expression, even though it's a bit ugly. e.g. to get around the ifExpr bug that prevents us from accessing the agent context inside an ifExpr nested inside an when conditional, we can do this:

when Fish touches Algae [[
  // hack around ifExpr bug
  exprPush {{ Fish.getProp('energyLevel').value + (Algae.getProp('energyLevel').value > 0 ? 1 : 0) }}
  propPop Fish.energyLevel
]]

Of course we don't expect students to do this.

Glow Costume

The original request to show Fish eating was to use sparkles, which requires a particle system. We currently do not have a particle system in place, so in order to quickly put in a place holder, we are taking advantage of PIXI's Glow filter.

You can tell an agent to Glow using a Costume method:

featCall Costume setGlow 1

The parameter tells the system how many seconds to show the glow before it reverts. We made the glow green to make a connection to eating Algae, but we can change it to another color if you prefer. In the current script, the Fish will glow as it eats an Algae.

Screen_Shot_2021-04-08_at_10.47.35_PM

If necessary, and if we still have time, we can try to implement a particles system.

alpha property

Set alpha by calling prop agent.alpha setTo 0.5. Valid values are between 0 and 1, where 1 = 100% opaque.

Dead fish now are set to an alpha of 0.3 to ghost them.

isInert property

Agents now have an isInert property. When set to true, inert agents are not "touchable".

Dead fish are set to inert so they no longer eat algae. Dead algae are set to inert so they no longer gain energy from lightbeams.

You still have to test for isInert in other script areas. e.g. if you want if you want inert fish to not do an onEvent Tick update, you have to test for it. Currently the only built-in handler for isInert is in touches.

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