Quantcast
Channel: GameDev Academy
Viewing all articles
Browse latest Browse all 351

How to Make a Platformer in Defold

$
0
0

You can access the full course here: BUILD A COMPLETE 2D PLATFORMER IN DEFOLD

Introduction

Learn to create a pulse-pounding Defold platformer that will take your players to new heights!

In this guide, we’ll walk you through the process of setting up input bindings, creating a player game object, and scripting essential player movements for a Defold platformer. With the steps provided, you’ll gain a foundational understanding of using Defold to build interactive and responsive gameplay experiences – and get started on a good first Defold project.

Whether you’re entirely new to Defold or already familiar with other game development tools, this tutorial is designed to help you get hands-on with key concepts in a streamlined and approachable manner. To ensure a smooth learning process, it’s recommended that you have some prior experience with Lua scripting and basic game design principles. You can explore our full course, Build a Complete 2D Platformer in Defold which covers these concepts in-depth.

Project Files

To make following along easier, we’ve included the assets used throughout this tutorial. These resources will provide a starting point as you work through the Defold platformer tutorial step by step. You can download them here: Defold Platformer Assets

CTA Small Image - How to Make a Platformer in Defold
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Input Bindings

In this first part of our Defold platformer tutorial, we will be setting up input bindings for a game project. Input bindings are essential for defining which actions on our keyboard, mouse buttons, or gamepad triggers will execute certain actions in our game. For instance, if we want our character to jump, we need to assign a specific button for the jump action. The same applies to actions like moving left or right.

Accessing Input Bindings

To access our game input bindings, navigate to the input folder in our project. Here, you’ll find a file named game.inputbinding. Double-click on this file to open it up for editing. The file contains various triggers for keyboard, mouse, gamepad, touch, and text. For the purpose of this tutorial, we will focus solely on keyboard triggers.

Setting Up Keyboard Triggers

For keyboard triggers, we have two components: input and action. The input refers to the specific key that will be pressed, while the action is the name of the action that will be triggered upon pressing the key. We can listen for this action name in our script and run code according to the action being pressed.

Let’s set up our first action:

  1. Click on the plus button to create a new action.
  2. Double-click on the name field and enter “move_left”. This action will move our character to the left.
  3. Since we don’t want this action to be assigned to the space bar, double-click on space to open up a dropdown menu.
  4. Scroll down and select the left key. Now, whenever the left arrow key is pressed, the “move_left” action will be triggered.

Follow the same steps to create a “move_right” action assigned to the right arrow key and a “jump” action assigned to the space bar.

Adding More Actions

Our game currently only includes the actions “move_left”, “move_right”, and “jump”. However, you can add more actions, such as a sprint button or a button to open a pause menu. Simply follow the steps outlined above to add these new inputs to your game.

Besides keyboard triggers, you can also add mouse triggers, gamepad triggers, and touch triggers. Gamepad triggers are useful for controller inputs, while touch triggers are ideal for mobile games.

Player Game Object

In this next part of our Defold platformer tutorial, we will learn how to set up a player game object in a game development environment. This process is essential as it allows us to create a player character that can interact with the game world, and can be used across different levels in the game.

Creating a Player Game Object in the Asset Pane

The first step in setting up a player game object is to create it in the asset pane of your game development environment, rather than in the level one collection. The reason for this is that you want to have the exact same player game object behave in the same way across all levels. If you were to build the player game object in the level one collection and wanted to change something about the player, you would have to go to every single level and adjust that player game object there. This could get quite convoluted, especially when you start adding in other elements like coins and enemies.

Instead, you can create a game object that is saved to your game files, which you can then spawn inside of your game. Whenever you make a change to that source game object, all of the instances that you create will be updated with those changes.

To create a player game object, follow these steps:

  1. Go to the game folder inside the assets pane and locate the player folder.
  2. Right-click on the player folder, go to ‘New’, and select ‘game object’.
  3. Name this game object ‘player’.

Constructing the Player Game Object

Once you’ve created the player game object, you can begin to construct its components. The player game object will have several components, including sprites, collision objects, and a script for controlling its movement.

  • Sprites: This component is used to give the player a visual representation in the game. To add a sprite, right-click on the game object, select ‘Add component’, and choose ‘Sprite’. You will then need to select a default animation for the sprite.
  • Collision Objects: This component allows the player to interact with the surfaces of the game world. To add a collision object, right-click on the game object, select ‘Add component’, and choose ‘Collision Object’. You will need to adjust the properties of the collision object to suit your game’s needs.
  • Player Script: This component controls the player’s movements. To add a player script, right-click on the player folder, select ‘New’, and choose ‘Script’. Name this script ‘Player’.

