Difference between revisions of "Tutorial:Baseline 2D Platformer"

m (Comment grammar and some misc 0.10.0 fixes)
m (update color for newest love version)
 
(9 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{notice|This tutorial is incomplete. Do you want to [{{fullurl:{{FULLPAGENAME}}|action=edit}} complete it]?}}
+
== Overview ==
Due to a request on IRC, this guide has been written to show a basic implementation of platformer-style jumping physics for a player character.
 
  
The tutorial is fairly basic and even a newbie should have no issue understanding.
+
[[#Part 1: The Platform|Part 1: The Platform]]<br>
 +
[[#Part 2: The Player|Part 2: The Player]]<br>
 +
[[#Part 3: Player Movement|Part 3: Player Movement]]<br>
 +
[[#Part 4: Jumping|Part 4: Jumping]]<br>
 +
[[#Part 5: The Whole Code|Part 5: The Whole Code]]
  
First, let's start off with our basic code, without any jumping.
+
In this tutorial, we will be creating a "Baseline 2D Platformer". In this context, "baseline" means the basic functions. Therefore, in a baseline 2d platformer, you can see or experience the following:
 +
* The platform for the player to move around on.
 +
* The player avatar/character.
 +
* The player's ability to move left and right.
 +
* The player's ability to jump around.
 +
 
 +
== Part 1: The Platform ==
 +
 
 +
First, let's create the platform. For tutorial purposes, the platform is a white rectangle that will encompass the bottom-half part of the game window.
  
 
<source lang="lua">
 
<source lang="lua">
 +
platform = {}
 +
 
function love.load()
 
function love.load()
  -- Nice and organised
+
        -- This is the height and the width of the platform.
  player = {
+
platform.width = love.graphics.getWidth()    -- This makes the platform as wide as the whole game window.
    x = 0,
+
platform.height = love.graphics.getHeight()  -- This makes the platform as tall as the whole game window.
    y = 0,
+
       
   
+
        -- This is the coordinates where the platform will be rendered.
    image = love.graphics.newImage("hamster.png") -- Let's just re-use this sprite
+
platform.x = 0                               -- This starts drawing the platform at the left edge of the game window.
  }
+
platform.y = platform.height / 2            -- This starts drawing the platform at the very middle of the game window
 +
end
 +
 
 +
function love.update(dt)
  
  winW, winH = love.graphics.getWidth(), love.graphics.getHeight() -- This is just so we can draw it in a fabulous manner
 
 
end
 
end
  
 
function love.draw()
 
function love.draw()
  love.graphics.rectangle("fill", 0, winH / 2, winW, winH / 2)
+
love.graphics.setColor(1, 1, 1)       -- This sets the platform color to white.
  love.graphics.translate(winW / 2, winH / 2) -- You don't need to understand this
 
  
  love.graphics.draw(player.image, player.x, -player.y, 0, 1, 1, 64, 103) -- Trust me on the origin position. Just trust me
+
        -- The platform will now be drawn as a white rectangle while taking in the variables we declared above.
 +
love.graphics.rectangle('fill', platform.x, platform.y, platform.width, platform.height)
 
end
 
end
 
</source>
 
</source>
:Note: The hamster.png file is [[:File:Resource-HamsterBall.png]].
 
  
You should get something that looks like this:
+
So for will have something that looks like this:
 +
 
 +
[[File:Part_1_The_Platform.PNG]]
 +
 
 +
Nice! Now we have a simple white platform!
 +
 
 +
== Part 2: The Player ==
  
[[Image:PlatformerJumpingStepOne.jpg|thumb]]
+
Next will be the player character. For tutorial purposes, the player character will be a 32x32 purple block. This time, instead of using the [[love.graphics.rectangle]], we will use [[love.graphics.draw]]. The reason for this is because the purple block would be an external file. This way, you can learn how to draw an image "internally" as well as how to draw an "external" image. You can get the purple block [[:File:purple.png|here]].
  
Now, it's time to make it jump. To do this, we'll give the player an upwards velocity, create a gravity variable and a jump height variable.
+
:Note: Just as a quick reference, [[love.graphics.rectangle]] lets you draw a rectangle in the game window. We can consider this as a form of "internal-file rendering" (not the exact or proper term) since we are declaring its (possible) parameters in the game's code. On the other hand, [[love.graphics.draw]] lets you draw an image in the window - as long as the file type is supported. We can consider this as a form of "external-file rendering" (again, not the exact or proper term) since the image we are rendering is an external file being called by the game's code.
  
Add this to your love.load function:
 
 
<source lang="lua">
 
<source lang="lua">
player = {
+
...
  ...
+
player = {}  -- Add this below the platform variable
   y_velocity = 0,
+
 
  ...
+
function love.load()
}
+
...
 +
        -- Add this below the platform variables.
 +
 
 +
        -- This is the coordinates where the player character will be rendered.
 +
player.x = love.graphics.getWidth() / 2   -- This sets the player at the middle of the screen based on the width of the game window.
 +
player.y = love.graphics.getHeight() / 2  -- This sets the player at the middle of the screen based on the height of the game window.
 +
 
 +
        -- This calls the file named "purple.png" and puts it in the variable called player.img.
 +
player.img = love.graphics.newImage('purple.png')
 +
end
 +
 
 +
function love.update(dt)
 +
 
 +
end
  
...
+
function love.draw()
gravity = 400
+
...
jump_height = 300
+
        -- Add this below the love.graphics.rectangle line.
...
+
       
 +
        -- This draws the player.
 +
love.graphics.draw(player.img, player.x, player.y, 0, 1, 1, 0, 32)
 +
end
 
</source>
 
</source>
  
When the player presses the jump button (spacebar for this tutorial) we will increase the x_velocity by the jump_height.
+
Now we have something that looks like this:
 +
 
 +
[[File:Part_2_The_Player_Character.PNG]]
 +
 
 +
Alright! Now the player exists!
 +
 
 +
== Part 3: Player Movement ==
 +
 
 +
We have the platform, we have the player! Time to make this guy move. Remember that in 2d platformers, the player '''must be able to move left and right!''' We can't just have the player move forward to the right forever. To do this, we need to declare the speed the of the player's movement as well as assign keyboard inputs so that when the player presses a certain button, the character will move left or right depending on what is coded.
  
Then, we will move the player upwards while decreasing the velocity by the gravity. We'll add a love.update and love.keypressed callback to make this work.
+
<source lang="lua">
 +
function love.load()
 +
...
 +
player.speed = 200    -- This is the player's speed. This value can be change based on your liking.
 +
end
 +
 
 +
function love.update(dt)
 +
        -- This is how to assign keyboard inputs.
 +
       
 +
if love.keyboard.isDown('d') then                    -- When the player presses and holds down the "D" button:
 +
player.x = player.x + (player.speed * dt)    -- The player moves to the right.
 +
elseif love.keyboard.isDown('a') then                -- When the player presses and holds down the "A" button:
 +
player.x = player.x - (player.speed * dt)    -- The player moves to the left.
 +
end
 +
end
 +
</source>
 +
 
 +
If you run the code, the player can now move left and right! '''HOWEVER!''' There is a problem. If you haven't noticed by now, if you keep on moving the character to the right (or to the left), it will go pass the game window and be out of the player's vision. For tutorial purposes, we don't want that. So inside the love.update() function:
  
 
<source lang="lua">
 
<source lang="lua">
 
function love.update(dt)
 
function love.update(dt)
  if player.y_velocity ~= 0 then -- We're probably jumping
+
if love.keyboard.isDown('d') then
    player.y = player.y + player.y_velocity * dt -- "dt" means we wont move at different speeds if the game lags
+
-- This makes sure that the character doesn't go pass the game window's right edge.
    player.y_velocity = player.y_velocity - gravity * dt
+
if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
+
player.x = player.x + (player.speed * dt)
    if player.y < 0 then -- We hit the ground again
+
end
      player.y_velocity = 0
+
elseif love.keyboard.isDown('a') then
      player.y = 0
+
-- This makes sure that the character doesn't go pass the game window's left edge.
    end
+
if player.x > 0 then
  end
+
player.x = player.x - (player.speed * dt)
 +
end
 +
end
 
end
 
end
 +
</source>
 +
 +
[[File:Part_3-_Player_Movement.gif]]
  
function love.keypressed(key, scancode, isrepeat)
+
Now the player can't get pass the screen. This way the player character will always be within the constraints of the game window.
  if key == "space" then
+
 
     if player.y_velocity == 0 then -- We're probably on the ground, let's jump
+
== Part 4: Jumping ==
      player.y_velocity = jump_height
+
All that's left now is jumping. You can't have a 2d platformer without jumping. Without it, player's can't really get over obstacles like pits or walls. It's time for some basic physics. For something to jump and fall, an object (in this case, the player's character) needs to have a Y-Axis Velocity, a Jump Height, and Gravity. For tutorial purposes, we will not be taking into consideration an object's mass however it is worth mentioning that giving mass to an object can change the way it's physics works. First, let's declare the three things we've mentioned earlier. I will also be adding a variable called "ground". This is to indicate where the ground is. Think of it as the place where the feet should touch and land after jumping.
    end
+
 
  end
+
<source lang="lua">
 +
function love.load()
 +
...
 +
        -- Add this below the player.img
 +
player.ground = player.y     -- This makes the character land on the plaform.
 +
 
 +
player.y_velocity = 0       -- Whenever the character hasn't jumped yet, the Y-Axis velocity is always at 0.
 +
 
 +
player.jump_height = -300    -- Whenever the character jumps, he can reach this height.
 +
player.gravity = -500        -- Whenever the character falls, he will descend at this rate.
 
end
 
end
 
</source>
 
</source>
 +
:Note: Feel free to change the values of player.jump_height and player.gravity to your liking. Just remember that for the most part, unless you want the game's physics to not work the usual way, player.gravity > player.jump_height
 +
 +
After declaring the variables, we will now proceed to making the character jump. To do this, we need to assign a key that will make the character jump.
 +
 +
<source lang="lua">
 +
...
 +
function love.update(dt)
 +
...
 +
        -- Add below the right key assignment.
  
Yay, jumping!
+
        -- This is in charge of player jumping.
 +
if love.keyboard.isDown('space') then                    -- Whenever the player presses or holds down the Spacebar:
  
However, this is not true platformer-style jumping. In a lot of platformers, holding the jump key makes you jump higher. Let's implement this.
+
                -- The game checks if the player is on the ground. Remember that when the player is on the ground, Y-Axis Velocity = 0.
 +
if player.y_velocity == 0 then
 +
player.y_velocity = player.jump_height    -- The player's Y-Axis Velocity is set to it's Jump Height.
 +
end
 +
end
 +
end
 +
</source>
  
First, let's create a new player variable in love.load.
+
We aren't done yet. If you try to jump, the character won't jump yet. This is because we haven't added the physics of the jump yet.
  
 
<source lang="lua">
 
<source lang="lua">
player = {
+
...
  ...
+
function love.update(dt)
  jetpack_fuel = 0.5, -- Note: not an actual jetpack. Variable is the time (in seconds) you can hold spacebar and jump higher.
+
...
  jetpack_fuel_max = 0.5,
+
        -- Add below the jump key assignment.
  ...
+
 
}
+
        -- This is in charge of the jump physics.
 +
        if player.y_velocity ~= 0 then                                      -- The game checks if player has "jumped" and left the ground.
 +
player.y = player.y + player.y_velocity * dt                -- This makes the character ascend/jump.
 +
player.y_velocity = player.y_velocity - player.gravity * dt -- This applies the gravity to the character.
 +
end
 +
       
 +
        -- This is in charge of collision, making sure that the character lands on the ground.
 +
        if player.y > player.ground then    -- The game checks if the player has jumped.
 +
player.y_velocity = 0       -- The Y-Axis Velocity is set back to 0 meaning the character is on the ground again.
 +
    player.y = player.ground    -- The Y-Axis Velocity is set back to 0 meaning the character is on the ground again.
 +
end
 +
end
 
</source>
 
</source>
  
Now, we remove our love.keypressed callback. We won't exact time, which is just bothersome to find out using keypressed and keyreleased. Instead, we'll move that logic to love.update.
+
And finally, the character can now jump!
 +
 
 +
[[File:Part_4-_Jumping.gif]]
  
 +
== Part 5: The Whole Code ==
 +
 +
Congratulations! You have created a Baseline 2D Platformer! Now for reference, the whole code:
 
<source lang="lua">
 
<source lang="lua">
 +
platform = {}
 +
player = {}
 +
 +
function love.load()
 +
platform.width = love.graphics.getWidth()
 +
platform.height = love.graphics.getHeight()
 +
 +
platform.x = 0
 +
platform.y = platform.height / 2
 +
 +
player.x = love.graphics.getWidth() / 2
 +
player.y = love.graphics.getHeight() / 2
 +
 +
player.speed = 200
 +
 +
player.img = love.graphics.newImage('purple.png')
 +
 +
player.ground = player.y
 +
 +
player.y_velocity = 0
 +
 +
player.jump_height = -300
 +
player.gravity = -500
 +
end
 +
 
function love.update(dt)
 
function love.update(dt)
  if player.jetpack_fuel > 0 and love.keyboard.isDown("space") then -- We can still move upwards and we're actually holding space
+
if love.keyboard.isDown('d') then
    player.jetpack_fuel = player.jetpack_fuel - dt -- Decrease the fuel meter
+
if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
    player.y_velocity = player.y_velocity + jump_height * (dt / player.jetpack_fuel_max)
+
player.x = player.x + (player.speed * dt)
  end
+
end
 +
elseif love.keyboard.isDown('a') then
 +
if player.x > 0 then
 +
player.x = player.x - (player.speed * dt)
 +
end
 +
end
  
  if player.y_velocity ~= 0 then -- we're probably jumping
+
if love.keyboard.isDown('space') then
    player.y = player.y + player.y_velocity * dt -- "dt" means we wont move at different speeds if the game lags
+
if player.y_velocity == 0 then
    player.y_velocity = player.y_velocity - gravity * dt
+
player.y_velocity = player.jump_height
 +
end
 +
end
  
    -- We hit the ground again
+
if player.y_velocity ~= 0 then
    if player.y < 0 then
+
player.y = player.y + player.y_velocity * dt
      player.y_velocity = 0
+
player.y_velocity = player.y_velocity - player.gravity * dt
      player.y = 0
+
end
      player.jetpack_fuel = player.jetpack_fuel_max
+
 
    end
+
if player.y > player.ground then
  end
+
player.y_velocity = 0
 +
    player.y = player.ground
 +
end
 +
end
 +
 
 +
function love.draw()
 +
love.graphics.setColor(1, 1, 1)
 +
love.graphics.rectangle('fill', platform.x, platform.y, platform.width, platform.height)
 +
 
 +
love.graphics.draw(player.img, player.x, player.y, 0, 1, 1, 0, 32)
 
end
 
end
 
</source>
 
</source>
  
[[Category:Tutorials]]
+
I hope this tutorial helped you in any way and I thank you for reading and trying this tutorial.
{{#set:LOVE Version=0.10.0}}
+
 
{{#set:Description=A basic implementation of platformer-style jumping physics.}}
 
  
== Other languages ==
+
{{#set:LOVE Version=0.11.2}}
{{i18n|Tutorial:Platformer_Jumping}}
 

Latest revision as of 23:03, 20 December 2018

Overview

Part 1: The Platform
Part 2: The Player
Part 3: Player Movement
Part 4: Jumping
Part 5: The Whole Code

In this tutorial, we will be creating a "Baseline 2D Platformer". In this context, "baseline" means the basic functions. Therefore, in a baseline 2d platformer, you can see or experience the following:

  • The platform for the player to move around on.
  • The player avatar/character.
  • The player's ability to move left and right.
  • The player's ability to jump around.

Part 1: The Platform

First, let's create the platform. For tutorial purposes, the platform is a white rectangle that will encompass the bottom-half part of the game window.

platform = {}

function love.load()
        -- This is the height and the width of the platform.
	platform.width = love.graphics.getWidth()    -- This makes the platform as wide as the whole game window.
	platform.height = love.graphics.getHeight()  -- This makes the platform as tall as the whole game window.
        
        -- This is the coordinates where the platform will be rendered.
	platform.x = 0                               -- This starts drawing the platform at the left edge of the game window.
	platform.y = platform.height / 2             -- This starts drawing the platform at the very middle of the game window
end

function love.update(dt)

end

function love.draw()
	love.graphics.setColor(1, 1, 1)        -- This sets the platform color to white.

        -- The platform will now be drawn as a white rectangle while taking in the variables we declared above.
	love.graphics.rectangle('fill', platform.x, platform.y, platform.width, platform.height)
end

So for will have something that looks like this:

Part 1 The Platform.PNG

Nice! Now we have a simple white platform!

Part 2: The Player

Next will be the player character. For tutorial purposes, the player character will be a 32x32 purple block. This time, instead of using the love.graphics.rectangle, we will use love.graphics.draw. The reason for this is because the purple block would be an external file. This way, you can learn how to draw an image "internally" as well as how to draw an "external" image. You can get the purple block here.

Note: Just as a quick reference, love.graphics.rectangle lets you draw a rectangle in the game window. We can consider this as a form of "internal-file rendering" (not the exact or proper term) since we are declaring its (possible) parameters in the game's code. On the other hand, love.graphics.draw lets you draw an image in the window - as long as the file type is supported. We can consider this as a form of "external-file rendering" (again, not the exact or proper term) since the image we are rendering is an external file being called by the game's code.
...
player = {}  -- Add this below the platform variable

function love.load()
	...
        -- Add this below the platform variables.

        -- This is the coordinates where the player character will be rendered.
	player.x = love.graphics.getWidth() / 2   -- This sets the player at the middle of the screen based on the width of the game window. 
	player.y = love.graphics.getHeight() / 2  -- This sets the player at the middle of the screen based on the height of the game window. 

        -- This calls the file named "purple.png" and puts it in the variable called player.img.
	player.img = love.graphics.newImage('purple.png')
end

function love.update(dt)

end

function love.draw()
	...
        -- Add this below the love.graphics.rectangle line.
        
        -- This draws the player.
	love.graphics.draw(player.img, player.x, player.y, 0, 1, 1, 0, 32)
end

Now we have something that looks like this:

Part 2 The Player Character.PNG

Alright! Now the player exists!

Part 3: Player Movement

We have the platform, we have the player! Time to make this guy move. Remember that in 2d platformers, the player must be able to move left and right! We can't just have the player move forward to the right forever. To do this, we need to declare the speed the of the player's movement as well as assign keyboard inputs so that when the player presses a certain button, the character will move left or right depending on what is coded.

function love.load()
	...
	player.speed = 200    -- This is the player's speed. This value can be change based on your liking.
end

function love.update(dt)
        -- This is how to assign keyboard inputs.
        
	if love.keyboard.isDown('d') then                    -- When the player presses and holds down the "D" button:
		player.x = player.x + (player.speed * dt)    -- The player moves to the right.
	elseif love.keyboard.isDown('a') then                -- When the player presses and holds down the "A" button:
		player.x = player.x - (player.speed * dt)    -- The player moves to the left.
	end
end

If you run the code, the player can now move left and right! HOWEVER! There is a problem. If you haven't noticed by now, if you keep on moving the character to the right (or to the left), it will go pass the game window and be out of the player's vision. For tutorial purposes, we don't want that. So inside the love.update() function:

function love.update(dt)
	if love.keyboard.isDown('d') then
		-- This makes sure that the character doesn't go pass the game window's right edge.
		if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
			player.x = player.x + (player.speed * dt)
		end
	elseif love.keyboard.isDown('a') then
		-- This makes sure that the character doesn't go pass the game window's left edge.
		if player.x > 0 then 
			player.x = player.x - (player.speed * dt)
		end
	end
end

Part 3- Player Movement.gif

Now the player can't get pass the screen. This way the player character will always be within the constraints of the game window.

Part 4: Jumping

All that's left now is jumping. You can't have a 2d platformer without jumping. Without it, player's can't really get over obstacles like pits or walls. It's time for some basic physics. For something to jump and fall, an object (in this case, the player's character) needs to have a Y-Axis Velocity, a Jump Height, and Gravity. For tutorial purposes, we will not be taking into consideration an object's mass however it is worth mentioning that giving mass to an object can change the way it's physics works. First, let's declare the three things we've mentioned earlier. I will also be adding a variable called "ground". This is to indicate where the ground is. Think of it as the place where the feet should touch and land after jumping.

function love.load()
	...
        -- Add this below the player.img
	player.ground = player.y     -- This makes the character land on the plaform.

	player.y_velocity = 0        -- Whenever the character hasn't jumped yet, the Y-Axis velocity is always at 0.

	player.jump_height = -300    -- Whenever the character jumps, he can reach this height.
	player.gravity = -500        -- Whenever the character falls, he will descend at this rate.
end
Note: Feel free to change the values of player.jump_height and player.gravity to your liking. Just remember that for the most part, unless you want the game's physics to not work the usual way, player.gravity > player.jump_height

After declaring the variables, we will now proceed to making the character jump. To do this, we need to assign a key that will make the character jump.

...
function love.update(dt)
	...
        -- Add below the right key assignment. 

        -- This is in charge of player jumping.
	if love.keyboard.isDown('space') then                     -- Whenever the player presses or holds down the Spacebar:

                -- The game checks if the player is on the ground. Remember that when the player is on the ground, Y-Axis Velocity = 0.
		if player.y_velocity == 0 then
			player.y_velocity = player.jump_height    -- The player's Y-Axis Velocity is set to it's Jump Height.
		end
	end
end

We aren't done yet. If you try to jump, the character won't jump yet. This is because we haven't added the physics of the jump yet.

...
function love.update(dt)
	...
        -- Add below the jump key assignment.

        -- This is in charge of the jump physics.
        if player.y_velocity ~= 0 then                                      -- The game checks if player has "jumped" and left the ground.
		player.y = player.y + player.y_velocity * dt                -- This makes the character ascend/jump.
		player.y_velocity = player.y_velocity - player.gravity * dt -- This applies the gravity to the character.
	end
        
        -- This is in charge of collision, making sure that the character lands on the ground.
        if player.y > player.ground then    -- The game checks if the player has jumped.
		player.y_velocity = 0       -- The Y-Axis Velocity is set back to 0 meaning the character is on the ground again.
    		player.y = player.ground    -- The Y-Axis Velocity is set back to 0 meaning the character is on the ground again.
	end
end

And finally, the character can now jump!

Part 4- Jumping.gif

Part 5: The Whole Code

Congratulations! You have created a Baseline 2D Platformer! Now for reference, the whole code:

platform = {}
player = {}

function love.load()
	platform.width = love.graphics.getWidth()
	platform.height = love.graphics.getHeight()

	platform.x = 0
	platform.y = platform.height / 2

	player.x = love.graphics.getWidth() / 2
	player.y = love.graphics.getHeight() / 2

	player.speed = 200

	player.img = love.graphics.newImage('purple.png')

	player.ground = player.y
	
	player.y_velocity = 0

	player.jump_height = -300
	player.gravity = -500
end

function love.update(dt)
	if love.keyboard.isDown('d') then
		if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
			player.x = player.x + (player.speed * dt)
		end
	elseif love.keyboard.isDown('a') then
		if player.x > 0 then 
			player.x = player.x - (player.speed * dt)
		end
	end

	if love.keyboard.isDown('space') then
		if player.y_velocity == 0 then
			player.y_velocity = player.jump_height
		end
	end

	if player.y_velocity ~= 0 then
		player.y = player.y + player.y_velocity * dt
		player.y_velocity = player.y_velocity - player.gravity * dt
	end

	if player.y > player.ground then
		player.y_velocity = 0
    	player.y = player.ground
	end
end

function love.draw()
	love.graphics.setColor(1, 1, 1)
	love.graphics.rectangle('fill', platform.x, platform.y, platform.width, platform.height)

	love.graphics.draw(player.img, player.x, player.y, 0, 1, 1, 0, 32)
end

I hope this tutorial helped you in any way and I thank you for reading and trying this tutorial.