DEV Community

christine
christine

Posted on • Originally published at christinec-dev.Medium on

The Book of Nodes: 2D

Book of Nodes

Below you can find a list of 2D nodes that can be used in Godot 4. This is part of my Book of Nodes series. If you want to see similar content on 3D or UI nodes, please refer to the parent page of this post for those links. 😊

Before we begin, if you need a base project to test these code snippets, feel free to download my FREE 2D and 3D templates here. I’ll be using these templates throughout this post.

*Please note that this list is not 100% complete yet, but I will be updating this list as time goes on.


  • AnimatedSprite2D
  • AnimationPlayer
  • AnimationTree
  • Area2D
  • AudioStreamPlayer2D
  • Camera2D
  • CharacterBody2D
  • CollisionShape2D
  • DirectionalLight2D
  • LightOccluder2D
  • MeshInstance2D
  • NavigationAgent2D, NavigationObstacle2D, NavigationRegion2D
  • Node2D
  • Path2D, PathFollow2D
  • PointLight2D
  • RayCast2D
  • RigidBody2D
  • Sprite2D
  • StaticBody2D
  • TileMap
  • Timer

Node2D

The Node2Dnode is the fundamental building block for all 2D scenes in Godot. It provides us with basic 2D spatial features like position, rotation, and scale. Almost all 2D nodes (like Sprite2D, Area2D, etc.) inherit from Node2D, which makes it the most essential node for any 2D game development.

Book of Nodes

Mechanic:

Move a group of 2D nodes collectively.

Implementation:

  • Add a Node2D to your scene to serve as a parent node. This could represent a game object like a vehicle or a character.

Book of Nodes

  • Add child nodes such as twoSprite2Dnodes. These children can represent visual components, collision areas, etc.

Book of Nodes

  • In the Inspector, assign a texture (image file) of your choosing to the texture property of the Sprite2D node. This image will be what is displayed in the scene.

Book of Nodes

  • Now if we manipulate the Node2D parent, it will affect all its children. For example, moving the Node2D will move all its children, whilst maintaining their relative positions and transformations.

Book of Nodes

  • We can also do this via code — say if we press SPACE on our keyboard, the node moves -100 pixels on the x-axis.
### Main.gd

extends Node2D

@onready var node_2d = $Node2D

func _process(delta):
 if Input.is_action_pressed("ui_select"):
  node_2d.position.x -= 100 * delta
Enter fullscreen mode Exit fullscreen mode

Book of Nodes

Sprite2D

The Sprite2Dnode in Godot is used to display 2D images in your scenes. Since it inherits from the Node2Dnode, it can handle various transformations like scaling, rotation, and translation. It’s one of the most commonly used nodes for representing characters, objects, and other visual elements in a 2D space.

Book of Nodes

You can use either a singular image as the texture, or a tilesheet.

Book of Nodes

If you are using a Tilesheet(as I am in this example) as your texture, you will have to crop out your sprite using the HFramesand VFramesproperty in the Inspector Panel. Then, add a key at each frame. The Frame Coords x property determines which column you are trying to access, and the Frame Coords y property determines which row you are trying to access.

Book of Nodes

Mechanic:

Display and animate a character.

Implementation:

  • Create a Sprite2D node in your scene.

Book of Nodes

  • In the Inspector, assign a texture (image file) to the texture property of the Sprite2D node. This image will be what is displayed in the scene.

Book of Nodes

  • Adjust the position, scale, and rotation properties to position the sprite correctly within your game world.

Book of Nodes

  • If you want to animate the sprite, you can use an AnimationPlayer to animate properties like position, rotation_degrees, and scale. You can also swap out the texture of the Sprite2D if you have a sprite that has multiple animations, for example walking, idling, etc.

Book of Nodes

Book of Nodes

Book of Nodes

Book of Nodes

  • Add the code to play the animation on load.
### Main.gd

extends Node2D

@onready var animation_player = $AnimationPlayer

func _ready():
 animation_player.play("move_character")
Enter fullscreen mode Exit fullscreen mode
  • Run your project and your Sprite2D node should be in your scene. If you added an animation, it should play.

Book of Nodes

AnimatedSprite2D

The AnimatedSprite2D node utilizes a series of images (sprites) and displays them in a sequence to create an animation. Unlike a simple Sprite2D node that displays a single static image, AnimatedSprite2D can cycle through multiple frames to animate characters, objects, or effects within your 2D game. To create these frames, we can use either sprites or a spritesheet.

Book of Nodes

The AnimatedSprite2D node utilized the SpriteFrames Resource to create animations. This is a special resource in Godot that holds collections of images. Each collection can be configured as an animation by specifying the images (frames) that belong to it. You can create multiple animations within a single SpriteFrames resource, each with its own set of frames and playback properties like speed and loop settings.

Book of Nodes

Book of Nodes

Mechanic:

Create a character with walking animations.

Implementation:

  • Create an AnimatedSprite2D node in your scene.

Book of Nodes

  • Assign a SpriteFrames resource to the AnimatedSprite2D.

Book of Nodes

Book of Nodes

  • Add a new animation by clicking on the page+ icon.

Book of Nodes

  • Rename this animation by double-clicking on it.

Book of Nodes

  • Either drag in sprites into the frames box or click the spritesheet icon to add animations via an atlas.

Book of Nodes

  • Crop out the frames horizontally and vertically.

Book of Nodes

  • Select the frames you want. For instance, in my person atlas, I will choose frames 0–6 in row 5.

Book of Nodes

  • Then play the animation to see if you need to alter the FPS to make the character move faster/slower:

Book of Nodes

  • Repeat the process for all of your animations, for example walk_left, walk_up, walk_down, walk_up, idle_x, run_x, etc.

