Our game will be based on scenes. We already described the advantages of scenes in a previous chapter. We can sum up the purpose of the scene system with these characteristics:
- Encapsulation: a scene will encapsulate a certain feature or component of your game. All the behaviour specific to that feature will be contained in that scene.
- Reusable: scenes will be instanced inside other scenes and contribute to game optimisation as well as reducing production time.
- Configurable: scenes can have different parameters that are unique for each instance, and affect their behaviour/appareance.
- Easier teamwork: Scenes can be edited by different members of the team, to enable them to integrate their content to the game.
Based on this, we will created a scene for each element that our game will need:
- a scene for the background,
- a scene for the player,
- a scene for each obstacle type,
- one scene to mix them all (and in the darkness bind them).
Creating a scene
So let's create our first scene.
- Go toScene > New Scene menu. This won't display anything in the editor as the scene is empty. In fact, if we try to save it right now, Godot tells us there is no possibility to save an empty scene. So let's add some elements to it.
- In the Scene panel, click theAdd/Create a new node or use Ctrl+A. This will show the Create New Node window.
- The list of pre-defined node types is quite long so we usually use the search field to get the one we want. Let's write Node2D which is a basic node type for 2D games.
- Among the displayed results, select the one you need and click create.
- You can now save the scene withScene > Save scene as... Here we save it in a scene folder that we just created.
Let's name it scene1.scn or level.scn.
- Eventually test the scene by clicking the play scene button in the header section of the editor.
The Node2D is now added to the scene and is shown both in 2D editor (main panel) and in the Scene panel.
Scenes can be saved with the .scn (binary) and .xscn (text-based XML) extensions. If you want to keep control on the content and do some batch operation, you might prefer the second one. But keep in mind that usually .scn will give better performance at runtime for complex scenes. In the upcoming 2.0 version, a new .tscn human-readable text-based format is also available and will later on deprecate the .xscn format.
Defining the main scene
As we said, the game will have many scenes. So there is a need to define with which scene the game will begin.
- Go back to the project settings (Scene > Project Settings)
- In the application category, check the main_scene setting and click the folder icon on the right of it.
- Choose the scene of your choice among the scenes you saved.
- You can now play the entire game by clicking the play game button . The game will also available from the project manager and played if the run button is pressed.
The bat is the main character of the scene. It will have a Sprite (or some other visual element), a Camera following it, and a collision area. When defining the bat scene, we keep in mind the following points:
- The bat will have many parameters to control various aspects of gameplay, such us the speed, the gravity force, the force of the jump. Expose these parameters to allow the game design team to tweak them and test them in the game.
- The bat will have a collision area, which will also be tweaked by the game design team. In this example game we use a Control node, even though we don't need all the features the Control provides, simply because it provides a simple way to modify a rectangle area inside the editor. Sometimes this is better than using the correct method from the programming point of view; we could use a Rect2 type to define the collision area, but that would be harder to edit for a game designer.
- We define two animations, fly and die, and we write code assuming those animations will exist. Remember that animations can control the entire scene, change the sprite for the bat, play sounds, fire off particle systems, show text, etc. This is all up to the artists.
With this in mind, we have a technical specification we can provide the team for this scene:
- There has to be a Control node, with the name area. The scripts will retrieve the size of this Control, to calculate collision.
- There has to be an AnimationPlayer node, with the name animation, and two animations called fly and die.
- There has to be a Camera node (type: Camera2D), following the bat; set the Limit > Bottom property to e.g. 800 so the camera stops when reaching the ground.
These are the only requirements for the scene to work, everything else (including for example, the name and type of the sprite node) is up to the team, and somewhat irrelevant to us. They can add nodes, use different types of nodes, etc. It is up to the team to make it look good, and play balanced, and up to the producer to organize everyone to provide content to the scene.
The scene for the user interface shows information about the current game, such as the score, lives, player status, etc. In this case, we have just one Label that displays a number, with the current score. The code will look for a node called label and set a number to it.
In this case we use a CanvasLayer node as root, and add a Label called points as a child. Using CanvasLayer instead of just a basic Node lets us define freely the z position of the gui. Viewport is supposed to be placed on 0, but we will also have a parallax background. So let's set the layer value as 2 and the GUI should always be on top, whatever the scene trees are.
The obstacle scene is spawned by the main level. This scene has 2 damage areas (one at the top, one at the bottom), and a bonus area in the middle. If the player goes through the bonus area, a point is awarded. This scene will be very important to the game designer, because it exposes the parameters used to balance the game (in conjunction with the parameters exposed in the bat scene). This can be a bit tricky to express, so be prepared to have many discussions with your game design team to make sure you can provide them all the options they need. Don't be afraid to provide basic elements, game designers don't need a pretty interface, they need freedom :) And sometimes they know the basics of programming (and sometimes they know a lot of programming, but don't count on this). Give them freedom, and they will surprise you!
In our example, the obstacle scene provides the following elements:
- A node named top, with the top element. This element has its own damage area, and sprite.
- A node named bottom, with the bottom element.
- A node named point, with the bonus area.
- Parameters top_range and bottom_range to control the range of positions where the top and bottom parts will spawn. These values specify two numbers, the minimum and maximum position on the Y axis that each part can have when they spawn. The gap between the top and the bottom parts will be the bonus area.
Optimize the workflow by instanciating obstacle scenes
Once your game is working, we can go deeper in the production. It will be time to add objects to our world.
- Create a new scene saved as obstacle.scn or bottom.scn (because will need also a top obstacle for clouds) and set it as followed :
- Add aControl area node
- Let's add some art element to our player by using an image file. Select the Sprite node.
- In the Inspector, look at the Texture property and click on the list on the right of the label and chooseLoad to get a file browser in which you will choose the right image.
- If you need to define the way this ressource is used, click the > button at the right of the texture preview field, and you will get more settings. You will particularly see the file path and Size as it is used in our node.
- Save the scene.
Now that we have made a first element we can add it to our main scene and use it in context.
- Create a scene file that will manage both obstacle position.
- Go to Scene tree panel. As the new node will be added as a child of the selected node, select the root node.
- Click the + button on top of the window and a popup will let you choose the obstacle.scn scene.
- Once done, it is displayed in the Scene tree and in 2D view. Let's go on until you get the right tree.
- Instantiate this new scene in the main scene or any other scene that will help, and so on until all the logic of the game is bound to the main scene.
The Scene panel displays the name of the instance, next to it a folder icon that shows it is an instanced scene, and then available script for it. If you right-click on the folder icon, the Show Children option will display its child nodes directly in the scene tree.
We are now done with instantiation. It really seems like we have done a copy-paste, but using this method, any changes made to the original bat or obstacle scenes will be applied to instances. In term of team management it helps everyone work on its part at their own rhythm, without having to worry about other contributors. On the other hand, it is still important to verify that the main scene is working well after deep changes. In this case, refer to game design documents or scene graph to know which parts are affected.
The main scene instances everything together, and in this example serves as the main title screen. The main scene will also receive input and notify every element of events that happen during the game (collision between objects, game was reset, etc.). There are many methods to structure your game and the communication between your game elements. Objects can send messages to each other (through method calls or other type of messaging), or they can all send events to a main scene. They can also broadcast their events through signals, that objects connect to, or sending messages to groups. It's up to you to decide, but keep in mind that level designers will want as much freedom as possible to instance objects around the level.
In our example scene, there are two animations: game_over and game_reset. These can be used to display/hide a title screen, or show messages to the user. (See chapter Animations to know more about animation factory.)
There is also a parameter that controls how often the obstacle scene spawns, called spawn_distance, which is the distance in pixels between two spawned obstacles.
Finally we will add a ParallaxBackground node (with 1:1 scaling).
- Add a ParallaxLayer child node.
- Set the
Motion > Mirroring property to the size of your sprite.
- Add a Sprite child node.
- Set the Texture property to your background image.