The code it generates
The scene editor needs nothing special from the VM: each room is translated into plain JARU code, which your game imports and calls. That is why it works the same on Windows and on every board.
This page tells which files it writes, which functions each room has and how they mix with your code. The walkthrough of a whole game, with its loop step by step, is in Your first game with rooms.
In the names on this page, <Room> is the name of the room (its Name field, see The room and the game) and <ROOM> is the same in capitals. For room Level1 they are Level1Load and LEVEL1_WORLD_W.
The files it writes
| File | What it holds | When it is written |
|---|---|---|
Code/<Room>.gen.aru | One room: its tables, its loader and its functions. | When you save the room (Ctrl+S), with Play and on every Build, if it has Generate .aru on. Generate (Ctrl+G) writes it even when it is off. |
Code/SceneRuntime.gen.aru | What the rooms share: the game variables, the controls, the frame rate and the code of the behaviours they use. | With every room. If the Build generates no room at all, it removes it. |
Code/Drivers/DRV_SEESAW_GAMEPAD.aru | The driver of the I2C gamepad. | The first time, with the gamepad on in the Game tab. After that it is left alone. |
Code/_Play_<Room>.gen.aru and Code/_Play_Display_.gen.aru | The program of Play: it opens the screen and plays the room. | On every Play. It is not part of the project, and the next Play deletes it. |
Build/flash/Scenes/World.gmap | All the rooms, as data. | On every Build, only in data mode. |
The Build is Build › Compile, or the Compile button of the scene editor. What it does with the rooms shows in Output: the ones it generates, the ones it skips and the warnings of the problems panel for each one.
The .gen.aru files and the runtime show in the Code folder of the explorer, like your code.
The IDE writes the .gen.aru files again when you save each room and on every Build: whatever you change in them is lost. Your code goes in your own .aru files, in the creation code of the room or in the code of each object.
The functions of each room
| Function | When it exists | What it does |
|---|---|---|
<Room>Load(spawn) | Always | Builds the room and returns room. spawn can be nil: see The spawn hook. |
<Room>FollowCamera(room) | If Room › Camera › Follow has an object | Makes the camera follow that object. Once, after <Room>Load. |
<Room>Update(room) | If the room has behaviours, rules, states, templates, variables or code in some object | One frame of what moves by itself: the I2C gamepad, the clock of the room, the states, the behaviours and the rules. Every frame, before room.scene.update(). |
<Room>Resolve(room) | Always | Pushes the dynamic bodies out of the solid tiles of the collision map, and keeps inside the room the ones with Keep in room. Every frame, after room.scene.update(). Returns what grid.hits returns, or nil if the room has no collision map. |
<Room>Draw(room) | Always | Paints all the layers in the order of the editor, with their parallax, their shapes and their decor, and the texts of Show a variable over everything. Use it instead of Draw.scene, which only paints the scene map and the sprites. |
<Room>Zone(name) | If the room has zones or shapes, and your code names <Room>Zone | Finds a zone or a shape by its name. Returns its row, [name, tag, kind, shape, x, y, w, h, color, filled], or nil. |
Calling a function that does not exist compiles, but the game stops when it reaches that line.
The room has more functions (<Room>Events, <Room>Spawn, <Room>Code0, <Room>Fsm0_Step…): they are the rules, the templates, the code of the objects and the states. The room calls them itself, not you.
The constants and the tables
| Name | What it is |
|---|---|
<ROOM>_WORLD_W, <ROOM>_WORLD_H | Room (px) |
<ROOM>_TILE_W, <ROOM>_TILE_H | Tile size |
<ROOM>_VIEW_W, <ROOM>_VIEW_H | Screen (px) |
<ROOM>_MUSIC, <ROOM>_COLLMAP | The path of the music and that of the collision map. |
<ROOM>_TAG_<TAG> | Each tag of the room, as text. |
<ROOM>_I_<OBJECT> | The number of each named object in the table of objects. |
<ROOM>_ENTITIES, <ROOM>_DECOR, <ROOM>_ZONES | The tables of the objects, of the decor and of the zones and shapes. |
Only the constants your code or the room's own code names are written: on a board, each constant takes RAM even if it is not used. The others stay in a comment, with their value. Name one in your code and the next time the room is generated it will be there. The same goes for <ROOM>_ZONES and <Room>Zone.
What room holds
| Field | What it is |
|---|---|
room.scene | The Scene of the room, with its physics, its camera and its sprites. See the Scene module. |
room.grids | One GridMap per tile layer. |
room.collision | The GridMap of the collision map, or nil. |
room.sprites | The sprites of the placed objects. |
room.named | The named objects: room.named["player"] is its sprite. |
room.game | The game variables. It is the same object in every room. |
room.nextScene | The room an Ask for another room rule has asked for, or "". |
room.vars | The room variables. |
room.tagged | The live sprites of each tag, in a map: tag → list. |
room.dt, room.elapsed | The seconds of this frame (taken from Display.dt, 0.1 at most) and those since the room loaded. |
The last three rows are only there in a room with <Room>Update.
The shared runtime
Each room imports Code/SceneRuntime.gen.aru. Importing it from several rooms costs nothing: the preprocessor keeps the first one.
| Name | What it is | When it is there |
|---|---|---|
sceneGame | The game variables: sceneGame.lives. It is the same object as room.game. | Always |
SceneInputMap() | Maps the controls of the Game tab: the GPIO on each board, the I2C gamepad and, on Windows, the keys. Nothing calls it for you: call it once, at start-up. | Always |
SceneInputRead() | Reads the I2C gamepad. Call it every frame where no room runs, such as a title or an end screen. | If your code names it |
SceneGameReset() | A new game: every game variable goes back to its start value. | If your code or a rule uses it |
SceneGameLoseLife() | One life less, and health goes back to its start value. | If your code or a rule uses it |
SceneGameClamp() | Keeps health between 0 and maxHealth. Call it if your code changes either of them. | If your code or a rule uses it |
SCENE_GAME_HEALTH0 | The health each life starts with. | If your code or a rule uses it |
It also holds:
Display.fps, if Rate (fps) in the Game tab is not 0. It is set once, at start-up.- What the I2C gamepad needs, if it is on.
- The code of the behaviours and of the helpers the rooms use, only once.
Whatever nothing names is not written: on a board, every function and every constant takes RAM even if it is not used. A comment in the file itself names the ones left out. To get one, name it in your code (even a comment will do) and the next time it is generated it will be there.
The spawn hook
<Room>Load(spawn) takes a function, or nil. If you give it one, the room calls it with each object (e) before creating it, and what it returns decides what happens:
| Returns | What happens |
|---|---|
| A sprite | The room uses that sprite for the object: it gives it its position, its flip, its angle, its frame, its body type and, if it has one, its own collider; it adds it to the scene, to room.sprites and to room.named, and attaches its behaviours. |
false | The room does not create the object or its behaviours: it is all yours. |
nil | The room creates it as usual, with its sprite or its image. |
The visible sprites and images of the object layers go through spawn. Shapes, zones, routes, decor and the copies of a template do not.
e is its row of the table of objects:
| Field | What it is |
|---|---|
e.name, e.tag | Its name and its tag. |
e.kind | "sprite" or "image". |
e.res | The path of its sprite or its image. |
e.x, e.y | Its position in the room: the pivot of a sprite, the centre of an image. |
e.w, e.h | Its width and its height. |
e.angle, e.flipX, e.flipY, e.frame | Its angle, its flip and its starting frame. |
e.layer | The number of its layer. |
e.body | Its body type, the value of sprite.bodyType. |
e.coll | Its own collider, [x, y, w, h], or nil. |
e.props | Its properties (PROPERTIES), in a map, or nil. |
An example: the game creates the enemies and keeps them in its own list, it handles the goal itself, and the room creates everything else.
import "Screen.aru" // opens the screen: it goes first
import "Level1.gen.aru"
use Sprite
var enemies = []
// The room calls it with each object, before creating it.
func MakeObject(e)
if (e.tag == "enemy") then
var s = Sprite.load(e.res)
enemies.append(s) // the game keeps them in its own list
return s // the room places it and adds it to the scene
end
if (e.tag == "goal") then
return false // the room does not create it: it belongs to the game
end
return nil // the room creates everything else
end
var room = Level1Load(MakeObject)
In data mode, SceneLoad(name, spawn) uses spawn the same way.
With your own code
Your first game with rooms shows the loop of a game with several rooms, switching rooms and the title and end screens. Here is what you need to know to write your own.
At start-up:
- Open the screen first, with
Display.openin a small file of your own that is imported first. The header of each.gen.aruexplains why, and the guide shows it step by step. - Call
SceneInputMap()once: Play does it, but your game has to do it itself.
When entering a room: <Room>Load and, if it exists, <Room>FollowCamera.
Every frame, in this order:
<Room>Update(room), if it exists: the behaviours give the bodies their velocity, and the physics of this same frame uses it.room.scene.update(): the physics, the camera and the animations.<Room>Resolve(room): pushes the bodies out of the tiles the physics has just moved them into.<Room>Draw(room).Display.update(). Neither the rooms nor the runtime call it. It is the one that waits for the next frame, at the Rate (fps).
To switch rooms, read room.nextScene: the Ask for another room rule only writes there the name it asks for, and your loop decides when to load it. (this room) asks for the same room, to start it again.
import "Screen.aru" // opens the screen: it goes first
import "Level1.gen.aru"
use Display
SceneInputMap() // the controls of the Game tab
var room = Level1Load(nil)
Level1FollowCamera(room) // if the camera follows someone
while (true)
Level1Update(room) // if the room has behaviours or rules
room.scene.update() // the physics, the camera and the animations
Level1Resolve(room) // pushes the bodies out of the solid tiles
Level1Draw(room) // all the layers
Display.update() // presents and waits for the next frame
if (room.nextScene != "") then
// A rule has asked for a room. There is only one here: start it again.
room = Level1Load(nil)
Level1FollowCamera(room)
end
end
Where your code goes:
| Where | When it runs |
|---|---|
Your .aru files | When you call them: the loop, the menus and the functions of the Call a function of yours action. |
| Room › Creation code... | Once, at the end of <Room>Load, with the room built. See The room and the game. |
| The code of an object | Once, when the object is created and for every copy of its template. See Objects. |
The functions Call a function of yours calls go in a .aru of the project, outside any other function, and receive (spr, other, room): see Rules and states.
Each control of the game takes an action number of Input, and the Game controls window tells which. If your game maps more actions with Input.map, use numbers no control has.
On a title or an end screen, where no room runs, the game variables are sceneGame, SceneGameReset() starts a new game and, with the I2C gamepad, SceneInputRead() reads it every frame.
Rooms as data
As code, each room is its own .gen.aru and the game carries the code of all of them. On a board, that takes RAM even while the room is not being played.
As data, the Build writes every room into one table and a single loader loads them by name. Only the room being played is in memory, and what appears in every room (the same sprite or the same image) is loaded once and kept.
When it is used
Data mode turns on when the code of your game names SceneLoad: any .aru of the project that is not a .gen.aru. A comment or a string counts too. If it does not name it, the rooms are code, as usual.
The Build says so in Output: Data mode: the game names SceneLoad, so every scene also goes to Build\flash\Scenes\World.gmap as data.
What the Build writes
Build/flash/Scenes/World.gmap: everything of every room with Generate .aru on, except their tilemaps. It is a table of bytes: it is not edited.- The tilemaps stay in their
.gmapfiles, with the other maps, inBuild/flash. Of each map only the half that is used is exported: the tiles, the cell values or both. - The loader, in
SceneRuntime.gen.aru. - The rooms that are only data have no
.gen.aru: the Build removes the one they had. Play writes its own when it needs it. - A room keeps its
.gen.aruif the game imports it, or if it uses something that only works as code (see What it does not do as data).
The functions
Your game imports no room: it imports SceneRuntime.gen.aru, which brings these functions. Like those of the runtime, they are only written if your code names them.
| Function | What it does |
|---|---|
SceneLoad(name, spawn) | Loads room name and returns room, or nil if it is not there (and says so in the console). name is the name of the .scn file, without the extension and with its capitals. spawn, as in <Room>Load. |
SceneUpdate(room) | One frame of the rules of the room and of the I2C gamepad. Before room.scene.update(). It only exists if some room has rules that run as data, not counting When the room starts. |
SceneResolve(room) | Like <Room>Resolve. |
SceneDraw(room) | Like <Room>Draw. |
SceneFollowCamera(room) | Like <Room>FollowCamera. In a room that follows nobody, it does nothing. |
SceneZone(room, name) | Like <Room>Zone. |
import "Screen.aru" // opens the screen: it goes first
import "SceneRuntime.gen.aru" // brings the loader of the rooms
use Display
SceneInputMap()
var room = SceneLoad("Level1", nil)
SceneFollowCamera(room)
while (true)
SceneUpdate(room) // only if there are rules that run as data
room.scene.update()
SceneResolve(room)
SceneDraw(room)
Display.update()
if (room.nextScene != "") then
room = SceneLoad(room.nextScene, nil)
SceneFollowCamera(room)
end
end
What it does not do as data
- Behaviours, states, templates, room variables, creation code and object code. A room that uses them keeps its
.gen.aru, and the Build warns in Output:as data, this room does not run its … yet. To have them, import its.gen.aruand load it with<Room>Load. - Rules: all of them work except When it leaves the room.
- Actions: Ask for another room, Change a variable (only with game variables), Play a sound effect, Call a function of yours, Destroy, Lose a life and Start a new game work. The others are left out, with a warning in Output.
- The music of Room › Music does not play (see Known issues).
- Solid values and One-way values go from 0 to 255: the others do not count, and the problems panel warns about it.
- Play of a room only plays that room: it does not move on to the rooms it asks for.
Known issues
- In data mode,
SceneUpdateandroom.nextScenemay not exist.SceneUpdateis only there if some room has rules that run as data (not counting When the room starts), androom.nextScene, if some room has rules. Without them, the game stops when it reaches those lines (Undefined variable). Take them out of your loop while your rooms have no rules, and if you use the I2C gamepad, callSceneInputRead()every frame: onlySceneUpdatereads it. - In data mode, import the rooms with double quotes and with their extension:
import "Level1.gen.aru". With single quotes or without.gen.aru, the Build does not see that you import it, deletes that file and the game does not compile. - In data mode, the music of Room › Music does not play:
SceneLoadonly leaves its path inroom.music. If it is not empty, play it yourself after loading the room, withSound.loadMusic(room.music)andSound.playMusic(true); callSound.init()first, on Windows too. - In data mode, the first Play of a room that is only data, after changing an asset (an image, a sprite, a map or a piece of music), can fail because it does not find its
.gen.aru. Press Play again. - Naming
SceneLoadin any.aruof the project, even in a comment, turns data mode on. If you do not want it, do not write that name. - Generate .aru off in a room your game imports: the Build leaves its
.gen.aruas it was and may take out of the runtime what that room needs. The game compiles and stops when it runs. Only turn it off in rooms the game does not import. - The header of each
.gen.arudoes not name<Room>FollowCameraorDisplay.update(). Follow the order on this page, under "Every frame". - If the Build of the resources fails (a room that cannot be generated, for example), the Compile button of the editor leaves the status bar at
Compiling the game... (the details go to Output). The error is in Output.