Book of Nodes

  • Play the animation in your code so that when your player moves during runtime the animation can play:
### Player.gd

extends CharacterBody2D

# Scene-Tree Node references
@onready var animated_sprite = $AnimatedSprite2D

# Variables
@export var speed = 100

# Input for movement
func get_input():
 var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
 velocity = input_direction * speed

# Movement & Animation
func _physics_process(delta):
 get_input()
 move_and_slide()
 update_animation()

# Animation
func update_animation():
 if velocity == Vector2.ZERO:
  animated_sprite.play("idle")
 else:
  if abs(velocity.x) > abs(velocity.y):
   if velocity.x > 0:
    animated_sprite.play("walk_right")
   else:
    animated_sprite.play("walk_left")
  else:
   if velocity.y > 0:
    animated_sprite.play("walk_down")  
   else:
    animated_sprite.play("walk_up")
Enter fullscreen mode Exit fullscreen mode
  • Run your project and move around:

Book of Nodes

AnimationPlayer

Unlike the AnimatedSprite2D which is specifically designed for sprite animations, the AnimationPlayer can animate virtually ANY node within a Godot scene. Instead of animating a simple sprite, you can animate the node’s properties — including but not limited to positions, rotations, scales, colors, and even variables.

The AnimationPlayer can hold a set of animations on a singular timeline, each containing keyframes that define the start and end points of any property that changes over time. You can create complex sequences and control animations in a non-linear fashion.

This node can be used to animate 2D, 3D, and even UI nodes!

Book of Nodes

Book of Nodes

Mechanic:

Animate a Sprite2D node of a potion that pulses in size to capture player's attention.

Implementation:

  • Add a Sprite2D node and an AnimationPlayer node to your scene. The Sprite2D node is the node we want to animate, and the property we want to animate of this node is its scale.

Book of Nodes

  • Assign a sprite to the Sprite2D node. I’ll assign a potion.

Book of Nodes

  • Select the AnimationPlayer node.
  • In the animation panel, click “New Animation” and name it something descriptive like “pulse”.

Book of Nodes

Book of Nodes

  • Set the animation length to the duration you want for one pulse cycle (e.g., 1 second).
  • Enable the “Loop” option to make the animation repeat continuously.

Book of Nodes

  • Go to the beginning of the animation timeline (0 seconds).
  • Select the Sprite2D node, and in the Inspector, set the scale property to its initial value (e.g., Vector2(1, 1)).
  • Right-click the scale property in the Inspector and select "Key" to add a keyframe.
  • Move to the middle of the timeline (e.g., 0.5 seconds), change the scale to a larger value (e.g., Vector2(1.2, 1.2)), and add another keyframe.
  • At the end of the timeline (1 second), set the scale back to the initial value (Vector2(1, 1)) and add a final keyframe.

Book of Nodes

Book of Nodes

Book of Nodes

Book of Nodes

  • You can control when the animation starts or stops via script, or let it run continuously since it’s set to loop.
### Main.gd

@onready var animation_player = $AnimationPlayer

func _ready():
    animation_player.play("pulse")
Enter fullscreen mode Exit fullscreen mode
  • Start your project and observe the potion sprite pulsing in size.

Book of Nodes

MeshInstance2D

The MeshInstance2D node is used for displaying a Meshin a 2D space. In Godot, a mesh is used as a resource that can be applied to MeshInstance nodes to render geometry in a scene.

Book of Nodes

It can be particularly useful for achieving effects or visual styles that are difficult with standard 2D sprites or animations, such as deformations or complex shading that reacts to lighting conditions.

Mechanic:

Dynamically deform a 2D mesh (Sprite2D conversion).

Implementation:

  • Create a Sprite2D node in your scene. Assign it with a texture of your choice.

Book of Nodes

  • Use the editor’s conversion tool to convert it to MeshInstance2D node.

Book of Nodes

  • A dialog will appear, showing a preview of how the 2D mesh will be created. The yellow lines are the mesh polygons — and this will make up your 2D mesh shape. The default values are fine.

Book of Nodes

  • The Sprite2Dnode should be converted into a MeshInstance2Dnode.

Book of Nodes

  • Use scripts to deform the mesh dynamically.
### Main.gd

extends Node2D

@onready var sprite_2d = $Sprite2D

func _process(delta):
    var scale_factor = sin(Time.get_ticks_msec() / 1000.0) * 0.1 + 1.0
    sprite_2d.scale = Vector2(scale_factor, scale_factor)
Enter fullscreen mode Exit fullscreen mode
  • Run the scene to observe the 2D mesh dynamically deform itself!

Book of Nodes

AnimationTree

The AnimationTree node enhances the capabilities of the AnimationPlayer by providing advanced features for animations, such as blending, transitions, and states. This makes it extremely easy to make detailed character animations and interactive scene elements in 2D and 3D environments.

We usually use blending to create smooth transitions between animations, for example, smoothly transitioning between walking and running depending on the player’s speed.

We use state machines to switch our animations dynamically depending on the conditions, for example, switching between idle and attack animations if the player presses a key.

Mechanic:

Animate a 2D character with multiple actions (e.g., walking, idle).

Implementation:

  • Add an AnimationPlayernode to your scene and create animations for it like "walk_x”, "idle". You’ll need to create these animations from a Sprite2Dnode, or else it won’t work.

Book of Nodes

Book of Nodes

  • Now, add an AnimationTree node, linking it to the AnimationPlayer.

Book of Nodes

  • Configure the tree_root as an AnimationNodeStateMachine for managing states.

Book of Nodes

  • Also assign the AnimationPlayeras the Anim Player property because this is where our AnimationTree will call the animations from, and the Advanced Expression as the root node because this is where our animation coding can be found (our script).

