In Rewtro code statements are used to manipulate objects and their keys to change their behavior. Since any line of code is a getter the subject of this processing is given by current line getter with very few exceptions.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","backgroundColor":5,"x":76,"y":16},
{"id":"B","backgroundColor":4}
],
"tilemaps":[{"map":["A"]}],
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":25}]}],
"then":[
{"id":"A","set":[{"backgroundColor":[{"list":[2,8],"randomNumber":true}]}]},
{
"id":"A",
"spawn":[{"ids":[{"character":"B"}],"at":[{"as":"this"}]}],
"set":[{"speedY":[{"smallNumber":3}]}]
}
]
}
]
}]
}
This cartridge shows a square that changes color every second and drops a cyan square.
Every 25 frames of the scene
object (that's every second for a game running at the default speed of 25 frames per second) the A
sprite is picked by the getter and its backgroundColor
is set to a randomNumber
from 2
to 8
.
Then the next line picks the A
sprite again, becoming the local this
. A brand new B
sprite is spawn
ed at the same position of the this
object - which is the said A
sprite - but spawn
is a special statement that changes the current line picked object to the just spawn sprite. That's why the set
statement will work on B
, changing its speedY
and making it fall, instead of the A
sprite.
The most powerful and yet the simplest statement in Rewtro is set
: it accepts a list of keys and getters and it sets the current picked object keys to the getter values. Used together with getters and sub-getters you can change sprites direction, text, colors, and more.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","x":0,"y":68,"width":160,"textAlignment":"center"},
{"id":"B","backgroundColor":3,"x":76,"y":68}
],
"tilemaps":[{"map":["AB"]}],
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":25}]}],
"then":[
{
"id":"A","set":[{
"text":[{"string":"BLINKING TEXT"}],
"backgroundColor":[{"smallNumber":6}]
}]
},
{
"randomize":true,
"id":"B","set":[{
"speedX":[{"list":[-2,2],"randomNumber":true}],
"speedY":[{"list":[-2,2],"randomNumber":true}]
}]
}
]
},
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":50}]}],
"then":[{
"id":"A","set":[{
"text":[{"string":"IS SO RETRO!"}],
"backgroundColor":[{"smallNumber":5}]
}]
}]
}
]
}]
}
This cartridge show a blinking text in the middle of the screen and a green square randomly roaming around.
Every second the A
sprite is picked and its backgroundColor
and text
are set
. The B
sprite is picked and it's given a random speedX
and speedY
, making it moving around.
Every 2 seconds the A
sprite is picked again but its backgroundColor
and text
are set
to other values, overwriting the previous changes and making it look like blinking.
There are other code statements to change variables. Let's quickly see all of them.
Like the set
statement these math and logic statements accept a list of keys and getters but values are processed instead of being just set.
sum
sums the specified key values for the picked object.subtract
subtracts the specified key values for the picked object.multiply
multiplies the specified key values for the picked object.divide
divides the specified key values for the picked object.module
applies the module operator the specified key values for the picked object. It's roughly the rest of the division (i.e. 5 module 2 equals 1)invert
just invert the specified key values for the picked object. The statement key value is just ignored.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","backgroundColor":3,"x":0,"y":68,"width":160,"textAlignment":"center","text":"I'M BLINKING TOO!"}
],
"tilemaps":[{"map":["A"]}],
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":15}]}],
"then":[
{
"id":"A",
"invert":[{"visible":[{"smallNumber":1}]}],
"sum":[{
"backgroundColor":[{"smallNumber":1}],
"textColor":[{"smallNumber":1}]
}],
"module":[{
"backgroundColor":[{"smallNumber":16}],
"textColor":[{"smallNumber":15}]
}]
}
]
}
]
}]
}
This cartridge show a blinking text in the middle of the screen that keep changing colors.
Every 15 frames the A
sprite is picked, its visible
key is inverted, making it blinking, and its backgroundColor
and textColor
are increased and then looped inside a range by the module
statement.
The push
statement appends the key value to a picked list.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A"}
],
"tilemaps":[{"map":["A"]}],
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"==","smallNumber":0}]}],
"then":[
{"id":"A","set":[{"value0":[{"emptyList":true}]}]}
]
},
{
"when":[{"as":"keyboard","attribute":"up","if":[{"is":"hit"}]}],
"then":[{"id":"A","push":[{"value0":[{"string":"UP"}]}]}]
},
{
"when":[{"as":"keyboard","attribute":"down","if":[{"is":"hit"}]}],
"then":[{"id":"A","push":[{"value0":[{"string":"DOWN"}]}]}]
},
{
"then":[{"id":"A","set":[{"text":[{"attribute":"value0"}]}]}]
}
]
}]
}
This cartridge show an empty screen. Hitting the up and down buttons of your controller will display the sequence.
When the game starts the A
sprite value0
key is set to an emptyList
. Then, hitting up
or down
the string
UP
or DOWN
is added to that list by push
. Finally, the value0
of the A
sprite is copied to its text
key, making it appear on the screen.
Statements can spawn sprites, remove, and move them around.
remove
removes the currently picked sprites from the scene. Just set the key totrue
when you need it.spawn
spawns one or more sprites on the scene. It accepts an object with two values:ids
which are the sprite IDs you want to spawn. It may be acharacter
for a single sprite or astring
for multiple sprites.at
that decides the position. It may be:- A getter to a
list
with two numbers. That's the horizontal and vertical position of the sprites. - A getter to another sprite. The spawned sprites will be centered on it.
- A getter to a
placeAt
moves the currently picked sprite to another position. It accepts the same values ofspawn
'sat
.
The spawn
statement is one of those special statements that change the current code
line picked object: once the sprites are spawn, they became the current picked object so they can be manipulated (set
, sum
, and so on) on the same line.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","x":76,"y":112,"backgroundColor":4},
{"id":"B","text":"I","x":76,"y":112,"speedY":-4},
{"id":"C","x":0,"y":0,"backgroundColor":3,"speedX":2},
{"id":"D","text":"*","speedY":1}
],
"tilemaps":[{"map":["ABC"]}],
"code":[
{
"when":[{"as":"keyboard","attribute":"buttonA","if":[{"is":"hit"}]}],
"then":[{"id":"B","placeAt":[{"id":"A"}]}]
},
{
"when":[{"id":"C","if":[{"itsAttribute":"x","is":">","number":160}]}],
"then":[{"id":"C","placeAt":[{"list":[0,0]}]}]
},
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":25}]}],
"then":[
{"spawn":[{"ids":[{"character":"D"}],"at":[{"id":"C"}]}]},
{
"spawn":[{"ids":[{"character":"D"}],"at":[{"list":[0,0]}]}],
"set":[{"text":[{"character":"#"}],"textColor":[{"smallNumber":5}]}]
},
{
"spawn":[{"ids":[{"character":"D"}],"at":[{"list":[152,0]}]}],
"set":[{"text":[{"character":"#"}],"textColor":[{"smallNumber":7}]}]
}
]
},
{
"when":[{"id":"D"}],
"then":[{
"code":[{
"when":[{"if":[{"itsAttribute":"y","is":">","number":144}]}],
"then":[{"remove":true}]
}]
}]
}
]
}]
}
This cartridge show a moving green square shooting an asterisk every second, a cyan square that fires an I
letter back when pressing the A button and some yellow and red scrolling #
on the sides of the screen.
Notice that even if there are a lot of sprite spawning the scene sprites counter on the debugger status never surpass the ~21 sprites threshold.
The first code
line moves the B
sprite back to the A
position when the A button is hit and that's why the I
projectile restarts from the cyan bottom every time. The sprite is not removed or spawned: is just moved back and that's why it can fire just a bullet.
The second line makes sure that the C
sprite, that's the green square, is moved to the top left corner of the screen when it goes outside the right side of the screen.
The third line does three things every second:
spawn
a new falling asteriskD
at the green squareC
positionspawn
a red#
on the top left corner of the screen. Notice that thespawn
statement spawns an asteriskC
but theset
statement changes itstext
andtextColor
.spawn
a yellow#
on the top right corner of the screen in the same way
The last line iterates all the D
objects and, when
they go outside the bottom side of the screen, it will remove
them. And that's why the sprites count of the scene is kept stable.
Rewtro supports several statements to manipulate areas of sprites. They work together with the mandatory inArea
getter and let you copy, flip, and spawn a large number of sprites at once - often tilemaps.
There are two methods to spawn maps of sprites that both of them accepts a map identical to data blocks tilemaps
as key value:
fillAreaWithPattern
fills theinArea
rectangle with the specified tilemap repeating it horizontally and vertically.outlineAreaWithPattern
just spawn a border inside theinArea
rectangle using the specified tilemap.
Then you can copy an area of already spawned sprites using areaCopy
. The sprites to be copied must be already picked by the current code
line getter so make sure to specify the id
, ids
, or tags
of the sprites you want to copy along with the inArea
key. The areaCopy
key accepts multiple instances of objects structured this way:
x
andy
are two getters to the copy destinationfromIds
andtoIds
are two strings that define which spriteid
(toIds
) must be spawned for every spriteid
to copy (toIds
). For example"fromIds":"AB","toIds":"DE"
instructsareaCopy
to copy every sprite withid
A
using a sprite withid
D
and every sprite withid
B
using a sprite withid
E
. IftoIds
is omitted no conversion map is applied and iffromIds
is omitted too all of the picked objects will be copied.
fillAreaWithPattern
, outlineAreaWithPattern
, and areaCopy
are special statements that change the current picked objects to the already spawned sprites so you can manipulate them in the same code line.
Finally, you can manipulate the picked sprites in the given area instead of spawning new ones this way:
areaFlipX
andareaFlipY
flip the specifiedinArea
sprites horizontally or vertically. Just set these keys totrue
.areaRotate
accepts a getter to the number of times the spritesinArea
must be rotated clockwise. If you want to rotate the area sprites the opposite way just add theareaCounterclockwise
key set totrue
.
Only sprite coordinates are rotated or flipped. The sprites rotation and flip (sprite attributes)[spriteattributes.md] will remain untouched.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","backgroundColor":3},
{"id":"B","backgroundColor":5},
{"id":"C","backgroundColor":11}
],
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"==","smallNumber":0}]}],
"then":[
{
"inArea":[{
"x":[{"smallNumber":8}],"y":[{"smallNumber":8}],
"width":[{"smallNumber":56}],"height":[{"smallNumber":56}]
}],
"outlineAreaWithPattern":[
"AA",
"BA"
]
},
{
"inArea":[{
"x":[{"smallNumber":16}],"y":[{"smallNumber":16}],
"width":[{"smallNumber":40}],"height":[{"smallNumber":40}]
}],
"fillAreaWithPattern":[
"CC",
"C ",
"CC",
" C"
],
"set":[{"text":[{"character":"*"}]}]
},
{
"ids":"AC",
"inArea":[{
"x":[{"smallNumber":8}],"y":[{"smallNumber":8}],
"width":[{"smallNumber":56}],"height":[{"smallNumber":56}]
}],
"areaCopy":[
{
"x":[{"smallNumber":8}],"y":[{"smallNumber":72}],
"fromIds":"AC","toIds":"BA"
},
{
"x":[{"smallNumber":72}],"y":[{"smallNumber":8}],
"fromIds":"AC","toIds":"BA"
},
{
"x":[{"smallNumber":72}],"y":[{"smallNumber":72}],
"fromIds":"AC","toIds":"BA"
}
],
"set":[{"text":[{"character":"R"}]}]
},
{
"id":"B",
"inArea":[{
"x":[{"smallNumber":72}],"y":[{"smallNumber":8}],
"width":[{"smallNumber":56}],"height":[{"smallNumber":56}]
}],
"areaRotate":[{"smallNumber":1}]
},
{
"id":"A",
"inArea":[{
"x":[{"smallNumber":72}],"y":[{"smallNumber":72}],
"width":[{"smallNumber":56}],"height":[{"smallNumber":56}]
}],
"areaFlipY":true
}
]
}
]
}]
}
This cartridge spawn a large tilemap but no tilemaps
data block is there.
Another scarily large chunk of code, huh? Don't worry: there are just 5 statements here that are executed at the game start. The first two lines build the top left square:
- The first line uses
outlineAreaWithPattern
creates the border. The specified pattern will create straight green lines to the top and the bottom and dotted red/green lines to the sides. - The second line fills the center with
fillAreaWithPattern
and using a pattern of cyan sprites. Since the current line picked objects are now the spawned spritesset
ting thetext
property to*
will make an asterisk appear in the center of the square.
Then the area is copied 3 times by areaCopy
. But there are three details here:
- The current line getter just picks
A
andC
sprites, which are the green and cyan ones. The redB
sprites are ignored and not copied. fromIds
andtoIds
are swapping theA
sprites withC
sprites and that's why the copies are green inside and cyan outside.- Finally,
areaCopy
sets the picked objects to the spawned sprites so theset
statement will make anR
appear in all of the sprites.
The areaRotate
line rotates the top right square border by 90 degrees clockwise. Why just the border? Because the line getter is just getting the C
sprites that are the cyan ones.
Finally the bottom right A
sprites are flipped vertically. The R
letter inside the sprites of flipped and rotated areas is still readable: these area statements just manipulate sprite positions and not their appearance.
In most of the examples, we've moved sprites around setting their speedX
, speedY
, gravityY
, etc. keys and let the basic Rewtro physics engine do all the work. We talked about the physics engine a little more in the sprites attributes chapter.
While it's strongly suggested to use it in your games some genres may need a less realistic movement logic like in puzzle games. For example, using the sum
or subtract
statement to manually change the x
or y
sprite keys of a cursor and then checking the isCollidingWith
condition to get the selected item may be a better way to implement a menu than using a physics engine.
There are a few helper statements for non-physics manipulations: pan
smoothly changes any key value to a destination value and moveTo
applies multiple movement vectors to a sprite without invoking the physics engine.
pan
accepts an object with two keys:to
which is a list of keys and getters to the destination values.speed
is a getter to the value panning speed
moveTo
accepts a list of objects with theangle
andby
getters
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","text":"A","x":76,"y":68,"zIndex":1},
{"id":"B","backgroundColor":5}
],
"tilemaps":[{"map":["AB"]}],
"code":[
{
"when":[{"as":"keyboard","attribute":"left","if":[{"is":"down"}]}],
"then":[{"id":"A","subtract":[{"rotate":[{"smallNumber":4}]}]}]
},
{
"when":[{"as":"keyboard","attribute":"right","if":[{"is":"down"}]}],
"then":[{"id":"A","sum":[{"rotate":[{"smallNumber":4}]}]}]
},
{
"when":[{"as":"keyboard","attribute":"buttonA","if":[{"is":"down"}]}],
"then":[{
"id":"A",
"moveTo":[
{"angle":[{"attribute":"rotate"}],"by":[{"smallInteger":3}]},
{"angle":[{"number":180}],"by":[{"smallNumber":2}]}
]
}]
},
{
"then":[
{"id":"B","pan":[{
"to":[{
"x":[{"id":"A","attribute":"x"}],
"y":[{"id":"A","attribute":"y"}]
}],
"speed":[{"smallNumber":5}]
}]}
]
}
]
}]
}
This cartridge shows an A
letter you can rotate. Hitting the A button the letter will thrust like an Asteroid ship... but with a pinch of vertical gravity, so it's harder to fly away to the top of the screen. A green square smoothly follows the A
spaceship in the meanwhile. This cartridge doesn't use the Rewtro physics engine at all.
The first two lines of the code
block rotate the A
spaceship when holding down the left and right keys. Then, when the A button is held down, two movements are applied to the spaceship: one toward the current rotate
angle by 3
pixels executing the ship movement and one to 180 degrees (which is to the bottom of the screen) by 2 pixels simulating the gravity.
Finally, the B
green square coordinates are moved from their current values to the spaceship coordinates by pan
with a speed of 5
: the distance between the starting value and destination value is split by 5
and then added to the starting value resulting in a smooth movement.
While manually moving sprites is fun you can do more with less moving them with the Rewtro physics engine. You can control sprites mostly using the set
statement on specific sprites attributes but there are some statements to make your life easier.
speedTo
andsetSpeedTo
add or set a force vector to a sprite like themoveTo
statement. The exception is that this statement changes the spritespeedX
andspeedY
keys instead of its coordinates so it will keep moving and be affected by gravity according to the physics engine.bounce
works together with physics events and changes a sprite speed when colliding with a blocking sprite. It accepts an object made this way:speedX
andspeedY
are getters to a multiplier that's applied to thespeedX
andspeedY
keys speed after colliding horizontally or vertically to another sprite. Negative values will make the colliding sprite bounce back.restitutionX
andrestitutionY
work the same but affecting the opposite force:restitutionX
is applied tospeedX
on vertical collisions andrestitutionY
is applied tospeedY
on horizontal collisions, simulating friction.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","text":"A","x":76,"y":68,"zIndex":1,"gravityY":0.2},
{"id":"B","backgroundColor":5},
{"id":"C","width":160,"x":0,"y":132,"backgroundColor":6},
{"id":"D","width":160,"x":0,"y":0,"backgroundColor":7}
],
"tilemaps":[{"map":["ABCD"]}],
"code":[
{
"when":[{"as":"keyboard","attribute":"left","if":[{"is":"down"}]}],
"then":[{"id":"A","subtract":[{"rotate":[{"smallNumber":4}]}]}]
},
{
"when":[{"as":"keyboard","attribute":"right","if":[{"is":"down"}]}],
"then":[{"id":"A","sum":[{"rotate":[{"smallNumber":4}]}]}]
},
{
"when":[{"as":"keyboard","attribute":"buttonA","if":[{"is":"down"}]}],
"then":[{
"id":"A",
"speedTo":[{"angle":[{"attribute":"rotate"}],"by":[{"smallInteger":1}]}]
}]
},
{
"then":[
{
"id":"B",
"setSpeedTo":[{
"angle":[{"angleTo":[{"id":"A"}]}],
"by":[{"smallInteger":1}]
}]
}
]
},
{
"when":[{"event":"hitWall","id":"A","if":[{"is":"collidingWith","id":"C"}]}],
"then":[{
"bounce":[{
"speedY":[{"float":-0.7}],
"restitutionX":[{"float":0.3}]
}]
}]
},
{
"when":[{"event":"hitWall","id":"A","if":[{"is":"collidingWith","id":"D"}]}],
"then":[{
"bounce":[{
"speedY":[{"float":-1}]
}]
}]
}
]
}]
}
This cartridge is the sequel of the previous example. This time our A
spaceship is affected by gravity even when not thrusting, it accelerates and bounces when hitting the yellow ceiling and purple floor. The floor feels like rubber: when the ship collides to it, it will bounce back vertically and slow down horizontally a little due to some friction happening. Finally, the chasing green square is moving constant speed.
The first two lines of code are the same as before: the sprite rotation is changed when pressing the left and right keys.
Hitting the A button a force is added to the current A
spaceship sprite using speedTo
to the direction that's facing. We don't need to apply gravity since we've set the gravityY
sprite property in its descriptor: the physics engine will do the rest.
Then a fixed speed is set to the green B
sprite aiming to the A
spaceship with a speed of 1
pixel per frame. This speed is set and not added so B
won't gain speed like the spaceship.
The last two lines handle the spaceship collisions:
- the first one checks the
C
floor, giving to the colliding spaceship 7/10 of its vertical speed, making it bouncing slower every bump, and 3/10 of its horizontal speed, simulating friction. - the second one checks the
D
ceiling but this time returns to the spaceship the full vertical speed back and without simulating any friction.
In a Rewtro cartridge, you can add both sounds
and songs
in your game cartridges using data blocks and play them using tilemaps
. And... that's not enough to add sounds to a game.
You can play a sound effect setting playAudio
to a getter to the sound id
to play or stop all playing sound effects just setting stopAudio
to true
. You can also stop a single sound effect channel instead of setting stopChannel
to a getter to the channelId
you want to stop - learn more about assigning a channel to a sound effect in the data blocks chapter.
You can play one of the songs
defined in a data block setting runSong
to a getter to the song id
. Then you can control the music player to stop, pause, and play the playing song setting stopSong
, pauseSong
, and playSong
to true
. You can also change the current song
tempo
setting the setSongTempo
to a getter key with the new tempo.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"systemConfiguration":[{"controllerModel":3}],
"data":[{
"id":"A",
"sounds":[
{"id":"A","wave":"square","sustain":255},
{"id":"B","wave":"whitenoise"}
],
"music":[
{
"id":"A",
"notes":[
["C4-"," "," ","C4-"," "," ","B3-"," "," ","B3-"," "," "],
["C4-"," "," ","G7-"," "," ","C4-"," "," ","G7-"," "," "]
],"instruments":"AB"
},
{
"id":"B",
"notes":[
["A3-"," ","B3-","A3-"," ","B3-"," "," "," "," "," "," "],
["C4-"," ","G7-","G7-"," ","C4-"," "," ","G7-","G7-"," ","G7-"]
],"instruments":"AB"
},
{
"id":"C",
"notes":[
["A3-"," ","B3-","A3-"," ","G3-"," "," ","G3-","A3-"," ","B3-"],
["C4-"," ","G7-","G7-"," ","C4-"," "," ","G7-","G7-"," ","G7-"]
],"instruments":"AB"
}
],
"songs":[{"id":"A","music":"ABAC","loopTo":0,"tempo":2}],
"code":[
{
"when":[{"as":"keyboard","attribute":"buttonA","if":[{"is":"hit"}]}],
"then":[{"playAudio":[{"character":"A"}]}]
},
{
"when":[{"as":"keyboard","attribute":"buttonB","if":[{"is":"hit"}]}],
"then":[{"stopAudio":true}]
},
{
"when":[{"as":"keyboard","attribute":"buttonC","if":[{"is":"hit"}]}],
"then":[{"setSongTempo":[{"smallNumber":1}]}]
},
{
"when":[{"as":"keyboard","attribute":"buttonD","if":[{"is":"hit"}]}],
"then":[{"setSongTempo":[{"smallNumber":4}]}]
},
{
"when":[{"as":"keyboard","attribute":"up","if":[{"is":"hit"}]}],
"then":[{"runSong":[{"character":"A"}]}]
},
{
"when":[{"as":"keyboard","attribute":"down","if":[{"is":"hit"}]}],
"then":[{"stopSong":true}]
},
{
"when":[{"as":"keyboard","attribute":"right","if":[{"is":"hit"}]}],
"then":[{"playSong":true}]
},
{
"when":[{"as":"keyboard","attribute":"left","if":[{"is":"hit"}]}],
"then":[{"pauseSong":true}]
}
]
}]
}
No screenshot this time: the screen is completely black but there is something to hear.
Hitting the A button a sound is played and then stops. Hitting the B button before the sound is over it will stop it immediately.
Hit the up direction to play a song and down to stop it. You can pause the song hitting left and resume it hitting right.
Hit the C and D buttons to change the music tempo. Remember that tempo is kept when music is paused and resumed but it's reset to the default value when stopped.
There isn't a true scene manager in Rewtro but it's possible to load one or multiple data blocks at once simulating a change of scene or a new game state. You can decide to reset the current scene with runScene
, removing all of the running sprites and forgetting the loaded code
blocks or replace part of the loaded data in realtime with load
.
Both of these statements accept a getter to a string or a single character identifying a sequence of data blocks to load.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","x":76,"y":64,"textAlignment":"center","text":"TITLE~SCREEN","scale":2},
{"id":"C","x":76,"y":68,"textAlignment":"center","text":"GET READY"}
],
"tilemaps":[{"map":["A"]}],
"code":[{
"when":[{"as":"keyboard","attribute":"buttonA","if":[{"is":"hit"}]}],
"then":[{"runScene":[{"string":"1S"}]}]
}]
},{
"id":"S",
"tilemaps":[{"map":["C"]}],
"code":[
{
"when":[{"as":"scene","attribute":"timer","if":[{"is":">","smallNumber":25}]}],
"then":[{"id":"C","remove":true,"load":[{"string":"R"}]}]
},
{
"when":[{"id":"X"}],
"then":[{"set":[{"backgroundColor":[{"list":[0,8],"randomNumber":true}]}]}]
}
]
},{
"id":"R",
"sprites":[{"id":"Y","width":236,"y":136,"backgroundColor":6}],
"tilemaps":[{"map":["Y"]}],
"code":[
{
"when":[{"as":"scene","attribute":"timer","if":[{"is":"==","smallNumber":50}]}],
"then":[{"id":"X","set":[{"speedY":[{"smallNumber":3}]}]}]
},{
"when":[{"id":"Y","if":[{"is":"collidingWith","id":"X"}]}],
"then":[{"runScene":[{"as":"scene","attribute":"value5"}]}]
}
]
},{
"id":"1",
"sprites":[
{"id":"X"}
],
"tilemaps":[{
"x":40,"y":16,
"map":[
"X X X X X ",
" X X X X X"
],
"set":[{"value5":"2S"}]
}]
},{
"id":"2",
"tilemaps":[{
"x":40,"y":16,
"map":[
"X X X X X",
" X X X X ",
" X X X "
],
"set":[{"value5":"A"}]
}]
}]
}
Good news. This cartridge is a game featuring a title screen and multiple levels. Bad news. Except for the title screen there is no interaction. Sorry.
When the game starts the A
data block is loaded and executed as usual. It describes the A
and C
sprites but displays only the first one using tilemaps
. The A
data block code
waits for the player to hit the A button and then executes the runScene
statement with 1S
.
runScene
clears all of the spawned sprites, stops any music and fakes load time as explained in Rewtro cartridge chapter. Then load the 1
block and S
block and start the game again:
- the
1
data block defines theX
sprite and spawn, spawn them in a tilemap, and set2S
to thevalue5
- the
S
scene spawns theC
sprite, which is theGET READY
message. Then thecode
block waits a second making theX
sprites flash, remove theC
sprite andload
theR
data block.
The load
statement won't remove the spawned sprites or fakes loading time but spawn its tilemap into the current scene and replaces the running code with the one coming from the R
That's why the sprites are not flashing anymore: the flash code has been replaced.
Even if the code
in R
looks like it's waiting for two seconds (50
frames) it happens after one second by the time the R
data block is loaded and that's because the load
statement doesn't reset any timer
. After waiting it gives the X
sprites a vertical speed, moving them to the just spawn Y
sprite.
The second line of R
triggers when any of the X
sprites collide with the Y
bar: it executes a loadScene
using the scene attribute value5
as key value. This way the 2S
data blocks are loaded, looping from the GET READY
message but with a different tilemap.
Finally, the 2
data block will set A
as the next scene, ending our game and moving it back to the title screen.
Rewtro features a tiny event system. Events are identified by a name and can be triggered on a sprite by its life cycle, the physics engine or by you using the triggerEvent
statement. Events are caught by when
commands and you can read more on the code commands chapter.
To trigger an event from your code set the triggerEvent
to event0
, event1
, event2
, event3
, or event4
. You can also trigger the system events hitWall
, isSpawn
, and isRemoved
.
An event can be triggered on a sprite just once per game frame. If you want to override this behavior set the forceEvent
key to true
in your triggerEvent
call.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","flags":"F"},
{"id":"B","flags":"F","x":0,"y":8}
],
"tilemaps":[{"map":["AB"]}],
"code":[
{
"then":[
{"flags":"F","set":[{"value0":[{"smallNumber":0}]}]},
{"id":"A","triggerEvent":"event0"},
{"id":"A","triggerEvent":"event0"},
{"id":"A","triggerEvent":"event0"},
{"id":"B","triggerEvent":"event0","forceEvent":true},
{"id":"B","triggerEvent":"event0","forceEvent":true},
{"id":"B","triggerEvent":"event0","forceEvent":true},
{"flags":"F","set":[{"text":[{"attribute":"value0"}]}]}
]
},{
"when":[{"event":"event0","flags":"F"}],
"then":[{"sum":[{"value0":[{"smallNumber":1}]}]}]
}
]
}]
}
This cartridge shows two event counters: the event0
is called three times on both of the sprites but the first one counts 1
call and the second one is 3
.
First, both of the sprites value0
counter is reset to 0
. Then the event0
event is called 3 times on sprite A
and then on the sprite B
but with the forceEvent
flag set to true
. Finally, the value0
counter is displayed on the screen.
The last line of the code and catches the event0
event called on sprites having the F
flag set - which are A
and B
. The triggered code increments the value0
counter. Notice that even if this line is into the code
block lines it's executed only when the event is triggered: the counter is increased exactly after the triggerEvent
call, pausing the code execution.
Since the same event can be triggered on a single sprite just once per frame the A
sprite will run the event0
code just once even after three consecutive calls. The B
sprite event0
is also called three consecutive times but the forceEvent
flag set to true
will force the event code to be executed at every call.
In the code statements chapter we've seen that every code
block may contain a when
command key that describes an iterator or condition, such a list of sprites or a collision condition, and a mandatory then
command that contains one or more lines of code to be executed.
It's possible to execute a sub-code block using the code
statement and describing block content as usual in its key value.
Code sub-block execution can be stopped using the break
statement at any time: just after the current line the running sub-block execution will be aborted and the previous code block execution is continued.
The loaded code
execution that's run every frame can be also considered a sub-block so break
ing its execution at the root level will end your game logic for that frame.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","flags":"F"},
{"id":"B","flags":"F","x":0,"y":8},
{"id":"C","flags":"F","x":0,"y":16}
],
"tilemaps":[{"map":["ABC"]}],
"code":[
{
"then":[
{"flags":"F","set":[{"text":[{"attribute":"value0"}]}]},
{
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":25}]}],
"then":[
{"break":true,"id":"A","sum":[{"value0":[{"smallNumber":1}]}]},
{"id":"C","sum":[{"value0":[{"smallNumber":1}]}]}
]
}
]
},
{"break":true,"id":"B","sum":[{"value0":[{"smallNumber":1}]}]},
{"id":"C","sum":[{"value0":[{"smallNumber":1}]}]}
]
},{
"then":[
{"id":"C","sum":[{"value0":[{"smallNumber":1}]}]}
]
}
]
}]
}
This cartridge shows 3 timers but even if the code
tries to increase all three of them the C
one is stuck to 0
due to multiple code break
s.
Its first line of code
executes the then
command without any condition and its first statement displays every value0
counter on the screen.
Then a sub-code
block is executed: in this block the A
sprite value0
is increased every second. Due to the break
statement, the code
block execution is aborted, the next line is not executed, and the C
sprite counter is not increased.
The code execution resumes from the code
statement we've left: the B
counter is increased but the break
statement will prevent the C
counter to be increased.
Since there isn't any previous code
statement it's the turn of the loaded code
execution to be aborted. The last line of code will be never executed and so the C
counter will remain 0
every frame.
Rewtro debugging tools are not that great but you can check out the _DEBUG
statement if you are in trouble for now. Setting its key to different string values will make different things to happen:
LOG
logs the current picked object to the JavaScript consoleDEBUGGER
also breaks the JavaScript interpreter into the Rewtro engine code
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":25}]}],
"then":[{"attribute":"timer","_DEBUG":"LOG"}]
},
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"==","smallNumber":125}]}],
"then":[{"attribute":"height","_DEBUG":"DEBUGGER"}]
}
]
}]
}
This cartridge shows... a blank screen. But in the JavaScript console will be logged the the scene timer every second and, at the fifth one, the Rewtro engine execution is aborted and the debugger is opened.
Random getters like randomNumber
and randomValue
will return a different random value every time a new line of code is executed. This way you can assign the same random number to multiple objects without worrying to store it somewhere. You may want to override this behavior setting the randomize
key to true
so a different random number will be picked every time instead.
{
"systemVersion":"0.2",
"metadata":{
"title":"My first game"
},
"data":[{
"id":"A",
"sprites":[
{"id":"A","height":80},
{"id":"B","height":80},
{"id":"C","height":80}
],
"tilemaps":[{"map":["AAA BBB CCC"]}],
"code":[
{
"when":[{"as":"scene","if":[{"itsAttribute":"timer","is":"%%","smallNumber":25}]}],
"then":[
{"id":"A","set":[{"backgroundColor":[{"list":[3,15],"randomNumber":true}]}]},
{"id":"B","set":[{"backgroundColor":[{"list":[3,15],"randomNumber":true}]}]},
{"id":"C","set":[{"backgroundColor":[{"list":[3,15],"randomNumber":true}]}],"randomize":true}
]
}
]
}]
}
This cartridge shows three color changing bands made of three sprites each. The first and the second bands have different colors but the third one is striped.
The bands made with A
and B
sprites are randomly colored in the same way but by different lines of code. Since the random number is picked once per line all of the A
sprites will have the same random color and all of the B
sprites will have another random one.
The third line of code looks like the previous but the randomize
flag forces randomNumber
to pick a different number for each C
sprite and resulting in a striped band.
Code statements are executed following a fixed order. This is the list of all code statements sorted by execution priority.
- Mode set
randomize
- Get changers
spawn
,fillAreaWithPattern
,outlineAreaWithPattern
, orareaCopy
- Statements
remove
bounce
areaFlipX
areaFlipY
areaRotate
areaCopy
placeAt
set
invert
subtract
sum
divide
multiply
module
pan
push
moveTo
speedTo
setSpeedTo
stopChannel
stopAudio
playAudio
stopSong
pauseSong
runSong
playSong
setSongTempo
triggerEvent
/forceEvent
runScene
load
break
code