Course PDF Notes
Course PDF Notes
Introduction
Welcome to our beginner-friendly course on developing a 3D platformer game using the Godot
engine. Your instructor for this course is Daniel Buckley. Together, we will explore various aspects of
game development in Godot, including level setup, creating coins, enemies, a player controller, and
more. By the end of this course, you will have a fully functional 3D platformer game.
Course Overview
The Player
The player will be a scene that can be moved around and can jump using various keys on
your keyboard (arrow keys, WASD, etc.).
We will set up input maps to facilitate these movements.
The player will have movement animations, including bobbing up and down and rotating to
face the direction of movement.
Enemies
Enemies will be represented by buzz saws that move from one position to another.
These enemies will be customizable, allowing you to set their movement speed, direction,
and distance.
Upon hitting the player, enemies will deal damage, reduce the player’s health, and trigger
sound effects and screen shake.
Coins
Coins will be collectible items that disappear when the player collects them, increasing the
player’s score and playing a sound effect.
Coins will have a visual effect where they bob up and down and rotate over time to attract
the player’s attention.
End Flag
Course Requirements
To fully benefit from this course, a basic understanding of Godot and GDScript is recommended.
However, do not worry if you are not fully proficient. We will go through the code line by line,
ensuring that you understand each step. Here are some assumed knowledge areas:
About Zenva
Zenva is an online learning academy with over a million students. We offer a wide range of courses
suitable for beginners and those looking to learn something new. Our courses are versatile, allowing
you to learn in various ways:
Getting Started
Now that you have an overview of what to expect, let’s dive into the first lesson and begin our
journey into 3D platformer game development with Godot.
Technology changes rapidly, so to make sure you have an optimal learning experience we
recommend using Godot 4.4 Beta for this course – the upcoming stable release.
Please make sure not to use newer versions of the software than what we recommend, as the course
material provided might not work as expected.
You can download the preview version of Godot by heading to the preview downloads page on the
Godot website: [Link]
Once on the download page, just click the Standard option for whichever operating system is
relevant to your system for Godot 4.4 Beta. This will download the Godot engine to your local
computer. From there, unzip the file and simply click on the application launcher – no further
installation steps are required!
Welcome to this beginner-friendly guide on designing a 3D platformer game using the Godot engine.
Before we dive into the practical aspects of creating the game, it’s essential to outline the game’s
design, mechanics, and key elements. This article will walk you through the fundamental building
blocks required to develop your 3D platformer game.
Game Overview
The game is a 3D platformer where the player’s primary objectives are to collect coins and avoid
enemies. The ultimate goal of each level is to reach the end flag, which teleports the player to the
next level. Here’s a breakdown of the game’s key elements:
Player Character
Movement: The player can move using the keyboard and jump. Inputs for movement will be
defined using Godot’s input map system.
Visual Polish: The player character will rotate to face the direction of movement and bob up
and down to simulate motion.
Health System: The player has a health variable that decreases when hit by enemies. If the
health reaches zero, the player is sent back to the menu.
Enemies
Behavior: Enemies are spinning blades that move from one point to another. Their
movement direction, distance, and speed can be defined.
Damage Mechanics: When an enemy hits the player, the following occurs:
1. The player’s health variable decreases by one.
2. The health UI updates to show the remaining health.
3. Screen shake effect simulates the impact.
4. A damage sound effect is played.
Coins
Collectibles: Coins are collectibles that increase the player’s score by one when picked up.
Animation: Coins rotate and bob up and down.
Sound Effect: A sound effect is played when a coin is collected.
Scoring System
The scoring system will be implemented using auto load scripts or singletons, allowing the score to
persist across levels. This ensures that the player’s score is maintained throughout the game.
End Flag
Level Transition: The end flag is a node placed at the end of each level. When the player
enters it, the next level is loaded.
Menu
First Scene: The menu is the first scene the player encounters.
Play Button: Pressing the play button loads the player into level one.
Quit Button: Pressing the quit button closes the game.
Now that we have a clear understanding of the game’s design and mechanics, we can begin setting
up the game in Godot. Stay tuned for the next steps, where we will dive into the practical aspects of
creating our 3D platformer game.
This overview provides a solid foundation for beginners at Zenva to understand the key components
of a 3D platformer game in Godot. As we progress, we will delve deeper into each element, ensuring
a comprehensive learning experience.
Welcome to this beginner-friendly guide on starting your first project in Godot. This article will walk
you through the initial steps of setting up your project, importing assets, and creating your first
scene. By the end of this guide, you will have a solid foundation to build upon as you continue your
learning journey with Zenva.
Begin by opening Godot and creating a new project. The first thing you will see is the editor interface
with various docks. You can rearrange these docks to suit your preferences. For this guide, let’s
enlarge the FileSystem and Inspector docks to get a better view of our project structure and asset
properties.
Importing Assets
Before we start building our game, we need to import some assets. These assets include audio clips,
3D models, and sprites for the user interface (UI). You can download the provided zip file from the
course files tab, which contains the following:
Audio files (e.g., coin sound effect, take damage sound effect)
3D models (e.g., character models, platformer models)
Sprites (e.g., heart PNG, menu background)
Feel free to explore the imported assets by clicking on the folders and previewing the contents. You
can always use your own assets if you prefer, but using the provided assets will help you follow along
with the course exactly.
Now that we have our assets imported, let’s create our base scene, which will serve as the first level
of our game. To do this, follow these steps:
1. Click on the “New Scene” button in the top left corner of the editor.
2. Select “3D Scene” as the root node type, since we are creating a 3D game.
3. Rename the root node to “Main” for better organization.
4. Save the scene by going to Scene -> Save Scene or pressing Ctrl + S.
5. Create a new folder named “scenes” in the FileSystem dock and save the scene as “Level1”
within this folder.
Additionally, create a folder for our scripts by right-clicking on the res:// folder, selecting “New
Folder,” and naming it “scripts.” This folder will be used to store our scripts in the future.
Next Steps
With our base scene set up, we are now ready to start creating the game environment. In the next
lesson, we will begin adding platforms and other visual elements to our scene. This will serve as the
foundation for our player to interact with as we continue to develop our game.
Congratulations on completing the initial setup of your Godot project! Stay tuned for more lessons as
Welcome to this beginner-friendly tutorial from Zenva on setting up a basic environment for a
platformer game. In this lesson, we will guide you through the process of creating a simple level for
your player to navigate. By the end of this tutorial, you will have a basic understanding of how to set
up platforms, configure lighting, and manage physics in your game environment.
Before we dive into the technical aspects, let’s plan what we want our level to look like. For this
tutorial, we will create a central platform where the player spawns and a clockwise loop of platforms
that get higher, leading to an end flag.
Adding a Camera
Configuring Lighting
To improve the visibility and aesthetics of our scene, we need to add lighting:
1. Create a new DirectionalLight3D node. This will act as the sun in our scene.
2. Adjust the light’s rotation to change the time of day (e.g., daytime, nighttime).
3. Enable shadows in the inspector to add realism to the scene. You can adjust the blur and
opacity of the shadows to achieve the desired effect.
To ensure that the player can interact with the platforms, we need to give them physical properties:
1. Select the platform model and click on the ‘Mesh’ button at the top of the screen.
2. Choose ‘Create Collision Shape’ and select ‘Simplified Convex’ as the collision shape type.
3. Create a new StaticBody3D node and drag the model and collider as children of this node.
4. Rename the static body and collider for better organization.
There is an alternative method to create platforms with a static body and collider:
Now that you know how to create platforms, it’s time to build your level:
Drag in more platforms and position them to create a path for the player.
Use the methods outlined above to ensure each platform has a static body and collider.
Experiment with different models and arrangements to create a unique level design.
In the next lesson, we will improve our environment a bit further before jumping into our player
setup.
Welcome to this beginner-friendly guide on enhancing your game’s visuals using Godot’s
WorldEnvironment node. In this article, we will walk through the process of setting up a background,
adding fog, adjusting colors, and organizing your scene. By the end of this tutorial, you will have a
more visually appealing and organized game scene.
The WorldEnvironment node in Godot is a powerful tool that allows you to modify various aspects of
your game’s visuals. With this node, you can add fog, adjust saturation, brightness, contrast, glow,
ambient occlusion, reflections, skies, and more. This node is essential for creating a immersive and
visually appealing game environment.
To begin, let’s set up a sky for our game. A sky adds depth and realism to your game environment.
By following these steps, you will have a basic sky in your game. However, to make it more
interesting, we can add some fog.
Adding Fog
Fog can enhance the atmosphere of your game by adding a layer of depth and color.
1. In the WorldEnvironment node, scroll down to the Fog section and enable it.
2. Adjust the Light Color to your desired fog color. For this tutorial, let’s choose a pinkish-purple
color.
3. Increase the Light Energy to make the fog brighter. A value of 1.2 is a good starting point.
4. Adjust the Density to control the thickness of the fog. A value of 0.06 works well for a subtle
effect.
5. Enable Height Fog and set the Height to 2m and Height Density to 0.4 for a gradual fog effect
along the y-axis.
Adjusting Colors
To make your game’s visuals pop, you can adjust the brightness, contrast, and saturation.
1. In the WorldEnvironment node, scroll down to the Adjustments section and enable it.
2. Increase the Contrast to about 1.2 for a more vibrant look.
3. Increase the Saturation to make the colors more vivid.
Ambient Light
Ambient light defines the color of objects that are not directly hit by the sun. Adjusting ambient light
can greatly enhance the overall look of your game.
Screen Space Ambient Occlusion (SSAO) adds shadows where two models intersect, creating a more
realistic lighting effect.
1. In the WorldEnvironment node, scroll down to the SSAO section and enable it.
2. Observe the differences in the corners where two models intersect to see the effect in action.
To keep your scene organized, you can use base nodes as folders to group related nodes together.
Organizing your scene makes it easier to manage, especially as you add more elements like enemies
and coins.
Conclusion
Congratulations! You have successfully enhanced your game’s visuals using the WorldEnvironment
node in Godot. By adding a sky, fog, adjusting colors, and organizing your scene, you have created a
more immersive and visually appealing game environment. In the next lesson, we will begin setting
up our player. Stay tuned and happy learning!
Welcome to this beginner-friendly guide on setting up your player character in Godot, tailored for
those starting their journey with Zenva. In this tutorial, we will walk through the process of creating
and configuring your player character, which will serve as the heart of your game. By the end of this
guide, you will have a player character that can move, jump, collect coins, interact with enemies,
and reach the end flag.
To begin, we need to create a root node for our player. This node will be of type CharacterBody3D.
The CharacterBody3D node is essential for characters as it provides built-in physics, velocity settings
for movement, collision detection, and various other functionalities that simplify character creation.
Next, we need to add a visual representation for our player. This will be the model that players see
and control in the game.
To enable physics and collision detection, we need to add a collision shape to our player.
1. Right-click on the Player node and add a child node of type CollisionShape3D.
2. In the inspector, set the shape to New CapsuleShape3D.
3. Adjust the radius and height of the capsule to fit the player model appropriately.
4. Ensure the capsule is positioned so that the player’s feet are at the bottom of the capsule.
You can use the side view to fine-tune the positioning. If the view is obscured by a purple overlay,
temporarily disable the fog in the world environment settings.
Ensure the player root node’s rotation is set to zero to avoid warnings. Adjust the rotation in the
main level scene if necessary.
In the next lesson, we will focus on setting up player inputs and coding the player’s movement and
jumping mechanics. Stay tuned for more detailed instructions on enhancing your player character’s
functionality.
Thank you for following along, and we look forward to seeing you in the next lesson!
Welcome to another informative lesson from Zenva. In this tutorial, we will guide you through setting
up player inputs in Godot. Inputs are essential for mapping specific keys to corresponding actions in
your game, such as moving forward with the ‘W’ key or jumping with the spacebar. Properly
configuring these inputs is crucial before you start coding, as it ensures your script knows which keys
trigger which actions.
In Godot, inputs are managed through actions. Actions are what your script will listen for, rather than
specific keys. For example, your code will listen for a ‘jump’ action instead of directly checking if the
spacebar is pressed. This abstraction allows for greater flexibility, as you can associate multiple keys
with a single action.
Setting Up Inputs
2. Go to Project Settings.
Creating Actions
Actions are the intermediaries between key presses and your script. Here’s how to create and
configure actions:
2. With the action selected, click the Plus icon on the right to add a new key.
3. Use the filter to find and select the spacebar. Click OK to associate the spacebar with the
‘jump’ action.
You will need to create additional actions for movement. Follow the same steps to create the
following actions:
Remember, you can add multiple keys to a single action. This is useful for supporting both keyboard
and controller inputs.
Next Steps
With all your inputs and actions set up, you are now ready to start scripting your movement logic
using these actions. In the next lesson, we will dive into scripting and utilize these actions to control
your player’s movements.
Thank you for joining us today. We look forward to seeing you in the next lesson!
Welcome back to our course on game development with Godot. In this lesson, we will begin working
on our PlayerController script, which will manage all aspects related to our player’s movement. This
script will be crucial for defining how our player interacts with the game world.
To get started, ensure you have the PlayerController script open. If you don’t see it, select your
Player node and double-click on the PlayerController script. If the script does not exist, create a new
script attached to the Player node.
We will begin by defining a few essential variables that will control our player’s movement:
move_speed: This float variable will define how many units the player moves per second.
jump_force: This float variable will define the force applied upwards when the player jumps.
gravity: This float variable will define the downward force applied to the player.
camera: This variable will hold a reference to our camera.
extends CharacterBody3D
The @export tag allows us to modify this variable directly from the Godot editor, making it easier to
tweak values without changing the script.
Finally, add the camera variable with the @onready tag to automatically assign the camera
reference when the node is ready:
The _physics_process function is where we will handle all physics-related code. This function is called
a fixed number of times per second, ensuring consistent physics calculations.
Let’s create the _physics_process function and add comments for the steps we will implement:
func _physics_process(delta):
# Apply gravity
# Implement jump
# Implement movement
Implementing Movement
We will start by implementing the movement logic. This involves getting the player’s input and
moving the player based on that input.
velocity.x = move_dir.x
velocity.z = move_dir.z
Finally, call the move_and_slide function to move the player and handle collisions:
move_and_slide()
Putting it all together, your _physics_process function should look like this:
func _physics_process(delta):
# Apply gravity
# Implement jump
# Implement movement
var move_input : Vector2 = -Input.get_vector("move_right", "move_left", "move_bac
k", "move_forward")
var move_dir : Vector3 = Vector3(move_input.x, 0, move_input.y)
velocity.x = move_dir.x
velocity.z = move_dir.z
move_and_slide()
As a challenge, try to modify the code so that the player’s movement speed is affected by the
move_speed variable. Hint: You will need to multiply the velocity components by move_speed.
With this setup, you should now have a basic movement system for your player. In the next lessons,
we will continue to build upon this foundation, adding more features and refining the player’s
controls.
In this lesson, we will enhance our player character’s movement by implementing gravity and jump
functionality. By the end of this lesson, you will have a character that can move, fall due to gravity,
and jump when the spacebar is pressed.
Challenge Solution
In the last lesson, we issued a challenge to you to adjust the movement with the move_speed
variable we set up. To do this, we simply need to multiply our velocity direction vectors by the
move_speed:
Implementing Gravity
First, let’s add gravity to our player character to make it fall when not standing on a platform. We will
modify the _physics_process function in the player_controller.gd script.
# gravity
Below this line, add the following code to apply gravity to the player:
This line decreases the y-velocity by the value of gravity multiplied by delta (the time passed since
the last frame), causing the player to accelerate downwards.
Implementing Jump
Next, let’s implement the jump functionality. We want the player to jump only when the spacebar is
pressed and the player is standing on the floor. Add the following code below the gravity line in the
_physics_process function:
# jump
if Input.is_action_pressed("jump") and is_on_floor():
velocity.y = jump_force
This code checks if the “jump” action (spacebar) is pressed and if the player is on the floor. If both
conditions are true, it sets the y-velocity to jump_force, causing the player to jump.
Finally, let’s adjust the camera to provide a better view of the player’s surroundings. Back in Godot,
manually adjust the tilt of the camera down to ensure the platform can be seen better. The goal
should be for the player to better understand where they’re jumping too and judge the distances
correctly.
Summary
In this lesson, we implemented gravity and jump functionality for our player character. We also
adjusted the camera to provide a better view of the game world. With these additions, our character
controller now supports basic movement, gravity, and jumping.
In the next lesson, we will improve our player’s visuals by adding rotation to face the direction of
movement and implementing a bobbing effect to simulate movement animation.
Welcome back! In this lesson, we will focus on enhancing the visuals of our player character.
Specifically, we will implement two key features:
First, we need to create a new script for our player’s model. This script will handle the rotation and
movement bob effects.
Defining Variables
We will define several variables to control the rotation rate, target rotation, and reference to the
player node.
extends MeshInstance3D
rotate_rate: This variable controls how fast the player rotates to face the movement
direction.
target_y_rot: This variable stores the target rotation along the y-axis.
player: This variable references the player node to access its velocity.
Creating Functions
Next, we will create the necessary functions to handle the rotation and movement bob effects. We
will start by defining the function structures and then fill them out.
func _process(delta):
_smooth_rotation(delta)
_move_bob(delta)
func _smooth_rotation(delta):
pass
func _move_bob(delta):
pass
_process(delta): This function is called every frame and will invoke the _smooth_rotation and
_move_bob functions.
_smooth_rotation(delta): This function will handle the smooth rotation of the player model.
_move_bob(delta): This function will handle the movement bob effect.
The _smooth_rotation function will rotate the player model to face the direction of movement. We
will use the player’s velocity to determine the target rotation.
func _smooth_rotation(delta):
var vel = -[Link]
if vel.x != 0 or vel.z != 0:
target_y_rot = atan2(vel.x, vel.z)
We first get the player’s velocity and store it in the vel variable.
If the player is moving (i.e., vel.x or vel.z is not zero), we calculate the target rotation using
the atan2 function.
We then smoothly interpolate the current rotation to the target rotation using the lerp_angle
function.
The _move_bob function will add a subtle up-and-down motion to the player model when moving. We
will use the player’s velocity to determine the movement speed and apply the bob effect only when
the player is moving on the ground.
func _move_bob(delta):
var move_speed = [Link]()
We first calculate the movement speed using the length of the player’s velocity.
If the player is standing still (move_speed < 0.1) or not on the ground (not
player.is_on_floor()), we set the scale to 1 and return. This more or less stops the bobbing
motion from happening, as in those scenarios we wouldn’t want to simulate movement.
In the next lesson, we will complete our bobbing motion and discuss in-depth how we can properly
adjust the player’s y-scale over time.
Welcome back! In this lesson, we will continue working on our MoveBob code. Our goal is to make
the player’s scale bob up and down slightly over time while moving on the ground. This effect will be
achieved using a sine wave. Let’s dive into the details.
A sine wave is a mathematical function that returns a value based on a given time. The wave
oscillates between -1 and 1, creating a smooth, repetitive pattern. This pattern is perfect for creating
a bobbing effect.
First, we need to create a variable that keeps track of the time. We’ll use Godot’s
Time.get_unix_time_from_system() function to get the current time in seconds.
Next, we’ll use the sine function to calculate the y-scale. The sine function takes a time value and
returns a value between -1 and 1.
scale.y = y_scale
To make the bobbing effect more subtle and visually appealing, we need to adjust the amplitude and
frequency of the sine wave. We’ll multiply the sine value by a small number to control the amplitude
and multiply the time variable by a larger number to control the frequency.
Here is the complete code snippet that implements the bobbing effect:
After implementing the code, test the bobbing effect by moving the player around. You should see
the player’s scale bobbing up and down slightly, creating a more dynamic and visually appealing
movement.
You can expand upon this effect by adding more visual improvements, such as:
Making the player stretch out when jumping and squash back down when landing.
Adding more complex animations based on the player’s movement and actions.
That’s it for this lesson! You’ve successfully implemented a bobbing effect using a sine wave. In the
next lessons, we’ll continue to enhance our game with more advanced features.
In this lesson, we will set up our enemy in the game. The enemy will move back and forth between
two positions and detect when the player comes into contact with it. This detection will later be used
to apply damage to the player. Let’s get started by creating and configuring our enemy node.
To begin, we need to create an Area3D node. This node will detect collisions with other objects
without physically blocking them, acting like a trigger.
Adding a Collider
Next, we need to add a collider to the enemy so that it can detect when the player comes into
contact with it.
1. Right-click on the Enemy node and add a child node of type CollisionShape3D.
2. Set the shape of the CollisionShape3D to BoxShape3D.
3. Adjust the bounds of the box shape to roughly match the size of the saw model. You can hold
down the Alt key to uniformly size the collider.
Now, we will create a script to manage the enemy’s movement, spinning, and collision detection.
With the Enemy node selected, create a new script and save it as [Link] in the Scripts folder.
Let’s start by defining the variables we’ll need for our enemy script.
The move_speed variable is a float that determines how fast our enemy moves.
The move_direction variable is a Vector3 that defines the direction in which our enemy
moves. This vector will also determine the distance our enemy travels. For example, if we
want our enemy to move 2 units down, we would set the y-component of the vector to -2.
The spin_speed variable is another float that determines how fast our enemy spins,
measured in degrees per second.
extends Area3D
Next, we’ll define three more variables using the @onready keyword.
The start_pos variable will store the starting position of our enemy, which we’ll set to its
current global position.
The target_pos variable will store the position our enemy is currently moving towards. At the
start of the game, this will be set to the start_pos plus the move_direction. This means our
enemy will start by moving towards a position that is move_direction units away from its
starting position.
Finally, the model variable will store a reference to our enemy’s model node.
To detect when the player enters the enemy’s collider, we need to connect the body_entered signal
to our script.
To ensure that only the player triggers the damage, we will assign the player to a specific group.
In the _on_body_entered function, we will check if the body that entered the collider is the player. If
it’s not the player, we’ll exit the function. If it is, for now we’ll just print to the console (we’ll set up
damage in a future lesson).
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
print("Take damage")
Finally, let’s test our setup to ensure that the enemy detects the player and prints “Take damage”
when the player enters the enemy’s collider.
In the next lesson, we will work on moving the enemy between two points and spinning the model
inside. Stay tuned!
In this lesson, we will focus on implementing the movement and rotation of the enemy in our game
using the Godot engine. By the end of this lesson, you will have an enemy that moves back and forth
between two points and rotates over time. Let’s get started!
To begin, we need to make our enemy move towards a target position every frame. We’ll use the
_process function to achieve this. Here’s how you can set up the basic movement:
The movement speed is controlled by multiplying move_speed by delta. The delta parameter
ensures that the movement is frame-rate independent, making the enemy move a small distance
each frame.
move_speed * delta
To make the enemy move back and forth, we need to switch the target position when the enemy
reaches it. We’ll use a condition to check if the enemy has reached the target position and then
switch it.
if global_position == start_pos:
target_pos = start_pos + move_direction
elif global_position == start_pos + move_direction:
target_pos = start_pos
Next, we’ll make the enemy rotate over time. We’ll rotate the enemy’s model along its Z-axis. Here’s
how you can do it:
Godot uses radians for rotation behind the scenes, even though the inspector shows degrees. To
convert the spin speed from degrees to radians, we use the deg_to_rad function.
deg_to_rad(spin_speed)
After implementing the movement and rotation, save the enemy as a scene. Then, position the
enemy in your level. You can duplicate and move the enemy to create multiple instances.
Drag the enemy into your scenes folder and save it.
Position the enemy in your level.
Duplicate and move the enemy to create multiple instances.
By following these steps, you will have an enemy that moves back and forth and rotates over time.
This adds dynamic behavior to your game and makes the enemy more engaging. In the next lesson,
we will focus on making the enemy deal damage to the player.
In this lesson, we will implement a damage system for our player character. This system will allow
enemies to deal damage to the player, reducing their health points. When the player’s health
reaches zero, the game will restart the level. Let’s break down the steps to achieve this:
First, we need to add a health variable to our player controller script. This variable will keep track of
the player’s current health points.
extends CharacterBody3D
The @export keyword allows us to modify the health value directly in the Godot editor if needed.
Next, we need a function that will handle the logic for taking damage. This function will subtract the
damage amount from the player’s health and check if the health has reached zero.
This function takes an amount parameter, which is the amount of damage to be dealt to the player.
It subtracts this amount from the player’s health and checks if the health is less than or equal to
zero. If so, it calls the _game_over function.
The _game_over function will handle the logic for restarting the level when the player’s health
reaches zero.
func _game_over():
get_tree().change_scene_to_file("res://Scenes/[Link]")
This function uses the get_tree().change_scene_to_file method to reload the current scene,
effectively restarting the level.
Now, we need to modify the enemy script to call the take_damage function whenever the enemy hits
the player.
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
body.take_damage(1)
This function checks if the body that entered the enemy’s collision area is the player. If so, it calls
the take_damage function on the player, dealing 1 damage.
Finally, we need to handle the scenario where the player falls off the level. We will add a check in the
player controller script to call the _game_over function if the player’s Y position falls below a certain
threshold.
func _process(delta):
if global_position.y < -5:
_game_over()
This function checks every frame if the player’s Y position is below -5. If so, it calls the _game_over
function to restart the level.
With these changes, our damage system should now be fully implemented. To test it:
1. Run the game and have the player get hit by enemies to ensure the health decreases and
the game restarts when health reaches zero.
2. Have the player fall off the level to ensure the game restarts when the player falls below the
threshold.
By following these steps, you have successfully implemented a damage system for your player
character in Godot. This system allows enemies to deal damage to the player, reduces the player’s
health, and restarts the level when the player’s health reaches zero or if the player falls off the level.
Welcome back! In this lesson, we will focus on creating and implementing coins in our game. These
coins will serve as the foundation of our scoring system. By the end of this lesson, you will have
coins that the player can collect, which will increase their score and disappear upon collection.
Let’s begin by creating our coin. The process is similar to how we created our enemy, using an
Area3D node.
Position the coin in the scene where you want it to appear. This will help us test the coin’s
functionality later.
Next, we will create a script to handle the coin’s behavior, including detecting when the player
collects it and increasing the player’s score.
1. Create a new script named [Link] and attach it to the Coin node.
2. With the Coin node still selected, go to the Signal tab and add an _on_body_entered signal –
which we will use to detect the player.
3. In the script, the _on_body_entered function checks if the body entering the coin’s area is in
the “Player” group. If it is, the coin will be destroyed using the queue_free function.
extends Area3D
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
In the future, we will also increase our score – but for now we simply want to ensure the coin is
deleted.
To make the coin more visually appealing, we will add rotation and bobbing motion. Let’s start with
rotation.
func _process(delta):
# Rotate the coin
rotation.y += deg_to_rad(rotate_speed) * delta
The deg_to_rad function converts the rotation speed from degrees to radians, ensuring the coin
rotates at the correct speed.
Thank you for following along! In the next lesson, we will continue building our coin by adding a
bobbing up and down motion to it. Stay tuned!
Welcome back! In this lesson, we will complete our coin implementation by adding an up-and-down
bobbing motion. This will make our coin more visually appealing and dynamic. Let’s dive into the
steps required to achieve this effect.
Setting Up Variables
First, we need to define a few variables in our coin script. These variables will control the height and
speed of the bobbing motion, as well as the starting Y position of the coin.
bob_height: This export variable defines how high the coin will bob up and down.
bob_speed: This export variable controls the speed of the bobbing motion.
start_y_pos: This onready variable stores the initial Y position of the coin.
extends Area3D
To create the bobbing motion, we will use a sine wave. A sine wave oscillates between -1 and 1,
which we can use to move the coin up and down. We will sample the sine wave every frame to get
the current position of the coin.
1. Create a variable called time that increases every second. This will be used to sample the
sine wave.
2. Calculate the Y position offset using the sine function and the time variable.
3. Adjust the Y position of the coin based on the calculated offset.
func _process(delta):
# bob up and down
var time = Time.get_unix_time_from_system()
var y_pos = sin(time)
global_position.y = start_y_pos + y_pos
After implementing the initial bobbing motion, you might notice that the coin goes below its starting
position. To fix this, we need to adjust the range of the sine wave from -1 to 1 to 0 to 1. This ensures
that the coin never goes below its starting position.
To control the speed and height of the bobbing motion, we will use the bob_speed and bob_height
variables. Multiply the time variable by bob_speed to control the speed, and multiply the resulting
Y position offset by bob_height to control the height.
func _process(delta):
# bob up and down
var time = Time.get_unix_time_from_system()
var y_pos = (1 + sin(time * bob_speed)) / 2 * bob_height
global_position.y = start_y_pos + y_pos
Once the bobbing motion is implemented, save the coin as a scene. You can then place multiple
coins around your level by copying and pasting the coin node.
To keep your scene organized, create a new node of type Node and rename it to Coins. Select all
your coin nodes and drag them into the Coins node. This will help you manage your coins more
easily.
Repeat the process for other nodes, such as enemies, to keep your scene hierarchy tidy.
Congratulations! You have now completed the implementation of the coin with a bobbing motion. In
the next lesson, we will continue building our game by adding more features and functionality.
Welcome back! In this lesson, we will implement a scoring system for our game. Unlike traditional
scoring systems that reset when the player moves to a new level, our scoring system will persist
across levels. This means that the score the player achieves in one level will carry over to the next.
To accomplish this, we will use a feature in Godot called autoloads or singletons.
Understanding Autoloads/Singletons
Autoloads, also known as singletons, are scripts that run independently of the scene and node
structure. They can be accessed from anywhere in the project at any time, and their values and
functions remain constant as long as the game is running. This makes them perfect for storing
persistent data like a player’s score.
Now, open the [Link] script and add the following code:
extends Node
Next, we need to modify the [Link] script to access and update the score in the
PlayerStats singleton.
Finally, we need to update the [Link] script to use the new increase_score function. Open the
[Link] script and modify the _on_body_entered function as follows:
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
body.increase_score(1)
queue_free()
Now, when you play the game and collect coins, you should see the score printed in the output. The
score will persist even if the player restarts the level. This is because the score is stored in the
PlayerStats singleton, which runs independently of any scenes or nodes.
To ensure the score resets when the game is over, we need to modify the _game_over function in
the [Link] script:
func _game_over():
[Link] = 0
get_tree().change_scene_to_file("res://Scenes/[Link]")
That’s it! You have now implemented a persistent scoring system using autoloads/singletons in
Godot. In the next lesson, we will explore how to display the score on the user interface. See you
then!
Welcome to this beginner-friendly tutorial at Zenva on creating a player User Interface (UI) in Godot.
In this lesson, we will focus on adding essential UI elements to help keep track of the player’s health
and score. By the end of this tutorial, you will have a clear understanding of how to create and
manage UI elements in Godot.
To begin, we need to create a CanvasLayer. This node is crucial for UI elements as it ensures that
they remain fixed on the screen, independent of the camera’s movement. Here’s how to do it:
Next, we will add hearts to represent the player’s health. These hearts will be displayed at the top
left corner of the screen.
To manage multiple hearts efficiently, we will use a Horizontal Box Container. This node
automatically arranges its child nodes horizontally.
Below the hearts, we will add a text label to display the player’s score.
1. Create a new Label node outside the HealthContainer to avoid visual bugs.
2. Rename the node to ScoreText.
3. In the inspector, set the text to something like “Score: 500”.
4. Adjust the font size and add a shadow for better visibility:
Create a new Label Settings resource.
Increase the font size (e.g., 38).
Add a shadow with a size of about 8 pixels and adjust the color for better contrast.
The final step is to prepare for scripting the UI to make it interactive. We will create a script that will
handle updating the hearts and score text based on the player’s actions.
In the next lesson, we will dive into scripting the UI to make it dynamic and responsive to the
player’s actions. Stay tuned for more!
Thank you for following along. See you in the next lesson!
Welcome back! In this lesson, we will begin scripting our User Interface (UI) in Godot. We’ll explore
two methods to update our UI elements: checking and updating every frame versus updating only
when necessary. The latter approach is more efficient and aligns with best practices in programming.
Updating UI Elements
There are two primary ways to update UI elements based on player actions:
1. Frame-by-Frame Update: Check and update UI elements every frame. This method is
feasible for a small number of UI elements but can impact performance as more elements
are added.
2. Event-Driven Update: Update UI elements only when specific events occur, such as
changes in the player’s health or score. This method is more efficient and follows best
practices by reducing dependencies between scripts.
To implement event-driven updates, we will use signals in Godot. Signals are events that can be
emitted and listened to by other parts of the game. This approach minimizes dependencies and
improves performance.
We will define custom signals in our player controller script to notify the UI when the player’s health
or score changes. Here’s how to define these signals:
Emitting Signals
Next, we need to emit these signals at the appropriate times. For example, when the player takes
damage or collects a coin, we will emit the respective signals.
if health <= 0:
_game_over()
Listening to Signals
In our player UI script, we will create functions that will be called when these signals are emitted.
These functions will update the UI elements accordingly (though for now we’ll leave them blank).
To connect the signals to the functions, we will use the connect method in the _ready function of our
player UI script.
func _ready():
var player = get_parent()
[Link](_update_hearts)
[Link](_update_score_text)
Now whenever these signals are emitted from the player, our UI update functions will trigger!
Initializing Variables
With our signals connected, we can now work on our update functions. We first need to initialize
some variables to reference our UI elements. These variables will be used to update the UI elements
when the signals are emitted. We need a reference to our health container, an array of all our hearts,
and a reference to our score_text label.
Let’s start by updating the score text. This is a straightforward process where we update the text
property of the score label whenever the score changes.
To ensure we overwrite our default text, we also want to make sure to call this function in this
script’s Ready function:
[Link](_update_hearts)
[Link](_update_score_text)
_update_score_text([Link])
Conclusion
In this lesson, we covered how to use signals to update UI elements efficiently. We defined custom
signals, emitted them at the appropriate times, and connected them to functions that update the UI.
This approach ensures that our UI updates only when necessary, improving performance and
adhering to best practices in programming.
In the next lesson, we will continue working on our UI by updating the hearts to reflect the player’s
health.
In this lesson, we will focus on setting up the user interface (UI) elements for displaying the player’s
health in our game. Specifically, we will be working with heart icons that represent the player’s
health points. By the end of this lesson, you will have a clear understanding of how to manage and
update these UI elements based on the player’s health status.
To begin, we need to access the heart nodes that are children of the HealthContainer node. These
heart nodes will be stored in an array called hearts. This array will allow us to easily manage and
update the visibility of each heart icon based on the player’s current health.
In the _ready function, we will initialize the hearts array by retrieving all the child nodes of
HealthContainer. Here is the code snippet to achieve this:
hearts = health_container.get_children()
Next, we need to create a function called _update_hearts that will loop through each heart icon and
set its visibility based on the player’s health. This function will be called whenever the player’s
health changes.
The _update_hearts function uses a for loop to iterate through the hearts array. For each iteration, it
checks if the current index is less than the player’s health. If it is, the heart icon will be visible;
otherwise, it will be hidden.
Like the score text, to ensure that the heart icons are updated at the start of the game, we need to
call the _update_hearts function in the _ready function.
func _ready():
...
_update_hearts([Link])
Finally, we will test our implementation to ensure that the heart icons are correctly displayed based
on the player’s health. When the game starts, the player should have a certain number of hearts
visible. As the player takes damage, the number of visible hearts should decrease accordingly.
To test this, you can run the game and observe the heart icons in the UI. Take damage to see the
hearts disappear one by one until the player’s health reaches zero.
Summary
In this lesson, we have learned how to set up and manage UI elements for displaying the player’s
health in our game. We accessed the heart nodes, created a function to update their visibility based
on the player’s health, and connected the necessary signals to ensure the UI is updated in real-time.
This knowledge will be valuable as you continue to develop and refine your game’s user interface.
Welcome to this lesson! For the next step in our game, we will be creating an end flag for our game.
The end flag will be responsible for transitioning the player to the next level upon contact. This
process will be familiar to you, as we have previously created similar objects: the enemy and the
coin. Both of these objects share common characteristics with our end flag, which we will explore in
this article.
Before we dive into creating the end flag, let’s understand how it is similar to the enemy and coin
objects:
Given these similarities, it’s time you tackle a challenge! Try to set up the foundations of the End
Flag on your own. In the next lesson, we’ll go over the steps – but trying it yourself first will help
cement these skills for future projects!
In this lesson, we will create an EndFlag that the player can interact with to transition to a new
scene. This is a crucial element in games where the player needs to progress through different
levels. We will use Godot’s Area3D node and attach a script to handle the scene transition. Let’s
break down the steps:
First, we need to create a new node of type Area3D in our scene. This node will represent the
EndFlag that the player needs to reach.
Next, we need to add a collision shape to the EndFlag so that it can detect when the player enters its
area.
We will now attach a script to the EndFlag to handle the scene transition when the player enters its
area.
We need to connect the body_entered signal of the EndFlag node to the _on_body_entered function
in our script.
With all this done, we can now write the script – which will be quite simple. First, we set up a
PackedScene variable which will allow us to dynamically set the next level scene in the Inspector.
In our _on_body_entered function, we’ll check if the player entered the collision area. If so, we’ll call
our _load_new_scene function. Meanwhile, in the _load_new_scene function, we simply load the
scene set in our variable.
extends Area3D
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
_load_new_scene()
Finally, we will save the EndFlag as a scene so that it can be reused in different levels.
1. Save the EndFlag node as a scene named end_flag.tscn in the Scenes folder.
To test the EndFlag, we will temporarily position it next to the player and assign a scene to load
when the player reaches it.
By following these steps, you have created an EndFlag that the player can interact with to transition
to a new scene. This is a fundamental element in level-based games and will help you create a more
engaging and structured gameplay experience.
In this lesson, we will guide you through the process of setting up a second level in your game. By
the end of this tutorial, you will have a new level with a distinct visual style and functional scene
transitions. Let’s get started!
To expedite the setup process, we will duplicate the assets from Level 1:
1. Open level_1.tscn.
2. Select all the child nodes of the Main node.
3. Copy the selected nodes.
4. Go back to level_2.tscn and paste the copied nodes.
Connecting Levels
To ensure smooth transitions between levels, we need to connect the end flag in Level 1 to Level 2:
1. Open level_1.tscn.
2. Select the EndFlag node.
3. In the Scene to Load property, drag in level_2.tscn.
You might encounter errors when transitioning between scenes. This is due to attempting to unload
and load scenes in the same frame. To fix this, we will use the call_deferred method.
extends Area3D
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
call_deferred("_load_new_scene")
func _load_new_scene():
get_tree().change_scene_to_packed(scene_to_load)
This modification ensures that the scene transition happens at the end of the frame, preventing
errors caused by the physics.
Congratulations! You have successfully set up a second level in your game with a unique visual style
and functional scene transitions. In the next lessons, we will continue to build upon this foundation to
create an engaging and immersive gaming experience.
In this lesson, we will set up the menu scene for our game. The menu scene is the first scene that
players will see when they launch the game. It will include a background image, a title, and two
buttons: one for starting the game and another for quitting the game. Let’s go through the steps to
create this menu scene.
To begin, we need to create a new scene. This scene will be of type User Interface, which allows us
to create UI elements using control nodes.
The first element we need is a background image. We will use a TextureRect node for this purpose.
1. Add a TextureRect node as a child of the Menu node and rename it to Background.
2. In the FileSystem dock, navigate to the sprites folder and find the
Menu_Background.png file.
3. Drag the Menu_Background.png file onto the Texture property of the Background node.
To ensure the background image resizes with the screen, we need to set the anchor presets:
Next, we will add a title to the menu. The title will be a Label node.
1. Add a Label node as a child of the Menu node and rename it to Title.
2. Set the Text property of the Title node to “3D Platformer Game”.
3. Adjust the size of the label and move it to the center of the screen.
4. Set the Anchor Preset to Center to ensure it stays centered when the screen resizes.
5. Change the Horizontal Alignment and Vertical Alignment to Center.
6. Increase the Font Size to 38 and adjust the color as desired.
We will now add the play and quit buttons to the menu.
1. Add a Button node as a child of the Menu node and rename it to PlayButton.
2. Set the Text property of the PlayButton node to “Play”.
3. Adjust the size of the button and move it to the center of the screen.
4. Set the Anchor Preset to Center.
5. Duplicate the PlayButton node, rename the duplicate to QuitButton, and set its Text
property to “Quit”.
6. Move the QuitButton below the PlayButton.
To make the buttons functional, we need to connect them to a script. We will create a new script for
the Menu node.
In the [Link] script, we will add the following code to handle the button presses.
extends Control
func _on_play_button_pressed():
[Link] = 0
get_tree().change_scene_to_file("res://Scenes/level_1.tscn")
func _on_quit_button_pressed():
get_tree().quit()
As established, the play button will reset the score and load us into the first level. The quit button
will quit out of the game.
To ensure the game returns to the menu upon game over or level completion, we need to make a
few adjustments:
1. In the player_controller.gd script, modify the _game_over function to load the menu
scene:
2. In Level 2 (or whatever your last level is), select the EndFlag node and set the menu scene
as the scene to load.
With these steps, we have created a functional menu scene with play and quit buttons. The game
will now loop from the menu to the levels and back to the menu, providing a complete game flow.
In this lesson, we will set up audio for our game using Godot. Audio is a crucial element in games as
it enhances the player’s experience by providing feedback and immersion. We will focus on playing
sound effects when the player collects a coin and when the player takes damage.
To begin, we need to create a node that can play sound effects. Follow these steps:
Next, we need to modify the player controller script to handle audio playback. Open the
player_controller.gd script and make the following changes:
We will preload the audio clips for the coin sound effect and the take damage sound effect. This
ensures that the sound effects are loaded into memory when the node is initialized, reducing any
delay when the sounds are played:
We will create a function called _play_sound that takes an AudioStream as a parameter and plays it:
Finally, we will integrate the sound effects into the existing functions:
In the increase_score function, add the following line to play the coin sound effect:
_play_sound(coin_sfx)
In the take_damage function, add the following line to play the take damage sound effect:
_play_sound(take_damage_sfx)
After making these changes, press play to test the game. You should hear the coin sound effect
when collecting a coin and the take damage sound effect when taking damage.
By following these steps, you have successfully integrated audio into your game, enhancing the
player’s experience with sound effects. In the next lessons, we will continue to build on this
foundation to create a more immersive and engaging game.
In this lesson, we will implement a camera shake effect in Godot. This effect will trigger when the
player takes damage, enhancing the visual feedback of the game. We will achieve this by modifying
the camera’s offset properties to create a shaking effect. Let’s break down the steps to accomplish
this.
These offsets allow us to create a shaking effect by rapidly changing their values.
To start, create a new script on the Camera3D node and save it as camera_shake.gd in the scripts
folder. This script will manage the intensity of the shake and apply the offsets to the camera.
Inside the script, define an intensity variable that will control the magnitude of the shake:
extends Camera3D
Next, we need to connect the camera shake to the player’s damage signal. In the _ready function,
connect the _damage_shake function to the player’s OnTakeDamage signal:
func _ready():
get_parent().[Link](_damage_shake)
The _damage_shake function will set the intensity of the shake when the player takes damage:
In the _process function, we will gradually reduce the intensity of the shake over time using the lerp
function. We will also apply a random offset to the camera’s position:
func _process(delta):
if intensity > 0:
intensity = lerp(intensity, 0.0, delta * 10)
var offset : Vector2 = _get_random_offset()
h_offset = offset.x
v_offset = offset.y
The _get_random_offset function will generate random offsets for the camera based on the current
intensity:
Here is the complete camera_shake.gd script with all the functions implemented:
extends Camera3D
func _ready():
get_parent().[Link](_damage_shake)
func _process(delta):
if intensity > 0:
intensity = lerp(intensity, 0.0, delta * 10)
var offset : Vector2 = _get_random_offset()
h_offset = offset.x
v_offset = offset.y
Finally, we need to address an error that occurs when trying to change the scene during the game
over sequence. This error happens because the game is trying to unload an object while it is still in
To fix this, we will use the call_deferred function to ensure that the game over function is called at
the end of the frame. In the player_controller.gd script, modify the take_damage function as follows:
if health <= 0:
call_deferred("_game_over")
With these steps, you have successfully implemented a camera shake effect in Godot. This effect will
provide visual feedback to the player whenever they take damage, enhancing the overall gameplay
experience.
Welcome to this lesson on level design techniques! Whether you’re new to game development or
looking to enhance your skills, understanding effective level design is crucial. This article will guide
you through various techniques to help you create engaging and intuitive levels for your games.
Let’s dive in!
One of the fundamental aspects of level design is clearly communicating the objective to the player.
This can be achieved by ensuring that the end goal is visible from the start. By seeing the end goal,
players can plan their approach and understand what they need to do to complete the level.
In levels with multiple pathways, using collectibles like coins can guide the player’s attention. Coins
or other collectibles serve as breadcrumbs, leading players along a specific path. This technique is
commonly used in many games to direct player movement.
Placing the end goal at a higher elevation improves visibility and can serve as a storytelling device.
This technique helps players see the objective from a distance and can enhance the narrative by
creating a sense of progression.
Place the end goal at the highest point for better visibility.
Use elevation to create a sense of progression in the story.
Giving players options adds depth to your levels. While it’s important to have a clear path to the
objective, providing multiple routes with varying difficulties and rewards enhances player
engagement.
Theming plays a significant role in level design, contributing to the narrative and creating a unique
identity for each level. Even small changes in theming can make levels feel distinct and memorable.
For example:
Conclusion
Level design is a vast and complex field, but by applying these techniques, you can create engaging
and intuitive levels for your games. Remember, the best way to improve is to create, test, gather
feedback, and iterate on your designs.
Congratulations on completing your course on setting up a 3D platformer game using the Godot
engine! Throughout this project, we have created and integrated various elements to build a
functional and engaging game. Let’s recap the key components and concepts we’ve covered to
reinforce your learning.
Player Setup
We began by creating the player character, which is the core of our game. The player character can
move around and jump using various input methods such as:
Arrow keys
WASD keys
Spacebar
Customizable controller or mouse buttons
This flexibility was achieved using Godot’s input map system, which allowed us to assign different
keys or buttons for player movements.
We then created a script utilizing the CharacterBody3D node to manage the player’s mechanics.
Here’s what we set up:
1. Gravity: Ensured the player is affected by gravity, allowing for realistic jumping and falling.
2. Movement: Implemented player movement based on input directions.
3. Jumping: Enabled the player to jump and control their character in the air.
Collectibles and UI
We also created collectible coins that provide rewards and update the UI score. Key features of the
coins include:
Level Transition
To create a full game loop, we set up an end flag that serves as a portal between levels. This allows
us to connect:
1. Level 1
2. Level 2
3. The main menu
This structure enables players to progress through the game and restart it after completion.
About Zenva
Zenva is an online learning academy with over a million students. We offer a wide range of courses
suitable for both beginners and those looking to learn new skills. Our courses are versatile, allowing
you to learn in various ways:
Thank you for following along with the course. We wish you the best of luck with your future Godot
games and hope to see you in more courses!
In this lesson, you can find the full source code for the project used within the course. Feel free to
use this lesson as needed – whether to help you debug your code, use it as a reference later, or
expand the project to practice your skills!
You can also download all project files from the Course Files tab via the project files or
downloadable PDF summary.
camera_shake.gd
This script extends the Camera3D class and adds a damage shake effect. When the parent node
takes damage, the `_damage_shake` function is called, setting the intensity of the shake effect. The
`_process` function then gradually reduces the intensity and applies a random offset to the camera’s
position, creating a shaking effect.
extends Camera3D
[Link]
This script rotates a coin object around its Y axis at a specified speed and makes it bob up and down
at a specified height and speed. The coin’s position is updated every frame to achieve this effect.
When another object in the “Player” group collides with this coin object, the player’s score is
increased by 1 and the coin is removed from the scene.
extends Area3D
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
body.increase_score(1)
queue_free()
end_flag.gd
This script for the End Flag extends the Area3D class and listens for a body to enter its area. When a
body enters and is part of the “Player” group, it triggers a scene change. The new scene is loaded
from a PackedScene resource assigned to the `scene_to_load` variable.
extends Area3D
func _on_body_entered(body):
if not body.is_in_group("Player"):
return
call_deferred("_load_new_scene")
[Link]
This script controls the movement and rotation of the enemy. It moves the enemy object back and
forth along a set direction at a specified speed, while also spinning it around its z-axis. When the
enemy collides with the player, it deals damage to the player.
extends Area3D
if global_position == start_pos:
target_pos = start_pos + move_direction
elif global_position == start_pos + move_direction:
target_pos = start_pos
body.take_damage(1)
[Link]
This code handles the behavior of two buttons in the menu scene. When the play button is pressed,
it resets the player’s score to 0 and changes the scene to the first level. When the quit button is
pressed, it exits the game.
extends Control
func _on_play_button_pressed():
[Link] = 0
get_tree().change_scene_to_file("res://Scenes/level_1.tscn")
func _on_quit_button_pressed():
get_tree().quit()
player_controller.gd
This script controls the player character’s movement, health, and scoring. It applies gravity, allows
jumping and movement, and updates the player’s health and score based on external inputs. The
script also handles game over conditions and plays sound effects for taking damage and collecting
coins.
extends CharacterBody3D
func _physics_process(delta):
# gravity
velocity.y -= gravity * delta
# jump
if Input.is_action_pressed("jump") and is_on_floor():
velocity.y = jump_force
# movement
var move_input : Vector2 = -Input.get_vector("move_right", "move_left", "move_back",
"move_forward")
var move_dir : Vector3 = Vector3(move_input.x, 0, move_input.y)
move_and_slide()
if health <= 0:
call_deferred("_game_over")
[Link] = sound
[Link]()
player_stats.gd
This singleton code declares a singleton variable named “score” with a data type of integer and
initializes it to 0. This variable can be used to store and manage a score value within the class and
maintain it across multiple scenes.
extends Node
player_ui.gd
This code manages the display of a player’s health and score. It initializes a container with hearts
representing the player’s health and a label for the score, and updates them when the player takes
damage or earns points. The hearts are shown or hidden based on the player’s current health, and
the score label is updated with the current score.
extends CanvasLayer
[Link](_update_hearts)
[Link](_update_score_text)
_update_hearts([Link])
_update_score_text([Link])
player_visual.gd
This script rotates the player to face the direction of movement and smoothly transitions between
rotations. It also applies a subtle bobbing effect to the mesh when the player is moving on the floor,
creating a sense of movement. The rotation and bobbing effects are updated every frame based on
the player’s velocity and position.
extends MeshInstance3D
if vel.x != 0 or vel.z != 0:
target_y_rot = atan2(vel.x, vel.z)