Book of Nodes

  • If you open your AnimationTree, you will see if you right-click you can add animations, blend trees, and state machines. Add your ‘idle’ animation and a BlendSpace2Dso that we can play our walk animations depending on our player's Vector2() coordinates. Rename the BlendSpace2D to ‘walk’.

Book of Nodes

Book of Nodes

  • Add a transition between start -> idle. The transition type should be immediatebecause we want the animation to play immediately.

Book of Nodes

  • Click the pencil icon to edit your walk BlendSpace2D. Then add a point with an animation at each coordinate. Up (0, -1). Down (0, 1). Left (-1, 0). Right (1, 0). Idle (0,0). Also change the blend mode to “Discrete”.

Book of Nodes

Book of Nodes

  • Add transitions between idle -> walk and vice versa. The transition type for both should be syncbecause we want to blend the animation. Also set the mode to enabledbecause we will activate this animation via the code, and not automatically.

Book of Nodes

Book of Nodes

  • Now in our code, we can play our animations based on our input.
### Player.gd

extends CharacterBody2D

# Scene-Tree Node references
@onready var animation_tree = $AnimationTree
@onready var animation_state = animation_tree.get("parameters/playback")

# Variables
@export var speed = 100

# Input for movement
func get_input():
 var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
 velocity = input_direction * speed

# Movement & Animation
func _physics_process(delta):
 get_input()
 move_and_slide()
 update_animation()

# Animation
func update_animation():
 var blend_position = Vector2.ZERO
 if velocity == Vector2.ZERO:
  animation_state.travel("idle")
 else:
  blend_position = velocity.normalized()
  animation_state.travel("walk")

 animation_tree.set("parameters/walk/blend_position", blend_position)
Enter fullscreen mode Exit fullscreen mode
  • Run the scene and control the character to observe the transitions and movement based on our state.

Book of Nodes

CollisionShape2D

The CollisionShape2D node allows you to specify the boundaries of an object for collision detection, which is essential for handling interactions between objects in your game.

Book of Nodes

Mechanic:

Add a collision area to block the character from passing.

Implementation:

  • Create a CollisionShape2D node as a child of a CharacterBody2D, RigidBody2D, orStaticBody2D. These nodes will block other collisions. To have a node pass through collisions, use an Area2D.

Book of Nodes

Book of Nodes

  • In the Inspector, assign a Shape2D resource to the shape property of the CollisionShape2D. The shape you choose will depend on the shape of your entity. For example, a player might have a capsule shape, a pickup a circle, an area a box.

Book of Nodes

  • Let’s enable debugging to see our collisions in action.

Book of Nodes

  • Run your scene to see how your player interacts with the collision shape. Since we used a StaticBody2D node, they should be blocked and unallowed to go through the collision.

Book of Nodes

CharacterBody2D

The CharacterBody2D node is a specialized class for physics bodies that are meant to be controlled or moved around by the user. Unlike other physics bodies such as the RigidBody2Dor StaticBody2Dnode, CharacterBody2Dis not affected by the engine’s physics properties like gravity or friction by default. Instead, you have to write code to control its behavior, giving you precise control over how it moves and reacts to collisions.

Book of Nodes

Mechanic:

Move a character with arrow keys, including handling gravity and jumping.

Implementation:

  • Add a CharacterBody2D node to your scene.

Book of Nodes

  • You’ll see it has a warning icon next to it. This is because it needs a collision shape to be able to interact with the world. Add a CollisionShape2D as a child of the CharacterBody2D and set its shape to match your character.

Book of Nodes

  • Add a Sprite2D node to this scene so that we can see our character.

Book of Nodes

  • Attach a script to the CharacterBody2D to handle movement and jumping.
### Player.gd

extends CharacterBody2D

# Variables
@export var speed = 200
@export var jump_force = -400
@export var gravity = 800

# Input for movement
func get_input():
 velocity.x = 0
 if Input.is_action_pressed("ui_right"):
  velocity.x += speed
 if Input.is_action_pressed("ui_left"):
  velocity.x -= speed
 if Input.is_action_pressed("ui_down"):
  velocity.y -= jump_force
 if is_on_floor() and Input.is_action_just_pressed("ui_up"):
  velocity.y = jump_force

# Movement & Gravity
func _physics_process(delta):
 get_input()
 velocity.y += gravity * delta
 move_and_slide()
Enter fullscreen mode Exit fullscreen mode
  • Run the scene and use the arrow keys to move the character and make it jump.

Book of Nodes

StaticBody2D

The StaticBody2D node is used to represent objects that do not move. This node is ideal for creating static elements in your game, such as walls, floors, and other immovable objects such as chests.

Book of Nodes

Mechanic:

Create an obstacle.

Implementation:

  • Create a StaticBody2D node in your scene. Add a CollisionShape2Das a child of the StaticBody and set its shape to match the obstacle.

Book of Nodes

  • Give it a Sprite2D of your choice so that we can see the item.

Book of Nodes

  • Run your scene to see how your player interacts with the collision shape. They should be blocked and unallowed to go through the obstacle.

Book of Nodes

RigidBody2D

The RigidBody2D node is used for objects that are affected by the engine’s physics. These bodies can move, rotate, and respond to forces and collisions. They are ideal for creating dynamic objects that need realistic physics interactions, such as balls, bullets, moveable obstacles, etc.

Book of Nodes

Mechanic:

Create a moveable obstacle.

Implementation:

  • Create a RigidBody2D node in your scene. Add a CollisionShape2Das a child of the RigidBody and set its shape to match the obstacle.

Book of Nodes

  • Give it a Sprite2D of your choice so that we can see the item.

