Tutorial:Baseline 2D Platformer

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.