Adding the Player Game Object to the Game Level

Once you’ve constructed the player game object, you can add it to your game level. To do this, right-click on the collection in the outline pane, select ‘Add game object file’, and choose your player game object. You can then position the player game object in the game level as desired.

Any changes you make to the player game object in the asset pane will be applied to the instance of the player in the game level. This means that if you adjust any of the settings on the player game object, those settings will be applied to every instance of the player in every level of the game. This is a very efficient way of managing game objects that need to be used multiple times in the game.

Want to take your Defold skills to the next level? Our Defold Game Development Academy offers exclusive access to expert-created courses, in-depth videos, and hands-on projects to help you build your portfolio.

Player Script – Variables

In this next part of our Defold platformer tutorial, we’ll begin setting up our player GameObjects script. We’ll start by opening our player script and removing any unnecessary functions. For our player script, we’ll focus on four callback functions: init, fixed update, on message, and on input.

Defining Variables

Before we start creating our functions and writing code, let’s define some constant variables that we’ll use for our player. These variables will remain the same across all instances of our players, which is why we’re defining them outside of our function. Here are the variables that we’ll define:

  • move_speed: This is the speed at which our player will move. For our purposes, we’ll set it to 100 pixels per second.
  • gravity: This represents the force that will be applied to our player. We’ll set this to -500, indicating that the force will move the player downwards.
  • max_fall_speed: This is the maximum speed at which our player can fall. We’ll set this to 500 pixels per second.
  • jump_speed: This is the force we’ll apply upwards when our player jumps. We’ll set this to 300 pixels.
  • normal_threshold: This variable will be used when we’re detecting collisions. We’ll explore this in more detail later.

Pre-Hashing Variables

We’ll also define some variables that will pre-hash a number of different things. Hashing is used when we want to detect inputs or check if a certain message is of a certain type. However, hashing can cause performance issues if done frequently or in large quantities, so it’s good practice to pre-hash these variables. Here are the variables that we’ll pre-hash:

  • moveLeftInput: This hashes the string ‘move_left’.
  • moveRightInput: This hashes the string ‘move_right’.
  • jump: This hashes the string ‘jump’.
  • idleAnim, moveAnim, jumpAnim: These variables hash their respective animation names.
  • contact_point_response: This hashes the string ‘contact_point_response’, which is used to check if a collider has hit another collider.
  • ground_layer: This hashes the string ‘ground’, used to check if the object hit belongs to the ground layer.

Instance Variables

Next, we’ll define some instance variables using the self keyword in our init function. These include:

  • spawn_position: This is the position at which our player spawns. We’ll get this by calling go.getPosition.
  • velocity: This represents the speed of the player in its direction. We’ll initially set this to an empty Vector3 object.
  • facing_direction: This indicates whether the player is facing left or right. We’ll set this to 1 by default.
  • correction: This variable will be used for collision detection to prevent our player from clipping inside any colliders. We’ll set this to an empty Vector3 object.
  • ground_contact: This indicates whether or not our player is standing on the ground. We’ll set this to true by default.
  • anim: This represents the current animation that’s playing. We’ll initially set this to nil.
  • score: This represents the player’s score. We’ll set this to 0 initially.

With these variables defined, we’re ready to start defining our functions and writing the logic to move our player around.

Code Snippet

Here’s what the code looks like with our defined variables:

-- constant variables
local MOVE_SPEED = 100
local GRAVITY = -500
local MAX_FALL_SPEED = 500
local JUMP_SPEED = 300
local NORMAL_THRESHOLD = 0.8

-- pre-hashing
local MOVE_LEFT_INPUT = hash("move_left")
local MOVE_RIGHT_INPUT = hash("move_right")
local JUMP_INPUT = hash("jump")

local IDLE_ANIM = hash("player_idle")
local MOVE_ANIM = hash("player_move")
local JUMP_ANIM = hash("player_jump")

local CONTACT_POINT_RESPONSE = hash("contact_point_response")
local GROUND_LAYER = hash("ground")

-- instance variables
function init(self)
  self.spawn_position = go.get_position()
  self. Velocity = vmath.vector3()
  self.facing_direction = 1
  self.correction = vmath.vector3()
  self.ground_contact = true
  self.anim = nil
  self.score = 0
end

Player Script – Movement

For this next part of our Defold platformer tutorial, we will be setting up the ability for our player controller to move around in our game. The first thing we need to do is detect inputs from our keyboard and then perform actions based on those inputs. Before we start coding our input function, we need to define our script as one that is meant to receive input. This is done manually in our init function.