Book of Nodes

  • Since we want to move this item when our player collides with it, we should disable its gravity so that it doesn’t get pulled downwards (unless you are making a 2D platformer).

Book of Nodes

  • Use GDScript to apply forces or impulses to the rigid body if the player pushes it.
### Player.gd

extends CharacterBody2D

# Scene-Tree Node references
@onready var animation_tree = $AnimationTree
@onready var animation_state = animation_tree.get("parameters/playback")

# Variables
@export var speed = 100
@export var push_force = 80.0

# Input for movement
func get_input():
 var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
 velocity = input_direction * speed

# Movement & Animation
func _physics_process(delta):
 get_input()
 move_and_slide()
 update_animation()
 handle_collisions()

# Animation
func update_animation():
 var blend_position = Vector2.ZERO
 if velocity == Vector2.ZERO:
  animation_state.travel("idle")
 else:
  blend_position = velocity.normalized()
  animation_state.travel("walk")

 animation_tree.set("parameters/walk/blend_position", blend_position)

# Handle Collisions
func handle_collisions():
 for i in range(get_slide_collision_count()):
  var collision = get_slide_collision(i)
  if collision.get_collider() is RigidBody2D:
   var collider = collision.get_collider() as RigidBody2D
   var impulse = -collision.get_normal() * push_force
   collider.apply_central_impulse(impulse)
Enter fullscreen mode Exit fullscreen mode
  • Run the scene and observe how the obstacle moves when the player pushes against it.

Book of Nodes

Area2D

The Area2D node is used to detect when objects enter or exit a defined area. They do not represent physical bodies but are useful for triggering events such as cutscenes or map transitions, detecting overlaps, and creating zones for things such as enemy or loot spawning.

Book of Nodes

We can use the Area2D node’s on_body_entered() and on_body_exited() signals to determine whether or not a PhysicsBody has entered this zone.

Book of Nodes

Mechanic:

Create a trigger zone that detects when the player enters a specific area.

Implementation:

  • Create an Area2D node in your scene. You also need to add a CollisionShape2D as a child of the Area and set its shape to define the trigger zone. Adjust the collision shape's properties to fit the dimensions of your trigger zone.

Book of Nodes

Book of Nodes

  • Attach the Area2D node’s on_body_entered() and on_body_exited() signals to your script.

Book of Nodes

Book of Nodes

  • Use GDScript to notify us when the Player enters or exits the area.
### Main.gd

extends Node2D

func _on_area_2d_body_entered(body):
 if body.name == "Player":
  print("The player has entered the area!")

func _on_area_2d_body_exited(body):
 if body.name == "Player":
  print("The player has exited the area!")
Enter fullscreen mode Exit fullscreen mode
  • Enable debugging so we can see when our Player enters/exits our area.

Book of Nodes

  • Run the scene and observe how the area detects when the Player enters or exits the defined zone. Each time the player enters/exits the zone, the game should be notified.

Book of Nodes

Book of Nodes

RayCast2D

The RayCast2D node is used to cast a ray in a 2D space to detect objects along its path. This is useful for various purposes such as line-of-sight checks, shooting and attacking mechanics, and collision detection.

