Tutorial:Gridlocked Player

In this tutorial, we will create a VERY basic game wherein you can move a "player" around the screen in fixed increments, but the player graphic moves in smaller increments. This could be useful if you are creating a game that is tile-based, and want your player to stay aligned to the grid, but don't want them to appear to teleport around.

Starting Code

To start with, we need to create a player. The player will have several aspects before we're done, so we're going to make the player variable a table inside of love.load().

function love.load()
	player = {}
end

Next we'll need to see what we're doing. We can render the player as a rectangle using love.graphics.rectangle() in love.draw().

function love.draw()
	love.graphics.rectangle("fill", 0, 0, 32, 32)
end

Let's fill in the player table a little so that the player has a position. Note that we're using the shorthand way of putting variables into a table: just define them inside the curly brackets, using commas to separate them. (We also need to change the parameters in love.graphics.rectangle to use these new values.)

function love.load()
	player = {
		x = 256,
		y = 256
	}
end

function love.draw()
	love.graphics.rectangle("fill", player.x, player.y, 32, 32)
end

Now that the player has position, let's add in some controls using love.keypressed(). All we do is check to see if the key matches, and change the value in the player table.

function love.keypressed(key)
	if key == "down" then
		player.y = player.y + 32
	end
end

So we now have a little square that can teleport down by one grid unit (32 pixels in our case, the same length that we made our player).

Expansion

We can expand this to more keys using elseif. Notice that everything is still in units of 32, a common tile size that plays nice with PO2_Syndrome.

function love.keypressed(key)
	if key == "up" then
		player.y = player.y - 32
	elseif key == "down" then
		player.y = player.y + 32
	elseif key == "left" then
		player.x = player.x - 32
	elseif key == "right" then
		player.x = player.x + 32
	end
end

So we now have a happy little square that can jump around the screen as fast as you can hit the keyboard. Now let's add in the smooth transitions. To do this, we're going to need to shift up the variables in the player table. Don't forget that this will affect all references to the player table.

There are two things that we will now need. One, we need actual XY coordinates as opposed to the gridlocked coordinates that will now serve as destination coordinates. Two, we need to update the actual coordinates in love.update() to move towards the destination coordinates. We can do that with a simple math problem:

actual_x = actual_x - (actual_x - destination_x)

Or, in real Lua this time:

function love.load()
	player = {
		grid_x = 256,
		grid_y = 256,
		act_x = 200,
		act_y = 200
	}
end

function love.update(dt)
	player.act_y = player.act_y - (player.act_y - player.grid_y)
	player.act_x = player.act_x - (player.act_x - player.grid_x)
end

function love.draw()
	love.graphics.rectangle("fill", player.act_x, player.act_y, 32, 32)
end

function love.keypressed(key)
	if key == "up" then
		player.grid_y = player.grid_y - 32
	elseif key == "down" then
		player.grid_y = player.grid_y + 32
	elseif key == "left" then
		player.grid_x = player.grid_x - 32
	elseif key == "right" then
		player.grid_x = player.grid_x + 32
	end
end

As you probably noticed, you probably can't see any difference. We need to use love.update's dt parameter to control speed, by multiplying the difference of the positions by dt:

function love.update(dt)
	player.act_y = player.act_y - ((player.act_y - player.grid_y) * dt)
	player.act_x = player.act_x - ((player.act_x - player.grid_x) * dt)
end

Still not quite right though. Now the player is unbearably slow (but at least he's consistent from computer to computer!). Let's give the player a speed attribute to control how fast he moves, by multiplying dt by the speed:

function love.load()
	player = {
		grid_x = 256,
		grid_y = 256,
		act_x = 200,
		act_y = 200,
		speed = 10
	}
end

function love.update(dt)
	player.act_y = player.act_y - ((player.act_y - player.grid_y) * player.speed * dt)
	player.act_x = player.act_x - ((player.act_x - player.grid_x) * player.speed * dt)
end

Putting it all together

Here is the full final code:

function love.load()
	player = {
		grid_x = 256,
		grid_y = 256,
		act_x = 200,
		act_y = 200,
		speed = 10
	}
end

function love.update(dt)
	player.act_y = player.act_y - ((player.act_y - player.grid_y) * player.speed * dt)
	player.act_x = player.act_x - ((player.act_x - player.grid_x) * player.speed * dt)
end

function love.draw()
	love.graphics.rectangle("fill", player.act_x, player.act_y, 32, 32)
end

function love.keypressed(key)
	if key == "up" then
		player.grid_y = player.grid_y - 32
	elseif key == "down" then
		player.grid_y = player.grid_y + 32
	elseif key == "left" then
		player.grid_x = player.grid_x - 32
	elseif key == "right" then
		player.grid_x = player.grid_x + 32
	end
end

There are several ways this could be improved.

  • The art could be touched up, of course, using the techniques covered in this article: [1]
  • We could also implement some extra controls, such as binding the Escape key to love.event.push('q'), which would quit the game.
  • We could keep the player on screen by checking that grid_x never dips below 0 or above 800, that kind of thing.

But for the purposes of this tutorial we have enough already.

See Also

| How to make placeholder art suck less