Defining the Script to Receive Input

To define the script to receive input, we will use the init function. The init function is called at the start of the game when this component is initialized. We will send a message to our game object to start listening for inputs. Here’s how we do that:

function init(self)
    msg.post(".", "acquire_input_focus")
    ...
end

The msg.post function is used to send a message. The first argument is the receiver of the message, in this case, our own game object (denoted by “.”). The second argument is the message id, which is “acquire_input_focus” in this case. This tells our game object to start listening for inputs.

Listening for Inputs

Now that our game object is listening for inputs, we can go to our onInput function and start receiving those inputs. We want to be able to move left and right. To do this, we will listen for the action ID and check if it matches the move left or move right input binding that we have set up previously.

We have pre-hashed these values for performance reasons. This means that instead of checking if the action ID is hash("move_left"), we just check if the action ID is move_left_input. We use the variable instead of hashing it each time.

Here is how we can set up our onInput function:

function on_input(self, action_id, action)
    if action_id == move_left_input then
        -- move left
    elseif action_id == move_right_input then
        -- move right
    end
end

As you can see, we are checking if the action ID is equal to our predefined move_left_input or move_right_input. If it is, we then call a move function to move our character in the appropriate direction. We haven’t created our move function yet, so we will do that next.

Creating the Move Function

The move function will be responsible for moving our player left or right. It will take two parameters – self and direction. The direction parameter will be a number from -1 to 1. -1 will be used to move left and 1 will be used to move right.

Here is how we can set up our move function:

local function move(self, direction)
    self.velocity.x = MOVE_SPEED * direction
    ...
end

Inside the move function, we are setting the x-axis of our velocity variable to be our move speed multiplied by direction. This determines how fast and in which direction we are moving.

We also want to set our facing direction inside the move function. This will be used for flipping our sprite later on, so it can look in the direction it’s moving. We only want to change our facing direction if the given direction parameter is not zero. Here’s how we do that:

local function move(self, direction)
    self.velocity.x = MOVE_SPEED * direction

    if direction ~= 0 then
        self.facing_direction = direction
    end
end

Now that we have our move function, we can go back to our onInput function and call this move function when the left or right input is detected:

function on_input(self, action_id, action)
    if action_id == move_left_input then
        move(self, -action.value)
    elseif action_id == move_right_input then
        move(self, action.value)
    end
end

Moving the Character

Now that we have set up our move function and are calling it when we detect the appropriate input, we need to actually move our character. As of now, if we build and run our game, pressing the left or right arrow keys won’t do anything. This is because we are only adjusting the velocity variable, not actually moving our character.

To move our character, we will modify our fixed_update function. This function is called every fixed frame, and we will use it to apply our velocity to our character’s position. Here’s how we can do that:

function fixed_update(self, dt)
    -- Get current position
    local pos = go.get_position()

    -- Modify position with velocity
    pos = pos + self.velocity * dt

    -- Apply new position
    go.set_position(pos)
end

Now, if we build and run our game, our character should be able to move left and right when we press the appropriate arrow keys!

Defold Platformer Wrap-Up

Congratulations on completing this Defold platformer tutorial! By following along, you’ve successfully learned how to set up input bindings, create reusable player game objects, and implement movement logic using Lua scripts. These skills form the backbone of many game development projects, providing you with a strong foundation to expand your knowledge further.

Of course, this is only the beginning! Feel free to build upon what you’ve created by adding additional player actions, designing complex environments, or integrating advanced game mechanics like AI or physics. The possibilities are endless with Defold’s versatile tools and scripting capabilities. Plus, with great resources such as Zenva’s Defold Game Development Academy (which offers a comprehensive learning pathway on Defold), it’s easier than ever to jump into game development.

We hope you’ve enjoyed this Defold platformer guide and wish you all the best in your game development journey!

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image - How to Make a Platformer in Defold

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

Transcript – Input Bindings

Welcome back, everyone.

In this lesson, we are going to be setting up our input bindings for our game project. Input bindings are essential for defining which actions on our keyboard, mouse, or gamepad triggers correspond to specific actions in the game. For example, if we want our character to jump, we need to specify which button will trigger the jump action. The same applies to moving left and right.

To set up the input bindings, navigate to the input folder in your project and double-click on the game.inputbinding file. This opens a new interface where you can configure the input bindings. Here, you’ll see triggers for various input devices, including keyboard, mouse, gamepad, touch, and text. For this project, we’ll focus on keyboard triggers since our game is a platformer and doesn’t require mouse input. If you want to include gamepad support, you can set that up similarly to how we configure the keyboard.