It can collide with bodies such as StaticBody2D (useful for detecting loot and quest items), CharacterBody2D (useful for detecting interactions with enemies and NPCs), and RigidBody2D (useful for detecting interactions with moveable objects. It can also collide with areas , such as Area2D (useful for interactions with trigger zones).

Book of Nodes

Mechanic:

Cast a ray from the player and detect what it hits.

Implementation:

  • Add a RayCast2D node to the Player in your scene.

Book of Nodes

  • Set the target_position property to define the direction and length of the ray. I usually leave mine at its default values. You can also enable its collision with areas.

Book of Nodes

  • In the code, let’s update our raycast to always face the player’s last direction. We will also print the colliders it’s hitting.
### Player.gd

extends CharacterBody2D

# Scene-Tree Node references
@onready var animation_tree = $AnimationTree
@onready var animation_state = animation_tree.get("parameters/playback")

# Variables
@export var speed = 100
var direction = Vector2()
@onready var ray_cast_2d = $RayCast2D

# Input for movement
func get_input():
 var input_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
 direction = input_direction
 velocity = input_direction * speed

# Raycast hit detection
func _process(delta):
 if ray_cast_2d.is_colliding():
  var collider = ray_cast_2d.get_collider()
  print("Raycast hit: ", collider.name)

# Movement & Animation
func _physics_process(delta):
 get_input()
 move_and_slide()
 update_animation()

 # Update raycast to face player direction
 if direction != Vector2.ZERO:
  ray_cast_2d.target_position = direction.normalized() * 50

# Animation
func update_animation():
 var blend_position = Vector2.ZERO
 if velocity == Vector2.ZERO:
  animation_state.travel("idle")
 else:
  blend_position = velocity.normalized()
  animation_state.travel("walk")

 animation_tree.set("parameters/walk/blend_position", blend_position)
Enter fullscreen mode Exit fullscreen mode
  • Run your scene and interact with objects that have colliders. The raycast should detect the objects and notify the game.

Book of Nodes

Book of Nodes

Camera2D

The Camera2D node is used to control the view of a 2D scene. It allows the screen to follow the player or other objects. Only one Camera can be active per viewport, and it registers itself in the nearest Viewport node.

In 2D games, we usually attach the Camera2D node to either the Player or our World scene. Attach it to the Player if you want to follow the player around. Attach the Camera to your World (Main) scene if you want a birds-eye view of the environment. This usually requires a bit more configuration and coding, as you have to make the camera able to move, rotate, scroll, or zoom around the scene.

Book of Nodes

Mechanic:

Create a “God Mode” 2D camera that can move, zoom, and rotate based on user input.

Implementation:

  • Add a Camera2D node to your Main (World) scene. Make sure this camera is enabled, and all other cameras are disabled.

Book of Nodes

  • Add the inputs to zoom and rotate your camera. The default up, down, left, and right inputs should be fine to move the camera.

Book of Nodes

Book of Nodes

  • Use GDScript to handle the camera’s zoom, movement, and rotation. You can do this in a custom Camera.gd script (preferred), or directly in your root script.
### Main.gd

extends Node2D

@onready var camera_2d = $Camera2D
@export var zoom_speed = 0.5
@export var move_speed = 200
@export var rotate_speed = 0.5
@export var min_zoom = 0.5
@export var max_zoom = 2.0

func _process(delta):
 handle_zoom(delta)
 handle_movement(delta)
 handle_rotation(delta)

# Zooming
func handle_zoom(delta):
 if Input.is_action_pressed("zoom_out"):
  camera_2d.zoom -= Vector2(zoom_speed, zoom_speed) * delta
 if Input.is_action_pressed("zoom_in"):
  camera_2d.zoom += Vector2(zoom_speed, zoom_speed) * delta
 camera_2d.zoom.x = clamp(camera_2d.zoom.x, min_zoom, max_zoom)
 camera_2d.zoom.y = clamp(camera_2d.zoom.y, min_zoom, max_zoom)

# Moving
func handle_movement(delta):
 var direction = Vector2.ZERO
 if Input.is_action_pressed("ui_right"):
  direction.x += 1
 if Input.is_action_pressed("ui_left"):
  direction.x -= 1
 if Input.is_action_pressed("ui_down"):
  direction.y += 1
 if Input.is_action_pressed("ui_up"):
  direction.y -= 1
 camera_2d.position += direction.normalized() * move_speed * delta

# Rotating
func handle_rotation(delta):
 if Input.is_action_pressed("rotate_left"):
  camera_2d.rotation -= rotate_speed * delta
 if Input.is_action_pressed("rotate_right"):
  camera_2d.rotation += rotate_speed * delta
Enter fullscreen mode Exit fullscreen mode
  • Run the scene and use the defined input actions to move, zoom, and rotate the camera.

Book of Nodes

DirectionalLight2D

The DirectionalLight2D node is used to simulate sunlight or moonlight. It emits light in a specific direction, affecting all objects in the scene equally, regardless of their distance from the light source. This type of light is useful for outdoor scenes where you need consistent lighting across the entire scene.

This node’s two main properties are the energy and color properties. The energy property determines how bright/dim the light is, and the color is the shading of the light.

Book of Nodes

By default, I prefer NOT to use this node without shaders because its features are a bit unfinished (and the shadows are not good). If you’d like an introductory tutorial on how to use this node with shaders ( by making a day and night cycle), please check out my YouTube video.

Mechanic:

Illuminate a scene with sunlight.

Implementation:

  1. Create a DirectionalLight2D node in your scene.

Book of Nodes

  • As you can see, this is crazy bright. Let’s adjust our properties like energy andcolorto customize the light's appearance and behavior.

Book of Nodes

Book of Nodes

  • Now let’s do something fun. I recommend using shaders with this node, but just for testing sake, let’s have it randomize its color each second.
  • Add a Timer node to your scene. In the Inspector Panel, enable autostart, and connect its timeout signal to your script

Book of Nodes

Book of Nodes

Book of Nodes

  • In your code, let’s randomize our light’s color every time the timer times out (every second).
### Main.gd

extends Node2D

@onready var directional_light_2d = $DirectionalLight2D

func _on_timer_timeout():
 directional_light_2d.color = Color(randf(), randf(), randf()
Enter fullscreen mode Exit fullscreen mode
  • Run your scene and enjoy the overstimulation.

Book of Nodes

PointLight2D

The PointLight2D node emits light in all directions from a single point. This type of light is useful for creating focused lighting effects, such as flashlights, lamps, or fires.

This node’s two main properties are the energy , color, texture scale, and textureproperties. The energy property determines how bright/dim the light is. The color is the shading of the light. The textureproperty allows us to give our light a shape. The texture scale property determines the size of our light.

Book of Nodes

Mechanic:

Create a flickering torch.

Implementation:

  • In your scene, add a PointLight2D node. We’ll need to give it a shape, so download this texture, and drag it into your texture property.

Book of Nodes

  • Play around with the energy, color, andtexture scalevalues.

Book of Nodes

  • Just for fun, let’s give it a flicker effect. We’ll do this via an AnimationPlayer node.
  • Create a new animation in the AnimationPlayer.
  • Add a track for the energy property of the PointLight2D.
  • Add keyframes to the energy track to simulate flickering.
  • Enable looping, and change the blend mode to discrete.

Book of Nodes

Book of Nodes

Book of Nodes

Book of Nodes

Book of Nodes

Book of Nodes

  • Then play this animation via the code when the game loads.
### Main.gd

extends Node2D

@onready var animation_player = $AnimationPlayer

func _ready():
 animation_player.play("flicker")
Enter fullscreen mode Exit fullscreen mode
  • Run the scene and observe the player’s color changes when they go into the flickering lights.

Book of Nodes

LightOccluder2D

The LightOccluder2D node is used to cast shadows from a light source that hits it. This light source can come from a DirectionalLight2D or PointLight2D. It requires an OccluderPolygon2D to define the shape of the occlusion.

Book of Nodes

Mechanic:

Cast a shadow from our player.

Implementation:

  • Create a LightOccluder2D node to the source you want to cast your shadows from.

Book of Nodes

  • You’ll see it has an error. This is because we need to draw a OccluderPolygon2D shape to define the shape of our shadow. Give your LightOccluder2D a new OccluderPolygon2D resource, and draw the polygon around your player. Make sure that you complete your polygon by connecting all of your points.

Book of Nodes

Book of Nodes

  • Now in our Main scene, we will need a light source to cast this shadow shape. Let’s add a PointLight2D node and put it above our player. Make sure its shadows are enabled so that it can cast this shadow.

Book of Nodes

  • Run your scene and watch the shadow move depending on where the light hits the occluder.

Book of Nodes

AudioStreamPlayer2D and AudioStreamPlayer

These nodes are used to play audio in our games. We use the AudioStreamPlayer to play audio equally across our scene (such as background music or ambient sounds), and the AudioStreamPlayer2D to play audio positionally (such as from our players or NPCs).

Mechanic:

Play ambient music in the background, and sounds from the player when they move.

Implementation:

  • Download your sound effects. You can find free ones on Pixabay. Look for ones that work well in the background (they loop), and ones that are short effects, such as jumping sounds.

Book of Nodes

Book of Nodes

  • Add an AudioStreamPlayer node to play background audio. Add an AudioStreamPlayer2D node to play positional audio.

Book of Nodes

  • You will need to reimport your audio that is supposed to loop. Double-click it, and enable looping.

Book of Nodes

  • Set the stream property to the desired audio file.

Book of Nodes

Book of Nodes

  • Adjust properties like volume_db, and pitch_scale if needed. We’ll enable autoplay on our AudioStreamPlayer node since that is our background music.

Book of Nodes

  • We will play our sound effect audio (AudioStreamPlayer2D) when our player enters a certain area. To do this, add an Area2D node to your scene with a collision body, and attach its on_body_entered() signal to your script.

Book of Nodes

Book of Nodes

  • Now play the audio when the player enters the area.
### Main.gd

extends Node2D

@onready var audio_stream_player_2d = $AudioStreamPlayer2D

func _on_area_2d_body_entered(body):
 if body.name == "Player":
  audio_stream_player_2d.play()
Enter fullscreen mode Exit fullscreen mode
  • Run your scene. The background music should play, and the sound effect should play when your player enters the area.

NavigationAgent2D, NavigationObstacle2D, NavigationRegion2D

The NavigationAgent, NavigationObstacle, and NavigationRegion nodes are used to manage navigation and pathfinding in both 2D and 3D environments. These nodes help create dynamic and realistic movement for characters and objects, allowing them to navigate around obstacles and follow paths.

  • The NavigationAgent2Dnode is used to move characters along a path while avoiding obstacles.
  • The NavigationObstacle2Dnode is used to create obstacles that navigation agents will avoid.
  • The NavigationRegion2D node defines areas where navigation is allowed or restricted.

Book of Nodes

These three nodes combined allow us to create a more immersive world through mechanics such as NPC and Enemy roaming, particle movements, and controlled entity spawning.

Mechanic:

Create an NPC that roams around a certain area on the map.

Implementation:

  • In your Main scene, add a NavigationRegion2D to your scene to define the roaming area.

Book of Nodes

  • Create a new NavigationPolygonresource for this node so that we can define our region. Draw the polygon to where you want your region to be.

Book of Nodes

Book of Nodes

  • Now all we need to do is select our NavigationRegion node and select “Bake Navigation”. You’ll see a blue-colored polygon get drawn over our floor, that is our navigation region!

Book of Nodes

  • In a new scene, create your NPC using a CharacterBody2Dnode as the root node. Add the collisions and animations for this entity just as you did for your player.

Book of Nodes

  • To your NPC scene, add a NavigationAgent2D node. The NPC will be assigned to this agent so that they can roam in the region. Enable avoidance for this NPC so that they can avoid obstacles.

Book of Nodes

Book of Nodes

  • Attach a script to your NPC. We will then need to connect our signals from our NavigationAgent2D node to 1) compute the avoidance velocity of our NPC, and 2) redirect our NPC when that target is reached. For moving the NPC whilst avoiding obstacles, attach the velocity_computed signal to your script. For redirecting the NPC, attach the navigation_finished signal to your script.

