CanvasItem in Godot – Complete Guide

Welcome to our deep dive into the world of 2D game development using Godot 4, focusing on a crucial aspect – the CanvasItem class. Whether you’re designing intricate UIs or creating the framework for a 2D platformer, understanding CanvasItem is key to mastering Godot’s rendering system. This tutorial is crafted to shed light on the CanvasItem class, offering you the knowledge to harness its full potential in your projects. With engaging examples and a clear structure, we aim to make this concept accessible and implementable for everyone who joins us on this learning adventure.

What is CanvasItem?

CanvasItem stands as the cornerstone of 2D rendering within Godot 4. It’s an abstract class which means it provides a template for other, more specific 2D classes to extend from, such as Control for GUI elements or Node2D for game objects. In essence, anything that occupies a 2D space in a Godot game will be a CanvasItem or inherit from it.

What is CanvasItem Used For?

The CanvasItem class is used as a basis to create and manage graphical elements in a 2D space. Thanks to its versatile nature, developers can use CanvasItem to control how these elements are displayed, handle their transformation relative to the parent, and manage visibility and drawing layers for complex scenes with ease.

Why Should I Learn It?

Learning about CanvasItem is crucial for two main reasons. Firstly, it is at the core of Godot’s 2D system, meaning a thorough understanding will vastly improve your ability to control the visual aspects of your game. Secondly, proficiency with CanvasItem allows for optimized performance, through its smart redraw system, which avoids unnecessary frame-by-frame redrawing. Equip yourself with this knowledge and witness the improvement in both the development process and the final outcome of your Godot 2D projects.

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Creating a Simple 2D Node with CanvasItem

Let’s start by creating a basic 2D node by extending CanvasItem. We will add our new node to a scene and control its position, demonstrating the fundamental capabilities of the CanvasItem class.

extends CanvasItem

func _ready():
    self.position = Vector2(100, 100)
    self.modulate = Color(1, 0, 0)  # Red color

func _draw():
    draw_rect(Rect2(Vector2(), Vector2(50, 50)), Color(1, 1, 1))  # Draw a white square

In the above example, we’ve extended CanvasItem with our new class. In the _ready() function, we position our CanvasItem at coordinates (100, 100) and change its color to red by altering the modulate property. Next, the _draw() function uses draw_rect() to render a white square.

Handling Transformation and Hierarchy

CanvasItem nodes can be transformed within their local coordinate space which influences their position, rotation, and scale. They can also be part of a hierarchy, where the transformation is relative to the parent node.

# Parent node
extends CanvasItem

func _ready():
    self.position = Vector2(200, 200)

# Child node that inherits properties and transformations from the parent
extends CanvasItem

func _ready():
    self.position = Vector2(50, 50)

func _draw():
    draw_circle(Vector2(), 20, Color(0, 0, 1))  # Draw a blue circle

In this example, the child node will appear at position (250, 250) on the screen because it adds its local offset of (50, 50) to the parent’s position of (200, 200). Similarly, any changes in the parent’s transformation will automatically affect the child’s transformation.

Managing Visibility

Another powerful CanvasItem feature is the ability to easily control the visibility of nodes. We can show and hide nodes or check if they are currently visible on the screen.

extends CanvasItem

func _ready():
    self.hide()  # Hide the node

func _process(delta):
    if Input.is_action_just_pressed("ui_accept"):
        if is_visible_in_tree():
            self.hide()
        else:
            self.show()

In the snippet above, we initially hide the node using self.hide(). Then, in the _process() function, we toggle the node’s visibility whenever the “ui_accept” action (usually the Enter key) is pressed.

Drawing with CanvasItem’s Drawing API

Unlock the power of Godot’s Drawing API with CanvasItem. Below are examples of how to draw various shapes, use different colors, and apply custom drawing logic.

extends CanvasItem

func _draw():
    # Draws a rectangle
    draw_rect(Rect2(Vector2(10, 10), Vector2(100, 50)), Color(0.75, 0.65, 0.87))

    # Draws a polygon
    var points = [Vector2(60, 60), Vector2(100, 100), Vector2(60, 140), Vector2(20, 100)]
    draw_polygon(points, [Color(0.36, 0.59, 0.75)])

    # Draws a string of text
    draw_string(load("res://font.tres"), Vector2(200, 150), "Hello, Godot!", Color(1, 1, 1), 64)

