Hour 21. Viewports and Canvas
    What You’ll Learn in This Hour:
    Viewport and its use cases
    Managing your rendering through Canvas Layers
    Mixing 2D and 3D scenes
    Adding a split-screen feature to a game
    In this hour, we start playing with viewports. So far, we’ve talked a lot about how to assemble different nodes types into a Scene Tree that
    represent our world. However, given that we are not building a weather forecast simulation, well want to render things by displaying images on
    the user’s screen. This is what the viewport is about: opening a window into our world to the user.
    While this seems a very generic role, this also means that viewports allow us to implement a wide range of features:
    Create advanced GUI
    Mix multiple scenes into one
    Take screenshots of your game
    Mix 2D and 3D
    Add advanced effects like rearview mirror
    Allow split-screen
    In short, if you’re planning to create more than simple demos, you will most likely need viewports sooner or later, so let’s choose sooner and
    start learning about them.
    Viewports
    If you’ve already read Hour 13 by now, you should have a rough idea what a viewport is and what Godot does with it.
    The viewport is seen as a bridge between our world scene and the low-level parts of Godot like the visual server responsible for rendering a
    scene as a frame.
    Most of the time, the viewport is a shy beast: so far, we’ve hardly encountered it, and all our projects rendered fine. This is because the very
    root node of our scene (the one we get by calling get_node(‘/root’) inGDScript) is itself a
    Viewport automatically created byGodot when
    the game starts (Figure 21.1).
    FIGURE 21.1
    Once our game starts, the root node is always a viewport that’s automatically added.
    Given that the viewport is going to render each frame of our world, we have to tell it where to place itself and what to face before doing the
    rendering. This is done by adding a
    Camera (or
    Camera2D if you’re doing 2D) child node. Note that a viewport can have multiple
    cameras, but only one at a time is configured as current.
    Obviously, if a viewport doesn’t have a camera set, it will stick with the default configuration (remember the blue rectangle that is present by
    default in the 2D editor). This is fine for simple 2D games like the one we did in Hour 5, but quickly falls short when we need more advanced
    features like scrolling. If you do a 3D game, however, a
    Camera is mandatory, or your screen will be black.
    So far, if talking about the camera made sense, viewport seems more like a technical detail of how Godot handles rendering. This is because
    our use cases were fairly simple, so let’s imagine something a bit more complicated.
    For example, we are building a 3D action game, and at some point, the player enters a room with a television screen on. A decade ago, we
    had no choice but to display fixed images on the TV screen due to power limitations. However, things have changed (and Godot has arrived),
    so a much more modern approach is to create a scene somewhere, render it, and display the result on the TV screen (hence, etymologically
    speaking, it’s really a television).
    Translated into Godot, this means we will create a new
    Viewport node, assign it a child scene so it’s played on the TV, and a
    Camera node that acts as a real television camera. Finally, back in our main scene, we will connect the television screen
    MeshInstance
    material’s texture to the
    Viewport’s texture (see Listing 21.1).
    LISTING21.1 Connecting to a Viewport’s Texture
    Click here to view code image
    func host_game(port):
    var viewport = get_node(‘Viewport’)
    # For a 2D Sprite
    var sprite = get_node(‘Sprite’)
    sprite.texture = viewport.get_texture()
    111111111
    22222222# For a 3D MeshInstance
    var mesh = get_node(‘Mesh’)
    mesh.material_override.albedo_texture = viewport.get_texture()
    Of course, in our main scene we also have a
    Camera representing the player view and configuring the root
    Viewport (the one
    created automatically byGodot) that produces the frame displayed on the user’s screen.
    One more thing that’s worth mentioning is that the
    Viewport node has anOwn World property that can be set to either use or not use the
    main scene.
    To illustrate this, let’s continue with our example: instead of a television displaying a show, there’s a security monitor connected to a camera (a
    real one this time) somewhere in the level. To do that, the security camera becomes another view on the world (just like the player has its own).
    The same way the player has a
    Viewport (which happened to be the default root one, but it doesn’t matter) and a
    Camera to render
    the world, we will create a
    Viewport with only a
    Camera child node for the security camera.
    On the contrary, to render a show completely unrelated to our main scene, enabling the Own World property prevents us from mixing the
    scenes together and rendering bad code, especially considering global configuration to a scene like
    WorldEnvironment’s skybox (Figure
    21.2).
    FIGURE 21.2
    Different Scene Trees for the TV show (left) and the security camera. Note that.TelevisionViewport’s Own World property is enabled, unlike the
    SecurityCameraViewport.
    Another great feature of viewport is that it allows us to mix 2D and 3D together! This is really useful every time you want to add a 2D minimap
    in a 3D game, for example.
    You should get the idea now: first we create a
    Viewport node child of our main scene containing all the nodes for our subscene.
    Then we create a surface (a
    Sprite for 2D, a
    MeshInstance in 3D) in our main scene and connect its texture (or its material’s texture
    for a
    MeshInstance) to the
    Viewport’s texture.
    Note that 2D and 3D are rendering separately, so you don’t have to use the
    Viewport’s Own World property to avoid mixing your 2D and
    3D scenes (well, unless your 3D scene inside your 2D main scene uses a viewport on 2D scenes . . . you’re not doing that, right?).
    One last cool thing about viewports: they are not limited to graphics. You can enable 2D or 3D positional audio (using the
    Camera to
    configure the position of your listener) and have your viewport play sound accordingly. You can also choose whether or not the inputs sent by
    the player are passed to a
    Viewport, or you can send arbitrary inputs to it.
    NOTE
    Watch Out With Input Singleton
    When processing inputs, most of the time, we use the Input singleton (for example, doing Input.is_key_pressed(KEY_UP). However,
    as a singleton, this is something that is global to the entire game and thus ignores the viewport’s “Disable Input” property!
    The bottom line is, to use viewport’s “Disable Input,” you should only process your input with a gui_input(event) method (Figure 21.3).
    FIGURE 21.3
    Viewport properties to configure in order to share (or not) with the parent Viewport.
    TRY IT YOURSELF
    Take Screenshots from Within a Game
    1. Take any of the games we already created and start modifying the input handling to trigger a _screenshot() function when the
    enter key is pressed.
    2. In the _screenshot(), we get back the main viewport node, select its texture, and extract its data as a new
    Image.
    3. The texture outputs an upside-down image, so we have to flip its on its y axis.
    4. Finally, we can simply save the
    Image as a file. See Listing 21.2.
    LISTING21.2 Screenshot Function
    Click here to view code image
    func _screenshot():
    var img = get_viewport().get_texture().get_data()
    img.flip_y()
    img.save_png(‘screenshot.png’)
    111111111
    22222222It’s that easy!
    NOTE
    Vflip
    Due to the wayGodot does its rendering, by default, the Viewport’s texture outputs an upside-down image. To solve this, you can enable the
    Vflip property in the Viewport or simply call the flip_y() method on the resulting image.
    Canvas Layers
    Now that we are head-deep into rendering, it’s good to mention another cool tool provided byGodot to make our life easier: the
    CanvasLayer.
    When making a 2D game, the order of drawing elements is really important, given that it determines whether your character stands under or
    over a tree, for instance.
    A simple solution for this trouble is to use the Z property to sort our nodes’ drawing order. This works great at first, but when our scene starts
    growing in complexity, it becomes increasingly harder to make sure no subscenes have a node with the wrong Z value.
    On top of that, this trick doesn’t work at all when dealing with a 3D game where we want to have a GUI.
    This is where the
    CanvasLayer steps in. As its name suggests, it wraps all of its child nodes into a single rendering layer. ThenGodot
    does the rendering of our scene layer by layer, starting with the
    CanvasLayer with the lower value of its Layer property.
    NOTE
    Default CanvasLayer
    Every node that is not child of a
    CanvasLayer is considered byGodot to be part of the default layer, which has a value of 0. So, when
    adding a
    CanvasLayer, remember to set its Layer property to a negative value if it’s in the background or a positive one for a foreground
    layer.
    A very common use case of
    CanvasLayer is with 2D platforms to create the separation between fixed background, parallax scrolling
    background, middle-ground (where the player interacts with the world), and foreground for the GUI. In fact, Godot even provides a special layer
    node called
    ParallaxBackground to simplify the implementation
    o
    f parallax scrolling.
    TRY IT YOURSELF
    Play with Parallax Scrolling
    1. Create a simple 2D scene with a fixed background and a player-controlled ship. To make things easier, you can use the assets
    from Hour 5’s game.
    2. Add a child
    Camera2D node to the ship and disable its Drag Margin H&V properties to make it follow the ship’s
    movements exactly.
    3. Now add
    ParallaxBackground to the scene. Make sure it displays between the background and the player (a good idea
    would be to use some
    CanvasLayer for the background and player, and remember that
    ParallaxBackground is
    already a
    CanvasLayer).
    4. Configure the
    ParallaxBackground’s base scale property (0 means no parallax scrolling on this axis). See Figure 21.4.
    FIGURE 21.4
    Configuring ParallaxBackground.
    5. Add one or more
    ParallaxLayer child nodes to the
    ParallaxBackground, each one containing multiple Sprites of
    asteroids. You can customize the Scale property to specify how fast each one will move on each axis. See Figure 21.5.
    FIGURE 21.5
    The Scale property specifies how fast each one will move on each axis.
    6. Now play the scene and observe how the different
    ParallaxLayer nodes move compared to the player. See Figure 21.6.
    111111111
    22222222FIGURE 21.6
    Mesh and light.
    NOTE
    Keep the Number of Layers Low
    Keep in mind that adding
    CanvasLayer has a cost given there’s less freedom for the Godot renderer to process our nodes in batches. A
    good rule of thumb would be to only use a couple of
    CanvasLayer in your main scene (typically world rendering, in-game GUI, and a
    paused menu for a 3D game) and keep the Z axis ordering in the subscenes.
    Split-Screen
    One really cool feature in
    Viewport is the split-screen: we can divide our screen into two (or more) parts, each one usually controlled by a
    different player.
    From what we’ve learned so far, we can implement this by dividing our screen into multiple
    Sprite nodes, each connected to a
    Viewport with a camera node.
    If we think about it, this is not a really elegant solution: it would be much better to have a
    Control node instead of this 2D
    Sprite to
    have useful features like anchor or margin. Besides, we have to do the texture connection with a bit of code, which adds to the complexity.
    The solution to these concerns, of course, lies in a new type of node provided byGodot: the
    ViewportContainer node. Think of it as a
    Control node that connects directly with its first child
    Viewport node.
    TRY IT YOURSELF
    Add Split-Screen to a Scene
    1. Create a simple 3D scene. Typically, it’s a mesh (don’t forget to add a material to it) and a light. See Figure 21.6.
    2. Create another scene with a regular
    Node as its root and our simple 3D scene as children.
    3. Now add multiple ViewportContainers, configure their size to divide the screen between them, and enable their Stretch
    properties.
    4. For each ViewportContainer, add a Viewport and Camera. Move the cameras to show different angles of the scene. See Figure
    21.7.
    FIGURE 21.7
    Cameras show various angles.
    5. Finally, play your scene. As expected, we have multiple views of the scene. See Figure 21.8.
    FIGURE 21.8
    Multiple views of the scene.
    Summary
    In this hour, we saw how useful
    Viewport is when dealing with advanced subjects like nested scenes, multiple views on the same scene,
    or mixing 2D and 3D scenes together. On top of that, we saw how useful
    CanvasLayer is for ordering the drawing of a complex scene
    and how to combine
    ParallaxBackground and
    ParallaxLayer nodes to create parallax scrolling efficiently. Finally, we learned how
    to add the very common split-screen feature to a game.
    Q&A
    Q. My 3D scene is not rendered by my viewport!
    A. Make sure you didn’t forget to add some illumination to your scene. Also, check how you do your connection between the
    Viewport
    texture and the surface using it. Finally, make sure you configured the Size property of the
    Viewport.
    Q. My viewport produces an upside-down image.
    A. You forgot to check the Vflip property of your
    Viewport. Also, if it is connected to a
    MeshInstance, it’s possible its
    111111111
    22222222Transformation property is the cause.
    Q. My Parallax background is not moving.
    A. Parallax moves relative to the camera, so make sure it is moving (and not just your character). Check if the
    ParallaxBackground’s
    Base Scale or
    ParallaxLayer’s Scale properties are not set to 0 on one of their axes.
    Workshop
    See if you can answer the following questions to test your knowledge.
    Quiz
    1. What are the differences between the
    Viewport and the
    Camera nodes?
    2. How do you configure the
    Viewport to render the frames displayed on the user’s screen?
    3. In which layer are the nodes rendered that don’t belong to a
    CanvasLayer?
    4. Why is it useless to give a wrap to a
    ParallaxBackground node a
    CanvasLayer parent node?
    Answers
    1. The
    Camera node configures the
    Viewport; the
    Viewport does the actual rendering of the scene.
    2. It’s always the root
    Viewport (aka /root node), automatically created byGodot.
    3. They will render in the default 0 layer. Keep in mind that layers are rendered from lower to greater value.
    4.
    ParallaxBackground is already a
    CanvasLayer by itself.
    Exercises
    1. Try to add a split-screen feature to the game we created in Hour 17 to provide a side and top view.
    2. Merge multiple games into a single one by using the
    ViewportContainer and configuring each
    Viewport to manage its own
    world.
    3. Now go inception-style by controlling a game from within another game:
    Create a 3D scene with an arcade game machine (basically a big rectangle
    MeshInstance with another plane and
    MeshInstance representing the screen) and a player (another rectangle
    MeshInstance) with a camera.
    Attach a
    Viewport playing the game from Hour 5 to the arcade machine screen.
    If the game-within-the-game originally uses the Input singleton, convert the input processing to use a _gui_input() method
    (typically by creating you own custom Input class this is updated each frame by _gui_input()).
    Now inject input to the game-within-the-game
    Viewport from script. The simplest way to do this is to provide some
    Button nodes. Another more advanced way is allowing the player
    MeshInstance to move (or control its view and send a
    RayCast there) and detecting its collision with other
    MeshInstance representing the arcade machine’s buttons.
    111111111
    22222222