Book of Nodes

Book of Nodes

  • We also want our NPC to pause before redirecting. To do this, we will add a Timer node to our scene. Enable its one_shot property, and change its wait_time to however long you want the NPC to wait before roaming again.

Book of Nodes

Book of Nodes

  • Also attach its timeout() signal to your script.

Book of Nodes

  • Now add your roaming functionality.
### NPC.gd

extends CharacterBody2D

@onready var navigation_agent_2d = $NavigationAgent2D
@onready var navigation_region = $"../NavigationRegion2D"
@onready var animation_tree = $AnimationTree
@onready var animation_state = animation_tree.get("parameters/playback")
@onready var timer = $Timer

# Variables
@export var movement_speed: float = 50.0
var roaming_area: Rect2 
var target_position: Vector2  

func _ready():
 # Add a delay to ensure the navigation map is loaded
 await get_tree().create_timer(1).timeout
 set_roaming_area()
 set_random_target()

func _physics_process(delta):
 # Move NPC towards the target
 var next_path_position = navigation_agent_2d.get_next_path_position()
 var new_velocity = (next_path_position - global_position).normalized() * movement_speed
 if navigation_agent_2d.avoidance_enabled:
  navigation_agent_2d.velocity = new_velocity
 else: 
  _on_navigation_agent_2d_velocity_computed(new_velocity)

 # Update the NPC's position
 move_and_slide()

 # Play walking animation
 update_animation(velocity)