Keyboard input bindings consist of two elements: the input and the action. The input represents the specific key pressed, while the action is the name of the behavior triggered by that key. Using these action names, we can write scripts to define what happens when a particular key is pressed.

Let’s create our first action by clicking the plus button. Name this action move_left. Next, assign it to the left arrow key by double-clicking the current key (default is often “space”), opening the dropdown menu, and selecting the left arrow key. This means pressing the left arrow key will trigger the move_left action.

Repeat the process for move_right, assigning it to the right arrow key. Finally, add another action for jump and assign it to the space bar. These are the basic actions needed for our game: moving left, moving right, and jumping.

Our input bindings are now ready. Although this project includes only a few actions, you can add more if desired. For instance, you might want a sprint button, a pause menu (triggered by pressing escape), or other interactions. Additionally, you can configure mouse and gamepad triggers. Default, the game engine used here, also supports touch triggers, making it well-suited for mobile games.

In the next lesson, we’ll start setting up the environment for our game. Before adding players, enemies, and coins, we’ll create a tile map to design an engaging and functional game world. Thanks for watching, and see you in the next lesson!

Transcript – Player Game Object

Hey everyone, in this lesson, we are going to begin setting up our player game object.

Now, the first thing that we are going to do is not go into our level one collection that we are in right now, go to the outline, right click, create a game object called player and build it here. But rather, we are going to be building our player’s game object inside of the asset pane here. We are going to construct it here.

The reason why is because across our levels, we want to have the exact same player game object behave in the exact same way. Now, we could do that by copying and pasting our player from level 1 to level 2 and so on, but if we wanted to change something about the player, we would have to go to every single level and adjust that player game object there. You can see how it can get quite convoluted, especially when we start looking at adding in coins and enemies.

If we want to adjust one of them, we’d have to go through every single level and adjust all of the different instances. That would just be way too much work.

What we can do is create a game object that is saved to our game files, which we can then spawn inside of our game. Whenever we make a change to that source game object, all of the instances that we create will be updated with those changes.

Inside of our assets pane, we are going to go down to our game folder and inside of game, we’ll go to player. Then, right click on player, go over to new, and find where it says game object. This game object will be named “player.”

Now, we have opened up our player game object inside of its own individual window. This is where we can construct its components. Then we can drag this player game object into our level, and it will basically act as a normal game object.

First, we need to attach all of our player’s components. The player will have quite a few. Let’s start with the sprite so we can actually see what our player looks like. Right-click on the game object, go to add component, and then select sprite. For the sprite, go to the image property, click the three dots, and select our tile map character’s tile source.

We still don’t see anything, and that’s because we need to choose a default animation. In the dropdown, we have those animations created previously. Select “player idle,” and that creates our player sprite. We can switch this to the move animation and preview it, but I’ll set it back to idle by default. Now we have our player visual.

The next step is adding a collider so our player can land and walk around. Right-click on the game object, go to add component, and then select collision object. For this collision object, go to the properties and change the type from dynamic to kinematic. This allows us to control things like gravity and friction manually in the script.

Go to the bottom where it says group and change it from default to player. This will act as the player’s physics group or layer. For our player’s mask, we want it to interact with the ground, enemy, end flag, and coin.

Next, we’ll assign a collision shape. Right-click on the collision object component, add shape, and select sphere. Set the diameter to about 24 to fit the bounds of our player, ensuring the collider touches the player’s feet.

Finally, we’ll add a player script to control its movement. Inside the player folder where we created the game object, right-click on the folder, select new, and create a script called “Player.” Attach this script to the player game object by adding it as a component.

At this point, we have the player’s three main components: a sprite for visuals, a collision object for surface interactions, and a script for movement control. To add the player to our level, go to the outline on the right, right-click on the collection, and add the player game object file. Position it as needed in the level.

Now, if we adjust any settings on the root player game object file, those changes will be reflected across all levels where it’s used. This approach ensures consistency and simplifies updates for the player and other game objects like coins, enemies, and end flags.

Press play or use Control + B to preview the game, and you’ll see the player in action. In the next lesson, we’ll script the player to start moving. Thanks for watching, and I’ll see you all then!

Transcript – Player Script – Variables

Hey everyone, in this lesson, we are going to begin setting up our player GameObject script.