The first draw_rect() call creates a purple rectangle. With draw_polygon(), we create a custom shape specified by the vertices in the points array. Finally, draw_string() is used to render text on the screen, which requires a font resource and specifies the text, color, and font size.

These code examples lay the foundation for your journey with the CanvasItem class in Godot 4. Mastering these basics will enable you to create dynamic and interactive 2D scenes. Each example showcases a different aspect of the CanvasItem, from drawing shapes to transforming nodes within a hierarchy, providing a strong starting point for further exploration and development of engaging 2D games and applications.

Continuing our exploration into Godot’s CanvasItem, let’s dive into more advanced features and functionalities through practical code examples.

First, we’ll look at how to use the update() function to request that the _draw() method is called, allowing us to animate our CanvasItem.

extends CanvasItem

var time_passed: float = 0.0

func _draw():
    # Draw a moving circle based on the time_passed variable
    draw_circle(Vector2(200 + sin(time_passed * 2) * 50, 200), 40, Color(1, 0, 0))

func _process(delta):
    time_passed += delta
    update()  # Request a redraw at every frame

In this example, a circle moves back and forth in a sine wave pattern, as time_passed changes over time.

We can also add interaction to our CanvasItems. Here’s how to make a CanvasItem react to mouse clicks:

extends CanvasItem

func _ready():
    set_process_input(true)  # Enables input processing for this node

func _input(event):
    if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed:
        if get_rect().has_point(to_local(event.position)):
            modulate = Color(randf(), randf(), randf())  # Change to a random color

func _draw():
    draw_rect(Rect2(Vector2(), size), modulate)  # Draw a rectangle with the current modulate color

var size = Vector2(100, 50)  # Size of the rectangle

func get_rect() -> Rect2:
    return Rect2(position - size / 2, size)

Now, when we click on our CanvasItem, it changes color to a randomly selected one. This demonstrates how to interact with graphics beyond UI elements in Godot.

Zenva advocates for a detailed understanding of customization, and the Godot API gives us a potent tool in signals. Here’s an example of emitting a custom signal when our CanvasItem is clicked:

extends CanvasItem

signal canvas_item_clicked

func _ready():
    set_process_input(true)  # Enables input processing for this node

func _input(event):
    if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed:
        if get_rect().has_point(to_local(event.position)):
            emit_signal("canvas_item_clicked")  # Emit our custom signal

func _draw():
    draw_rect(Rect2(Vector2(), size), modulate)

This example shows how to emit a custom signal that other nodes or scripts can connect to in order to respond to clicks on this CanvasItem.

Manipulating layers and order can be vital when managing visuals. Here’s how to use the z_index property to control the drawing order of CanvasItems:

extends CanvasItem

func _ready():
    z_index = 1  # Draw this CanvasItem on top of others with a lower z_index

Setting a higher z-index ensures that this CanvasItem will be drawn above others with lower z-indices.

In some cases, we might want to apply a shader to a CanvasItem. Here’s how you can attach a shader and modify its parameters programatically:

extends CanvasItem
var material: ShaderMaterial

func _ready():
    material = ShaderMaterial.new()
    material.shader = load("res://my_shader.shader")
    material.set_shader_param("my_parameter", 2.0)
    self.material = material

func _draw():
    draw_rect(Rect2(Vector2(), size), Color(1, 1, 1))  # Draw a rect to apply the shader to

Through this code snippet, you can dynamically load a shader and assign it to a CanvasItem, further enhancing its appearance or behavior according to specific requirements.

Now you have a more comprehensive set of tools to use with CanvasItem in your Godot projects. Through manipulating visibility, interacting with user inputs, handling drawing order, and utilizing shaders, you can create rich and dynamic 2D scenes that stand out. As always, we at Zenva encourage learners to experiment with these features to get a clearer understanding and begin crafting unique and immersive gaming experiences.

Let’s expand our repertoire with additional CanvasItem capabilities, focusing on examples that are particularly useful for game development with Godot 4.

Implementing drag-and-drop functionality can add interactive elements to a game or UI element. Here’s an example of how you might make a CanvasItem draggable:

extends CanvasItem

var dragging = false

func _ready():
    set_process_input(true)

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == BUTTON_LEFT:
            if get_rect().has_point(to_local(event.position)):
                dragging = event.pressed

func _process(delta):
    if dragging and Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
        global_position = get_global_mouse_position()

func get_rect() -> Rect2:
    return Rect2(position - size / 2, size)