func set_roaming_area():
 # Set the roaming area
 var navigation_polygon = navigation_region.get_navigation_polygon()
 if navigation_polygon.get_outline_count() > 0:
  var outline = navigation_polygon.get_outline(0)
  # Calculate the bounding rect
  var min_x = INF
  var min_y = INF
  var max_x = -INF
  var max_y = -INF
  for point in outline:
   min_x = min(min_x, point.x)
   min_y = min(min_y, point.y)
   max_x = max(max_x, point.x)
   max_y = max(max_y, point.y)
  roaming_area = Rect2(min_x, min_y, max_x - min_x, max_y - min_y)
 else:
  print("No outlines found in the navigation polygon.")

func set_random_target():
 # Set next roaming position within the roaming area
 target_position = Vector2(
  randf_range(roaming_area.position.x, roaming_area.position.x + roaming_area.size.x),
  randf_range(roaming_area.position.y, roaming_area.position.y + roaming_area.size.y)
 )
 navigation_agent_2d.set_target_position(target_position)

func update_animation(velocity: Vector2):
 if velocity.length() == 0:
  animation_state.travel("idle")
 else:
  animation_state.travel("walk")
  animation_tree.set("parameters/walk/blend_position", velocity.normalized())

func _on_navigation_agent_2d_velocity_computed(safe_velocity):
 # Move NPC
 velocity = safe_velocity

func _on_timer_timeout():
 # Move NPC again
 set_random_target()

func _on_navigation_agent_2d_navigation_finished():
 # When path reached, redirect NPC
 velocity = Vector2.ZERO
 animation_state.travel("idle")
 timer.start()
Enter fullscreen mode Exit fullscreen mode
  • Instance your NPC in your Main scene. Move them into your region.

Book of Nodes

  • Optionally, add NavigationObstacle2D nodes to create obstacles. Add this node to a StaticBody2D with a collision shape.

Book of Nodes

Book of Nodes

  • Run your scene and see your NPC randomly roam. They should avoid your obstacles.

Book of Nodes

Path2D and PathFollow2D

The Path2D and PathFollow2D nodes work together to create and follow paths in a 2D space. The Path2D node is used to define a path using a sequence of points. I like this more than using a NavMesh because it allows you to create and visualize a path in the Godot editor, instead of randomizing it. The PathFollow2D node is used to make an object follow a path defined by a Path2D node.

Book of Nodes

Mechanic:

Create an NPC that roams on a defined path on the map.

Implementation:

  • Create a Path2D node in your scene.

Book of Nodes

  • Add a PathFollow2D node as a child of the Path2D.

Book of Nodes

  • Ensure the rotatesvalue of this path is disabled since our NPC should only move diagonally.

Book of Nodes

  • In a new scene, create your NPC using a CharacterBody2Dnode as the root node. Add the collisions and animations for this entity just as you did for your player.

Book of Nodes

  • Attach your NPC to your PathFollow2D node. This will tell the game that this is the object that should follow this Path.

Book of Nodes

  • Now we can draw our path. In the Godot editor, select the Path2D node. Use the “Add Point” button in the toolbar to add points to draw the path shape that your NPC has to follow. Select the point to move it on your map.

Book of Nodes

Book of Nodes

Book of Nodes

  • Add more points to complete your path.

Book of Nodes

Book of Nodes

  • With your path created, attach a script to your NPC. Then, add the logic for them to move along the path.
### NPC.gd

extends CharacterBody2D

@onready var animation_tree = $AnimationTree
@onready var animation_state = animation_tree.get("parameters/playback")
@onready var path_follow = get_parent()

# Variables
@export var speed = 50.0
var current_offset = 0.0
var path_length = 0.0
var direction = 1

func _ready():
 # Get the total length of the path
 path_length = path_follow.get_parent().curve.get_baked_length()

func _physics_process(delta):
 # Update the progress along the path
 update_path_progress(delta)
 update_animation(Vector2(direction, 0))
 move_and_slide()

func update_path_progress(delta):
 # Update the progress along the path
 current_offset += speed * delta * direction
    # Reverse direction if the end or start of the path is reached
 if current_offset >= path_length or current_offset <= 0:
  direction *= -1 
 current_offset = clamp(current_offset, 0, path_length)
 path_follow.progress = current_offset

func update_animation(velocity: Vector2):
 if velocity.length() == 0:
  animation_state.travel("idle")
 else:
  animation_state.travel("walk")
  animation_tree.set("parameters/walk/blend_position", velocity.normalized())
Enter fullscreen mode Exit fullscreen mode
  • Run your scene and see your NPC roam. They should follow your path in a zig-zag (back-and-forth) motion.

Book of Nodes

TileMap

The TileMap node allows us to create 2D grid-based levels. With it, we can place our objects directly onto a grid to draw our world.

The TileMap is composed of cells. Each cell has the same dimensions.

Book of Nodes

The TileMap uses a TileSet Resource that contains an array of 2D tiles that can be placed on cells on the grid. Each tile is comprised of a sprite, either from an atlas (tilesheet) or an individual image.

Book of Nodes

We can also add these tiles on different layers, which are added on top of other objects on the grid. These tiles can also have their own collision, navigation, and physics properties.

Book of Nodes

Book of Nodes

Book of Nodes

In my 2D base template project, you will see that my entire (very basic) world was made using several layers and tilesheets on the TileMap node.

Book of Nodes

Mechanic:

Create a tile-based map.

Implementation:

  • Let’s start by adding a TileMap node to our scene.

Book of Nodes

  • In the Inspector Panel, give this node a new TileSet resource.

Book of Nodes

  • You’ll see a panel open at the bottom. This is where we can add the sprites and tilesheets that we want to draw on our TileMap. To make it clear, we create tiles in the TileSet panel, and we draw these tiles onto our world in the TileMap panel.

