Hour 17. Game 2: Bloxorz Clone
    What You’ll Learn in This Hour:
    Using 3D objects in a game project
    Making custom transformations on physics objects
    Detecting an out-of-bounds player for a losing condition
    Using the GridMap node
    Loading different levels dynamically
    In this hour, you will make a 3D rolling block puzzle game. You’ll see the main concept of the game and how it is structured. Then you’ll see how
    to create the scenes and develop each level. From there, you’ll add the scripts to handle the movements and detect winning/losing conditions.
    Finally, you’ll play the game yourself and have fun!
    Concept and Design
    This game is a relatively simple, but challenging puzzle game. The player controls a rectangular prism, which has a square base and a height
    two times taller than the base. The player needs to roll it around to fit the square hole in the middle of the floor. Once he does so, he will
    advance to the next level. If he falls to the sides, he will lose and get respawned at the beginning. The floor consists of square tiles that form a
    different path each level. The player needs to be creative in each level to roll the block around and finish in the right position to fall through the
    ending level. Ideally, he should use the least possible number of moves.
    Design Ideas
    Let’s take some time to decide how to structure our game scenes. First, the moving block should be a simple mesh, so we can use a
    MeshInstance node to hold it. The block itself can be a
    RigidBody to make use of gravity when falling.
    The floor is a square grid, which makes it easy to use a
    GridMap node to lay it out. This node acts as a TileMap (as shown in Hour 3), but
    for the 3D space, laying pre-made blocks in a grid. For this, you will need to also make a
    MeshLibrary even though it consists of a single
    tile type. The ending will be checked with an
    Area, and another will be used to set the inbounds of the level. Once the player leaves this
    area, he won’t be able to control the block until it respawns. Each level should have its own spawn point, which can be set with a
    Position3D node.
    For the code part, you will make use of a few simple state machines to determine the transitions and winning/losing conditions based on the
    current orientation and rest position of the block (whether it’s standing or lying). A few
    Timer nodes will set the wait times for respawning
    and avoid player movement before the block is ready. Signals will be used to communicate between different scenes. A singleton will be used
    to find the levels and to keep track of the current one.
    Game Scenes
    Break everything down in scenes to help structure the game with the employment of reusable components. Table 17.1 shows the description of
    each scene.
    TABLE 17.1 Bloxorz Game Scenes
    Scene Description
    Game The main scene. This is where the game starts and everything is connected. Put here the
    WorldEnvironment, the
    DirectionalLight, and the
    Camera. It will also load the block and levels.
    Block
    The controllable block. This scene will be based on a
    Spatial. It will contain the
    RigidBody and
    MeshInstance to
    represent the block itself. There will be a couple of
    Area nodes to control the out-of-bounds checking. You’ll also use a couple of
    Timer nodes for some controls.
    Levels Each level will be its own scene. You will make a base scene with all the components:
    GridMap for layout,
    Area nodes for
    ending and inbounds check, and
    Position3D for the spawning point. The specific levels will inherit this scene and change the
    parameters.
    MeshlibThere’ll be also a scene to convert into a
    MeshLibrary for use with the
    GridMap.
    Making the Scenes
    There’s not much to change in the Project Settings. Since this is a 3D project, the window size is quite flexible, and resizing just changes the
    amount of area seen by the camera. Yet, there’s a bit of configuration that can make life a bit easier.
    First, let’s change the default gravity. The block should fall quite quickly, so increase it to about 50. You can set that under Physic > 3d >
    Default Gravity in the Project Settings. You should also give names to the physics layers to make it easier to set on the objects without getting
    111111111
    22222222lost (Table 17.2). This can be done under Layer Names > 3d Physics.
    TABLE 17.2 Layer Names
    Layer
    Name
    Layer 1
    Floor. This is the one applied to the
    GridMap.
    Layer 2
    Block. This will be applied to your controllable character.
    Layer 3
    Bound. It represents the ending and inbounds areas.
    TIP
    Orthogonal versus Perspective
    Since this is a grid-based game, it makes sense to use the orthogonal view so that all of the squares are equal in size. You can toggle the view
    between perspective and orthogonal in the Viewport menu (which is on the top left of each viewport) or by pressing the 5 key on the numeric
    keypad.
    Mesh Library Scene
    A good place to start is the Mesh Library source scene. This is the base for the
    GridMap, and can be changed later to tweak the floor if
    needed.
    Start by creating an empty scene. Add a
    Spatial node as the root so it will be considered a 3D scene. In the
    MeshLibrary, there are
    a collection of
    MeshInstance nodes. Add one, and call it “Floor.” For its Mesh property, add a new
    CubeMesh. Set the size of the
    mesh to (0.95, 0.1, 0.95). This will make it a short square tile. It will be a little smaller than the unit square to make the grid more visible.
    Add a
    SpatialMaterial to the resource. Note to add it to the
    CubeMesh resource and not to the
    MeshInstance node. This will
    ensure that the material is properly applied to the final object in the
    GridMap. A trick to add a simple outline is to add material to the Next
    Pass property. In this new material, set it to Unshaded, add a Grow of 0.01, change the Cull mode to front, and set its color to black. This
    outline is not perfect, but it should be enough for this simple game.
    You’ll want the player body to fall in the starting block and avoid falling through it when the game is lost. For this, you’ll need a collision body. To
    acquire that in a Mesh Library, add a
    StaticBody node as a child of the
    MeshInstance. This will be kept when converted. You can
    then add a
    CollisionShape node to it and set the Shape property to a new
    BoxShape. Set its Extends to (0.45, 0.05, 0.45). This is
    half the tile size, because the shape uses this property as a radius and not as length. It’s also a bit smaller than the actual size to make the
    ending hole wide enough for the block to fall through.
    TIP
    Better Graphics
    This example game is very simple, and by that standard, it uses only simple materials without any texture. You can improve on this by making
    custom textures and tweaking the materials to make the visuals more vibrant.
    With the mesh and its collision, the scene is complete. Save it as “meshlib.tscn” to be a reference if something needs to be changed. Then
    click on Scene > Convert To > MeshLibrary to create the resource. Save this as “meshlib.meshlib.” The name is a bit redundant, but it’s not
    an issue for this simple game. You can be more creative during larger projects.
    Block Scene
    The next scene to prepare is the Block. This is our controllable character that we can roll around when playing the game. The root of the scene
    is a
    Spatial node, so we can have the 3D transform and work with it. The structure of this scene can be seen in Figure 17.1.
    FIGURE 17.1
    Structure of the block scene.
    NOTE
    Spatial as Scene Root
    You may remember the tip in Hour 8 to make the physics body the root of the scene and everything else children to make them follow the
    transform. This tip still stands, and you can see in this structure that most of the nodes are under the
    RigidBody.
    The idea here is that because you need to rotate the body across an edge, you’ll use the root
    Spatial as a pivot point to facilitate the
    rotations. It makes the movements a little more complicated, because there’s a need to adjust the body getting away from the parent (as you’ll
    see in the scripting section), but in the end, it will be easier to make the rotations.
    The
    MeshInstance here will be set with a
    CubeMesh. Create a
    SpatialMaterial for the mesh and adjust it to your liking. You can
    look at the company project to see the exact parameters for our example. This mesh must have its size set to (1, 2, 1). This will make a prism
    with a square base and double the height, as proposed in our design. The
    CollisionShape for the body is a
    BoxShape, with half of
    111111111
    22222222that used for its Extents property.
    The Up and Down
    Area nodes are for checking the out of bounds parameters. When any of those areas leave the bounds, it’s game
    over. They should have a
    CollisionShape equal to the floor tiles and be placed slightly above the block. You can position each area in the
    center of each square base, and it will be enough.
    Both
    Timer nodes have their functions explicit in their names. GravityTimer will set the gravity of the body to zero when it finishes. This will
    allow the block to fall naturally at the beginning, but then it’s controlled manually by the controls. Its wait time will be set to 0.5 seconds, but you
    can play around with this value. The RespawnTimer represents the amount of time the game will wait after the fall before respawning the
    block. One second should be enough. Both nodes should be set to One Shot, since we don’t want them to keep being triggered.
    Levels
    The sample game has three levels, and they all inherit from the same base scene. Again, this scene is rooted with a
    Spatial to make use
    of 3D transforms. The structure can be seen in Figure 17.2.
    FIGURE 17.2
    Base level scene structure.
    The
    GridMap will make the floor for the level. This is the main node to design the actual layout of the level. You’ll first need to set the
    Theme property to the
    MeshLibrary we created earlier. When you select this node, the editor will change to show the list of available tiles,
    which will be only one in this case. You can select the tile and use your mouse to place it on the grid. Right-clicking will delete the tile. You can
    also use Ctrl + mouse wheel to change the elevation of the grid and the A, S, and D keys to rotate the tile before placing it.
    Since the player block spawns in a different location each level, the Start node is need. It’s simply a
    Position3D to hold the transform. The
    height of it is constant for every level, and in this sample, you’ll use a value of 4.
    The
    Ending Area node will be placed on the hole that ends the level. Its
    CollisionShape is a
    BoxShape with Extents set to
    (0.45, 2, 0.45). The base is smaller than the grid tile to avoid any edge contact due to numerical imprecision. Areas report the entering signal
    as soon as any part of the body touches them, so this size is wide enough to properly detect the winning condition.
    When the player reaches the end successfully, the gravity should be turned back on, but only after the block completes its rotation, so it can fall
    naturally if it’s outside of the game boundaries. That’s what the
    GravityTimer is for. You can set it to One Shot, but the time itself will be set
    via code to match the block movement duration, so you don’t need to change it.
    The
    Inbounds Area defines the boundaries of the level. When the
    detection areas of the player block get outside the boundaries, the
    game is over. The
    CollisionPolygon makes it easy to draw a line that defines the level border. You’ll need to rotate this negative 90
    degrees in the X-axis so the polygon line matches the level position. You’ll also need to set the depth property to a high value, like 15 or so,
    because the block spawns on a high position, and it should be inside the boundaries.
    Note this is only the base scene, and you don’t need to change the default position of the nodes. You’ll need to create inherited scenes based
    on this one where you’ll fill the GridMap, move the start and ending points, and create the inbounds polygon. Create a few levels and add them
    all to a directory called “levels.” This will ensure they can be easily listed and fetched by scripts.
    TIP
    Grid Snapping
    Since the game features a perfect grid, you can use the Godot Editor features to snap the objects into it. You can enable this by clicking on the
    Transform> Use Snap menu on the toolbar. Use the Transform> Configure Snap dialog to change the Translate option to 0.5. While the
    grid square has side 1, you may need to snap things in the center of squares. This is quite helpful when setting the start and end positions of
    the levels (Figure 17.3).
    FIGURE 17.3
    Grid snap configuration The Translate Snap makes things move perfectly into the grid.
    NOTE
    Pixel-perfect Inbounds Polygon
    While you don’t need to make the polygon fit the boundaries perfectly, it is nice to do so, since the game fits into a grid. The easiest way to do
    this is to simply make the polygon manually by clicking on the corners and using Inspector to change the array of points to the closest integer.
    This way, the polygon will snap into the grid nicely.
    TRY IT YOURSELF
    Create a Level
    Let’s see how to create the first level of the game (see Figure 17.4):
    111111111
    22222222FIGURE 17.4
    The layout of the first level.
    1. Create a new scene inherited from the original level scene.
    2. Click on the
    GridMap node.
    3. Use the floor tile to design the level. You can be creative or just follow the existing example.
    4. Select the
    Start node.
    5. Use the
    Move tool to set it over the level’s spawn point. Use the snapping option to make it easier.
    6. Move the
    Ending node and put it over the hole at the end of the level. Its bounding box should match the square hole.
    7. Select the
    CollisionPolygon under the
    Inbounds node. Use the
    Create Polygon tool to make it go around the
    level’s tiles and enclose them. You can manually adjust the values of the points to make it fit exactly onto the grid.
    8. Save the scene in the “levels” folder. This will make the script load it.
    Main Game Scene
    The last step is to make the main game scene. This will put everything together and handle the environment settings. It will also be responsible
    for making the level’s transitions and setting up the block spawn point. Figure 17.5 shows the main scene structure.
    FIGURE 17.5
    Main scene structure
    First, you have the
    WorldEnvironment. This allows you to set up the environment without relying on the default one. Use it to set up the
    clear color of the world, since the generated sky does not look good with an orthogonal perspective. There’s also a
    Camera, which should
    be set to orthogonal with a Size Yof 10 to capture the entire level.
    The
    DirectionalLight is used to give illumination to the game. It’s also responsible for giving a nice shadow to the block. For that, you’ll
    need to enable its Shadow property. You may need to adjust the Color, Bias, and Contact properties to make the shadow look nicer.
    There is a
    Timer node here to make the game wait a little after the level is won before loading the next one. In our project, we set it to 2
    seconds. It should also be set to One Shot.
    Last, we have an instance of the
    Block scene. For testing purposes, you may also want to instance one of the levels before the level
    loading is set up. After that, you can save this scene and set it as the Main Scene in Project Settings.
    Scripts and Input
    Before adding the scripts, set up the input mapping. This way, you can change the keys used to control the block or add joystick support
    without changing the code.
    The Input Map can be set in the Project Settings. In this game, you only need four: left, right, up, and down. These will be responsible to
    move the block. You can expand on this to add other functionality such as respawn, pausing, and level skip.
    TIP
    Input Configuration
    The Input Map can be altered in runtime with scripting. You can set up and delete actions as well as change the keys and buttons associated
    with it. This means it’s possible to make a menu screen the player can use to set up the keys that move the block.
    You can refer to Hour 7 and check the official Godot documentation about the InputMap singleton if you intend to make something like this
    (Figure 17.6).
    FIGURE 17.6
    The Input Map configuration for the game Notice you can have multiple keys or gamepad buttons triggering the same action.
    Controlling the Block
    The most important and complex script is meant to move the block. It relies on state-keeping variables and a few magic numbers to move the
    block in the correct way. This section will follow each function and explain the most complex portions of the code (Listing 17.1).
    LISTING17.1 Block Input Code (block.gd)
    Click here to view code image
    extends Spatial
    111111111
    22222222…
    enum Direction { UP, DOWN, RIGHT, LEFT }
    enum Orientation { PARALLEL, ORTHOGONAL }
    enum RestPosition { STANDING, LYING }
    var orientation = PARALLEL
    var rest_position = STANDING
    var is_turning = false
    var interpolation = 0
    var rotation_direction = null
    var won = false
    var lost = false
    var respawning = false
    var right_shift = Vector3(0.5, -1, 0)
    var down_shift = Vector3(0, -1, 0.5)
    func _input(event):
    if event.is_action_pressed(“right”):
    start_turning(RIGHT)
    elif event.is_action_pressed(“down”):
    start_turning(DOWN)
    elif event.is_action_pressed(“left”):
    start_turning(LEFT)
    elif event.is_action_pressed(“up”):
    start_turning(UP)
    func start_turning(direction):
    if respawning or won or lost or interp != 0 or not $GravityTimer.is_stopped():
    return
    is_turning = true
    match direction:
    RIGHT:
    rotation_direction = RIGHT
    adjust_transform(right_shift)
    DOWN:
    rotation_direction = DOWN
    adjust_transform(down_shift)
    LEFT:
    rotation_direction = LEFT
    adjust_transform(right_shift Vector3(-1, 1,
    0))
    UP:
    rotation_direction = UP
    adjust_transform(down_shift
    Vector3(0, 1, -1))
    adjust_orientation()

    func adjust_transform(shift):
    translation += $RigidBody.translation + shift
    $RigidBody.translation = -shift
    func adjust_orientation():
    if rest_position == LYING:
    match rotation_direction:
    RIGHT, LEFT:
    if orientation == PARALLEL:
    rest_position = STANDING
    UP, DOWN:
    if orientation == ORTHOGONAL:
    rest_position = STANDING
    elif rest_position == STANDING:
    rest_position = LYING
    match rotation_direction:
    RIGHT, LEFT:
    orientation = PARALLEL
    UP, DOWN:
    orientation = ORTHOGONAL
    func update_shifts():
    if rest_position == STANDING:
    right_shift = Vector3(0.5, -1, 0)
    down_shift = Vector3(0, -1, 0.5)
    else: match orientation:
    PARALLEL:
    right_shift = Vector3(1, -0.5, 0)
    down_shift = Vector3(0, -0.5, 0.5)
    ORTHOGONAL:
    right_shift = Vector3(0.5, -0.5, 0)
    down_shift = Vector3(0, -0.5, 1)

    111111111
    22222222Listing 17.1 shows part of the block script. This part of the code is responsible for handling the input and preparing the block for movement
    while adjusting the state to match the direction of the movement.
    The first lines define three enum structures to be used in the state code. Using these kind of constants ensures they are integers internally
    instead of strings, which requires less processor usage to compare them when needed. The structures we define here are used throughout the
    code to check and update the block state. It’s useful to know which direction the block is oriented and whether it’s standing or not, because its
    asymmetrical nature makes it harder to calculate the rotations without this information.
    Following that, you will set a few variables to keep the state. Most of them aren’t in use currently, but will be very soon. The names are quite
    descriptive, except maybe for interpolation, which keeps the position of the movement animation on a scale of 0 to 1. The
    right_shift and down_shift variables are basically “magic” numbers that help us move the pivot point of the rotation. Think of it like
    this: from the origin (center of the block), how much in each direction do you need to move the pivot point to the edge, where it rotates? To
    move it to the right, you need half a unit in the X-axis and 1 unit in the Y-axis downward (hence the negative). Those values change depending
    on the block orientation, so the update_shifts() function updates the values after each movement.
    The _input() function essentially checks if an action was pressed and passes it around to another function so that you can reuse the code.
    This function is called automatically byGodot when there’s a user input. The start_turning() function sets the state of the object to be
    rotated. This state will then be used on the process function (which we will check later). First, it checks if in a state where it cannot turn, such as
    if it’s respawning or already turning. If so, it bails out of the function early. Then it checks the direction of the turn and calls a function to adjust
    the transform accordingly. After that’s done, it calls another function to update the orientation of the block (it will only reach such orientation after
    the rotation is completed, but you set it early on).
    To adjust the transform, you’ll need to zero out the
    RigidBody translation and shift it according to the direction of movement. For this, you’ll
    move the root node the same amount as the body and add the shift, then set the translation of the body to zero minus the shift you need.
    The orientation is adjusted by checking the current state and direction of movement. With a bit of logic, you can figure out what the next
    orientation will be. Updating the shift vectors is almost the same thing, but you’ll need to reset the magic numbers once again.
    Rotating the Block
    The actual rotation is made in the _physics_process() function (Listing 17.2). It uses the current state of the block to apply the transform.
    The most important detail of this function is the axis of rotation. Godot uses the right-hand rule. This means that if you take your right hand, stick
    your thumb up, and imagine it as the axis of rotation, the closing fingers indicate the direction of rotation (Figure 17.7).
    FIGURE 17.7
    The right-hand rule for rotation Pointing your thumb in the axis direction, the other fingers show the rotation movement.
    LISTING17.2 Block Rotation (block.gd)
    Click here to view code image
    const movement_duration = 0.2;

    func _physics_process(delta):
    update_labels()
    if is_turning:
    var step = (delta / movement_duration)
    var angle = (PI / 2.0) step
    var body = $RigidBody
    match rotation_direction:
    RIGHT:
    body.transform = body.transform.rotated(Vector3(0, 0, -1), angle)
    DOWN:
    body.transform = body.transform.rotated(Vector3(1, 0, 0), angle)
    LEFT:
    body.transform = body.transform.rotated(Vector3(0, 0, 1), angle)
    UP:
    body.transform = body.transform.rotated(Vector3(-1, 0, 0), angle)
    interpolation += step
    if interpolation > 1:
    is_turning = false
    interpolation = 0
    update_shifts()

    The function first calculates the step, which is how much movement will be made this frame, in a ratio value. From this, you can calculate the
    angle considering that by the end, the block will be rotated 90 degrees (which in radians is half of Pi). Then it rotates the transform of the block
    along one of the axes, depending on the direction of movement. At the end, it updates the state to stop the animation once it gets to the end.
    Winning and Losing Conditions
    The rest of the block code deals with win/lose conditions and properly resetting the properties to start a new level (or restart the same one).
    The functions respawn() and reset_properties() are responsible for most of that burden. They update the state to the original values
    and zero the transforms. The
    GravityTimer is used after respawning to disable the gravity once the block is on the floor.
    111111111
    22222222There are two functions that will be triggered by the level areas, namely win() and lose(). They are responsible for setting up the routines
    for winning and losing conditions. The won signal is connected to the game code, which will trigger the loading of the next level.
    Out of Bounds Checking
    When any of the block Area nodes get out of the level boundaries, an area_exited signal is triggered. You’ll use that to call the lose()
    function of the block, which will make it respawn (Listing 17.3).
    LISTING17.3 Out of Bounds Check (outbounds.gd)
    Click here to view code image
    extends Area
    func _on_Inbounds_area_exited( area ):
    var body = area.get_parent()
    var parent = body.get_parent()
    if parent.won or parent.respawning: return
    body.gravity_scale = 1
    parent.lose()
    body.angular_velocity
    = 2
    Note that it gets the grandparent of the detected Area, since that will be our block node. This also increases the angular velocity of the body to
    make it rotate a bit faster and create an interesting effect.
    NOTE
    Script Attachment and Signals
    You should note the outbounds.gd script is attached to the Inbounds node. This also has implications in the signal connection, since it’s
    dependent on the node path. When connecting the signal using the editor, you’ll need to select the correct node where the function is.
    Winning Condition
    The winning condition has a similar logic to out of bounds checking. The difference is the block doesn’t fall if it’s over the ending hole unless it
    is in a standing position. There’s also a timer to reenable the gravity once the movement is completed to make the block fall. When the timer
    runs out, it will call the win() function on the block, which will handle the condition (Listing 17.4).
    LISTING17.4 Winning Check (ending.gd)
    Click here to view code image
    extends Area
    const BlockClass = preload(“res://block.gd”)
    func _on_Ending_body_entered( body ):
    var block = body.get_parent()
    if block.rest_position == BlockClass.STANDING:
    block.won = true
    $GravityTimer.connect(“timeout”, self, “_on_GravityTimer_timeout”, [
    block, body ])
    $GravityTimer.wait_time = block.movement_duration
    $GravityTimer.one_shot = true
    $GravityTimer.start()
    func _on_GravityTimer_timeout(block, body):
    block.win()
    body.gravity_scale = 1
    $GravityTimer.disconnect(“timeout”, self, “_on_GravityTimer_timeout”)
    TIP
    Accessing Constants from Scripts
    You may have noticed the ending script preloads the “block.gd” file. This makes it possible to access the constants of that script. In this
    example, you’ll only use the STANDING constant, so it’s not much of a problem, but in a larger game, this pattern avoids repetition of code. If
    you simply tried to define the constant here, it would work, but it would need maintenance if you needed to change the value. And if you forget
    that it is defined in multiple places, it would lead to hard-to-find bugs.
    Loading Levels
    The logic of the level listing is done by a singleton keeping the state. The game code uses this to load the next available level. This is a regular
    Node, because while it needs to be a singleton in the tree, you don’t need any special functions from any of the provided nodes. You can
    set this up in the AutoLoad tab of Project Settings. You’ll need to set the path to script and the name of the node that it will have when in the
    scene. This name can then be used for reference. If you enable the Singleton field, it will be available directly by its name without the need to
    use the get_node() functionality (Listing 17.5).
    LISTING17.5 Level Keeping Singleton (levels.gd)
    Click here to view code image
    111111111
    22222222extends Node
    signal levels_ended()
    const levels_path = “res://levels”
    var level_list = []
    var current_level
    func _enter_tree():
    var dir = Directory.new()
    dir.open(levels_path)
    dir.list_dir_begin()
    var file = dir.get_next()
    while file != “”:
    if file == “.” or file == “..”:
    file = dir.get_next()
    continue
    level_list.push_back(levels_path.plus_file(file))
    file = dir.get_next()
    level_list.sort()
    current_level = -1
    func get_next_level():
    current_level += 1
    if current_level == level_list.size():
    emit_signal(“levels_ended”)
    return “”
    return level_list[current_level]
    The main logic is on the _enter_tree() function, which will be called when the script is loaded. This will get all the files in the “levels”
    directory (as provided in the levels_path constant) and put their full paths in a list. Then it will sort the list to make sure that “level1” is the
    first level (Listing 17.6). Note this makes the name of the level files important. Finally, this sets the current_level variable to −1, since it will
    be incremented by the game to 0, which is the first index on the list.
    The other function is the get_next_level(), which will increment the level counter and return the file name as stored. If the game is over, it
    will return an empty string, and the caller function will have to handle the result.
    LISTING17.6 Changing Levels (game.gd)
    Click here to view code image
    extends Spatial
    var current_level = null
    func _ready():
    next_level()
    func _on_Block_won():
    $LevelLoad.start()
    func next_level():
    var next_level = Levels.get_next_level()
    if next_level == “”:
    return
    if current_level != null:
    current_level.queue_free()
    current_level = load(next_level).instance()
    add_child(current_level)
    $Block.start_point = str($Block.get_path_to(current_level)) + “/Start”
    $Block.respawn()
    The main game script is the last piece in the puzzle. This will be responsible for loading and instancing the levels into the scene.
    First, note the current_level variable here is different from the one in the singleton. This one holds the actual level instance so that it can
    be easily referenced later. Also note the timeout signal of the
    LevelLoad timer is connected to the next_level() function, and that
    the won signal of the block is connected to the _on_Block_won() function.
    The next_level() function gets the next level path from the singleton. If it’s an empty string, it does nothing, since this means the game is
    out of levels. Otherwise, it frees the current level (if there is one) and loads the next in the same variable. Then it adds the level to the game
    scene and sets the start point of the block. Finally, it tells the block to respawn, which makes it appear in the correct position.
    Playing the Game
    With all that done, the game is finally done and ready to be played! Run the main scene, and you should be able to move the block around with
    the arrow keys on the keyboard. You can roll the block around to see it falling out of the grid or getting into the winning hole (Figure 17.8).
    111111111
    22222222FIGURE 17.8
    The final game running Move around the block and test your puzzle-solving skills.
    Summary
    In this hour, we created a simple 3D puzzle game. You learned how to set up the camera, lighting, and environment. You also learned how to
    create your Mesh Library for use with the GridMap node. You saw how the instanced scenes come together to form a larger game, and how
    scene inheritance can help with that. You learned how to make scripts, and how they can call functions in other scripts and singletons. Finally,
    you saw how to put everything together and form a complete game.
    Q&A
    Q. Why use a GridMap instead of placing instanced scenes?
    A. The
    GridMap node has a great advantage in the editor, making it much easier to edit. It also uses batch instancing for the same tiles,
    resulting in a better overall performance.
    Q. Do you need an interpolation in the processing function? Can’t it be done with animation?
    A. Theoretically, it’s possible to make it via animation, but you’d need multiple animations for each variation, which isn’t very practical. It’s
    also possible to use the
    Tween node to interpolate properties automatically.
    Q. Why connect and disconnect the Timer signal in the ending script?
    A. The connection function binds the block and body objects to be referenced when the time-out ends. Since these binds happen only at the
    connection, it’s needed to disconnect and reconnect later. This is not a real problem in this game, since only one block exists, but this
    would avoid issues in more complex games with multiple objects.
    Workshop
    Take a moment to go through these questions and make sure you have a grasp of the content.
    Quiz
    1. Where can you reconfigure the input keys and buttons?
    2. Why do we use different physics layers?
    3. True or False: You can only connect signals using script.
    4. True or False: You should only attach scripts to the root of the scene.
    Answers
    1. In Project Settings, under the Input Map tab.
    2. To categorize the objects and avoid contact between certain categories.
    3. False. You can also connect using the editor interface.
    4. False. You can have multiple specialized scripts for many nodes in the same scene.
    Exercises
    Let’s change the game a bit to make it more interesting.
    1. Add a
    Label node to the block scene. Update its content to show the number of rotations. Remember to use the built-in function
    str() to convert the number into a string.
    2. Add a few more levels to the game.
    3. Change the order of the levels by renaming their scenes in the “levels” folder.
    4. Add a “game over” screen that shows when all the levels are finished. Use the levels_ended signal of the singleton.
    5. Advanced: The original Bloxorz game has buttons that activate blocks, opening or closing paths. Try to implement this functionality. You
    can make other types of tiles for the buttons and use areas in the levels to detect the presence of the block.
    111111111
    22222222