var size = Vector2(100, 50)

In this snippet, we set up a process for dragging the CanvasItem by clicking and holding the left mouse button. We then update the global position of the CanvasItem to follow the mouse position.

Adjusting a CanvasItem’s opacity can be useful for transitions and effect animations. Below is how to modify the alpha value of a CanvasItem to achieve a fade-in effect:

extends CanvasItem

func _ready():
    modulate = Color(1, 1, 1, 0)  # Fully transparent

func _process(delta):
    modulate.a = min(modulate.a + delta, 1)  # Increment alpha until it's fully opaque
    update()

Here we increment the alpha value of the modulate color over time until the CanvasItem becomes fully opaque.

CanvasLayer is another critical class that inherits from CanvasItem, which lets you create layers that can be independently transformed, hide, or show. This is perfect for UI elements. Let’s look at how to create a CanvasLayer and manipulate it:

extends CanvasLayer

func _ready():
    layer = 1  # Assign to layer 1
    offset = Vector2(100, 100)  # Move the entire layer by the offset value

Setting the layer’s properties, you can effectively stack elements, like placing your game’s HUD above the game scene without interference.

We can also respond to the visibility notification to execute code when a CanvasItem becomes visible or hidden. Here’s how to connect to those notifications:

extends CanvasItem

func _notification(what):
    match what:
        NOTIFICATION_VISIBILITY_CHANGED:
            if is_visible_in_tree():
                print("I'm now visible")
            else:
                print("I'm now hidden")

We utilize the _notification() function, which can be overridden to respond to various built-in notifications, like the visibility change notification.

Finally, let’s look at how we can work with multiple CanvasItems in a scene by applying a common behavior. Say we want to reset the modulate color of all CanvasItems when we press a key:

extends CanvasItem

func _process(delta):
    if Input.is_action_just_pressed("ui_cancel"):
        for canvas_item in get_tree().get_nodes_in_group("Colorables"):
            canvas_item.modulate = Color(1, 1, 1)

In the _process() function, we check for a keypress and then loop through all CanvasItems in a specific group named “Colorables”, resetting their color to white.

These additional code examples illustrate more advanced features of CanvasItem in Godot 4, equipping you with a broad and powerful set of techniques for varied gameplay and interaction mechanics. Through drag-and-drop actions, opacity adjustments, layering UI elements, responding to visibility changes, and controlling groups of CanvasItems, you’ll be well on your way to crafting refined and compelling games. Remember, the most effective way to learn these concepts is by applying them in your projects, and we at Zenva encourage you to tinker, test, and iterate as you journey through the multifaceted realm of game development with Godot.

Continue Your Game Development Journey with Zenva

The world of game development is vast and ever-evolving, and your journey doesn’t stop here. To delve deeper and expand your skill set, our Godot Game Development Mini-Degree is the perfect pathway to refine your abilities and to transform your concepts into fully-realized games using the latest Godot 4 engine.

We’ve designed a curriculum that supports you from the basics to more complex game development concepts. The Mini-Degree covers key topics such as mastering the GDScript programming language, controlling gameplay flow, engaging with player and enemy combat mechanisms, and implementing item collection and UI systems. Whether you aim to create an RPG, RTS, survival game, or platformer, with Zenva, you will have the resources to craft games in under two hours.

For an extensive lineup of courses tailored to a variety of skill levels, check out our repertoire of Godot courses. From beginner to professional, there’s always something new to learn and create at Zenva, and with our project-based courses and live coding sessions, you’ll be equipped to take the next step in your game development career. Continue learning, continue building, and make your game development dreams a reality with Zenva.

Conclusion

Embarking on the game development journey with Godot 4 and mastering the CanvasItem class is just the beginning. Every line of code, every new challenge conquered, and every innovative gameplay experience you create pushes the boundaries of your skill and creativity. We at Zenva are here to support and guide you every step of the way. With our Godot Game Development Mini-Degree, the possibilities are endless, and your potential is unlimited. Embrace the challenges ahead and remember, the most remarkable games start with the first line of code. Let’s continue to code, create, and captivate the world together!

Gain confidence in your abilities as a game developer. Through our comprehensive courses, you will not only learn about Godot 4 and its powerful features like CanvasItem but will also be immersed in an engaging, practical learning experience. Level up your game development skills with Zenva and let your creativity flourish. So, what are you waiting for? Your next great game awaits. Let’s get coding!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.