Book of Nodes

  • Now, find a tilesheet or a sprite that you like, and drag it into this panel. Say yes to the prompt if you are using a tilesheet so that it can separate the tiles into grid cells.

Book of Nodes

Book of Nodes

  • Here we can give our tileset a name and change its margins (if necessary, usually it’s not).

Book of Nodes

  • In the Select and Paint panels, we can draw properties such as navigation, collision, or other physics properties onto our tiles. We’ll get to that in a bit.

Book of Nodes

Book of Nodes

  • Now, go to your TileMap panel. You will see that we can paint our tiles onto our screen. Using the tools, you can draw, erase, or select tiles that you have added.

Book of Nodes

Book of Nodes

  • You can also erase tiles by hovering over them and holding down your right mouse button.

Book of Nodes

  • You can rotate tiles via the X and Z keys on your keyboard.

Book of Nodes

Book of Nodes

  • You’ll see that we are drawing all the tiles on one layer. If you draw a tile over an existing one, it will replace that tile.

Book of Nodes

Book of Nodes

Book of Nodes

  • We don’t want this, so let’s add more layers. In the Inspector Panel, underneath Layers, press the “Add Element” button to add more layers.

Book of Nodes

Book of Nodes

  • Now we can draw on our different layers.

Book of Nodes

Book of Nodes

Book of Nodes

  • But what about collisions? For this, we need a Physics Layer. Click on the TileSet resource in your Inspector Panel, and navigate to Physics Layers.

Book of Nodes

  • Click “Add Element” to add a layer. You usually only need one layer for collisions.

Book of Nodes

  • Now go back to your TileSet panel, and navigate to the Paint pane. Select the Physics property, and select the layer you just created.

Book of Nodes

Book of Nodes

  • Now we can draw collisions wherever we want the player and other entities to be blocked. We usually only do this on objects such as trees, buildings, or the outer boundaries of our map.

Book of Nodes

Book of Nodes

  • To delete collisions, just clear your polygon on the left and click on your existing cells.

Book of Nodes

  • You can add more tilesheets and draw collisions on there too.

Book of Nodes

  • The last thing I want to show you is autotiling. Drawing the ground tile for tile, with all of its edges is extremely tedious. We can make use of autotiling, also called TERRAINS, to speed up this process. This feature automatically selects and places the appropriate tile based on the surrounding tiles, ensuring that the tiles blend seamlessly together.
  • Click on the TileSet resource in your Inspector Panel, and navigate to Terrain Sets.

Book of Nodes

  • We will add an Element for each resource we want to “autotile”. So if you added a tilesheet for dirt, grass, water, and mud in your TileSet panel, you will create an element for each of those resources. Since we only have the “Dirt” tilesheet (we don’t want to autotile foliage or buildings), we will only add one element.

Book of Nodes

Book of Nodes

Book of Nodes

  • If we had a “Grass” TileSet, we would add another element.

Book of Nodes

  • Now go back to your TileSet panel, and navigate to the Paint pane. Select the Terrains property, and select the layer you just created.

Book of Nodes

Book of Nodes

Book of Nodes

  • Now we will draw in our bits. This is what the engine will use to check the tiles for the appropriate variant to use. This might be confusing to you, but just remember to select all the areas of your tilemap that ARE NOT on weird shapes or corners.
  • You will have to play around with the tiles to find the bits that give you the best results.

Book of Nodes

  • Navigate back to your TileMap node, and go to the terrains property. You’ll see that your autotiles have been created.

Book of Nodes

  • Select your terrain and draw it onto the screen.

Book of Nodes

  • Go ahead and create your mini world. You can also select and drag commonly used tiles into the Patterns panel for easy access.

Book of Nodes

Book of Nodes

Book of Nodes

  • Run your scene and test your creation!

Book of Nodes

Timer

The Timer node is used to create countdown timers that can trigger events after a specified period. The Timer node provides several properties to control its behavior, including wait_time, autostart, and one_shot.

  • wait_time : The duration in seconds that the timer will count down before emitting the timeout signal.
  • autostart : If set to true, the timer will start automatically when the scene is loaded.
  • one_shot : If set to true, the timer will stop after emitting the timeout signal once. If false, the timer will restart automatically after each timeout.

It comes with a timeout signal, which is emitted when the timer reaches zero. This signal can be connected to a function to perform specific actions when the timer completes its countdown. The timeout signal is a crucial part of the Timer node's functionality, allowing you to trigger events at precise intervals.

Mechanic:

Spawn an enemy every 5 seconds.

Implementation:

  • Add a Timer node to your scene. Set its wait_time to 5 seconds. Since we want the enemy to “spawn” as soon as the game loads, we should enable its autostart property.

Book of Nodes

Book of Nodes

  • Connect the timer node’s timeout signal to your script. This will execute our logic to spawn our enemy every 5 seconds.

Book of Nodes

  • In your code, let’s “spawn” an enemy. Since we don’t have an actual enemy scene, we will just print the amount of enemies we have spawned.
### Main.gd

extends Node2D

@onready var timer = $Timer
var enemy_count = 0

func _ready():
 if not timer.is_stopped():
  timer.start()

func _on_timer_timeout():
 spawn_enemy()

func spawn_enemy():
 enemy_count += 1
 print("An enemy has spawned!")
 print("Current enemy count: ", enemy_count)
Enter fullscreen mode Exit fullscreen mode
  • Run your game and your enemy should spawn each time the timer reaches 0!

Book of Nodes

Top comments (2)

Collapse
 
aquablue profile image
k p

Thank you for going through these with examples!!!! This is so useful, thank you so much!

Collapse
 
christinec_dev profile image
christine

You're welcome!!!