We’ll start by opening our player script and removing functions we don’t need. The init function will remain, but we’ll remove unnecessary comments. The final function won’t be needed, so we can delete it. We won’t need the update function, but we’ll keep fixed update. This function, called a fixed number of times per second, is where we handle physics calculations, such as moving the player.

We’ll keep the on message function to detect and send messages, and the on input function to listen for inputs from the keyboard, mouse, or controllers. The on reload function is not needed, so we’ll delete it. These four callback functions will be the primary ones we use in our player script. Many more custom functions will be created later, but these are the ones we’ll use for now.

Before writing our functions, we’ll define a few variables. These will be constant variables, meaning they will remain the same across all player instances. That’s why we define them outside of any function.

First, we define move_speed and set it to 100 pixels per second. Then we add gravity, which will be -500 to represent downward force. To prevent infinite acceleration, we set a max_fall_speed of 500 pixels per second. Next, we define jump_speed as 300 pixels and introduce a normal_threshold for collision detection, which we’ll explain further later.

We’ll also pre-hash variables for inputs, animations, and messages. Pre-hashing reduces performance issues by converting strings into hashes during script initialization rather than repeatedly doing so at runtime. For inputs, we hash strings like move left, move right, and jump. For animations, we hash names like player idle, player move, and player jump. Finally, for messages, we hash strings like contact point response and ground layer.

Moving to the init function, we’ll define instance variables using the self keyword. We create spawnPosition to store the player’s initial position, using go.getPosition. Variables such as velocity, facingDirection, and correction are initialized to handle movement, facing direction, and collision correction, respectively. We also add groundContact to check if the player is standing on the ground, anim for the current animation, and score, initialized to zero.

The capitalized variables are constants, shared across all player instances (e.g., move speed, gravity). Meanwhile, instance-specific variables like spawnPosition may differ between players or levels. These are dynamic and will change as the game progresses.

With the variables set up, the next step is defining functions and implementing the logic for player movement. Thanks for watching, and I’ll see you all in the next lesson!

Transcript – Player Script – Movement

Hey everyone, in this lesson, we are going to begin setting up the ability for us to move around with our player controller here. The first thing we need to do is be able to detect inputs from our keyboard and then do stuff based on that.

Before we go down to the bottom inside of the onInput function and enter in our code, we need to do something because at the moment onInput is not going to be receiving any calls. The reason is that we have not strictly defined this script as being one that is to receive input. We have to do that manually.

To do that, we’re going to go to our init function, which gets called at the start of the game when this component is initialized. What we’re going to do is send a message to ourselves. This message will go msg.post. The receiver is going to be us. To send a message to our own game object, we just have the receiver as a single full stop, and the message ID is going to be acquire_input_focus. Exactly like this. So we are basically telling our game object that it is going to begin listening for inputs.

Now that we have done that, we should be able to go down to onInput and receive those inputs. We want to be able to move left and move right. To do this, we are going to be listening for the action ID and checking to see if that action ID is the move left or move right input binding that we have set up previously. We’ve already pre-hashed these values, meaning that instead of checking to see if the action ID is hash move left, we just need to check to see if the action ID is move_left_input. Since we’re pre-hashing it, we use the variable instead of hashing it each time.

What we’re going to do is check if action ID (which carries the name of the input) is equal to our move left input. If so, we want to move left. For now, we’ll add a comment for moving left. Otherwise, if the action ID equals the move right input, we want to move right. This structure allows us to handle both inputs.

Next, we need a function to handle the actual movement. We’ll call it move. The function takes two parameters: self and direction. This function will be used to move left or right, where the direction is a number from -1 to 1 (negative for left and positive for right). Inside, we set the X direction of our velocity variable to move_speed multiplied by direction. The move speed is defined as 100 by default but can be adjusted later.

Now we need to modify the onInput function to call the move function. We’ll call move(self, -action.value) for moving left and move(self, action.value) for moving right. Using action.value allows for finer adjustments for future controller inputs.

One additional feature in the move function is setting the facing_direction. This helps flip the sprite later to face the correct direction. We only update the facing_direction if direction is not zero to prevent bugs.

After implementing the move function, we need to apply the velocity to our position. Inside the fixed_update function, we get the current position, modify it by adding velocity multiplied by delta time (to ensure consistent movement), and then set the new position.

Finally, when we build the project and test, the character should move left and right as intended. However, it can still pass through obstacles, which we will address in the next lesson by setting up gravity and collision.

Thanks for watching, and I’ll see you all then.

Interested in continuing?  Check out our all-access plan which includes 300+ courses, guided learning pathways, new courses monthly, and more!


Viewing all articles
Browse latest Browse all 351

Trending Articles