Properties and Physics - theRAPTLab/gsgo GitHub Wiki
If you want to use touches
, you need to use the Physics feature:
- Define
Costume
and set the default sprite BEFORE using Physics - Use Physics on both the source and target blueprints you want to monitor.
- 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).
- Physics will automatically define the physics body bounds based on the Costume size.
- 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.
- 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.
- In general, don't use the following agent props directly:
scale
scaleY
[[TOC]]
# 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.
Tests whether the current agent context's bounds is inside the targetagent
bounds.
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]
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.
-
scale
is now a featProp instead of a featMethod, so it can be easily updated via afeatPropPop
call. -
Dimensions are now split into three types of featProps:
- User-defined
width
andheight
-- These are set via GEMSCRIPT. By default they are set to the costume width and height. - Costume
costumeWidth
andcostumeHeight
-- 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. - Physics body
bodyWidth
andbodyHeight
-- These are calculated dimensions based on an application ofscale
to the user-define width and height. These dimensions are used for calculating touches. Students should not need to touch this.
- User-defined
-
scale
andwidth
/height
work 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
- Define the costume first:
# PROGRAM DEFINE
useFeature Costume
featCall Costume setCostume 'algae.json' 0
- 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
.]
- 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
- 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
]]
git fetch && git checkout dev-bl/physics-scale
npm run bootstrap && npm run gem
- Load Main:
http://localhost/app/missioncontrol?model=aquatic
- Load CharControl:
http://localhost/app/charcontrol
- 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.
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 supportfeatProp Physics ...
,featProp agent.Physics ...
andfeatProp 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
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.
- To use Physics:
useFeature Physics
- 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.
- 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.
- 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
- 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.
- 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 acircle
, 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.
You can now use GEM-SCRIPT to set sprite sizes.
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.
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.
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
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.)
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.
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.
If necessary, and if we still have time, we can try to implement a particles system.
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.
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
.