This is a part of a series on developing Gridworld Plus. See part 1 for more details.

Adding the player

A view of what our empty world looks like so far
A view of what our empty world looks like so far

Now that we have a world, the next step to making a functional game is to add a player. We begin by making a Player scene that represents a player in the world, once again using an asset from Humble Bundle for the player sprite.

The sprite for the player
The sprite for the player

Adding the player to the world, we now have a player!

A player in the world, to a certain extent
A player in the world, to a certain extent

However, at the moment, there are several problems with playing the game. First of all, because the player is added to the scene before the tiles are, the player is placed behind the tiles. We can fix this by setting the Z Index property of the Player node to 1, which places it above the default 0.

Next of all, the player is not in the correct location. To resolve this issue, we add the following code to the _process function of the World scene:

$Player.position = get_tile_position(0, 0)

This code uses the get_tile_position function previously defined to place the tiles in the correct place, to place the player accordingly in the same way, on top of the tile (0, 0). Now we get the player in the correct place, on top of the grass tile at (0, 0):

A player correctly placed in the world
A player correctly placed in the world

Player movement

One of the primary mechanisms for the player interacting with the world is player movement. Pressing one of the directional movement keys should move the player one tile in the direction of the movement key, provided that the movement should be permitted. Since we already use the arrow keys for moving the camera, I decided to use the WASD keys for controlling player movement instead. To do so, I set up entries in the input map in the project settings corresponding to player_up, player_down, player_left, and player_right.

Adding this code to the player and changing the player location updating in the World script to refer to x and y will enable movement:

var x
var y

func _process(delta):
	# ...
	if Input.is_action_just_pressed("player_left"):
		x -= 1
	if Input.is_action_just_pressed("player_right"):
		x += 1
	if Input.is_action_just_pressed("player_up"):
		y -= 1
	if Input.is_action_just_pressed("player_down"):
		y += 1

However, this doesn’t stop the player from moving somewhere where they shouldn’t go:

A player going off into the wild
A player going off into the wild

Therefore, there needs to be a check in the World script as to whether the destination for the player is a valid block:

func can_move(character, x, y):
	if x < 0 || y < 0:
		return false
	if y >= len(map):
		return false
	var row = map[y]
	if x >= len(row):
		return false
	var tile = row[x]
	return can_move_to_tile(character, tile)

func can_move_to_tile(character, tile):
	# Prevent movement to water tiles
	if tile == "w2":
		return false
	return true

With this change, we should move the input processing code also to the World script.

# World.gd

func _process(delta):
	# ...
	if Input.is_action_just_pressed("player_left"):
		$Player.move(self, -1, 0)
	if Input.is_action_just_pressed("player_right"):
		$Player.move(self, 1, 0)
	if Input.is_action_just_pressed("player_up"):
		$Player.move(self, 0, -1)
	if Input.is_action_just_pressed("player_down"):
		$Player.move(self, 0, 1)

# Player.gd
func move(world, dx, dy):
	if world.can_move(self, x + dx, y + dy):
		x += dx
		y += dy

There, no more players moving off the rails.

Action system

One central mechanic of Gridworld Plus is the Action mechanic. Essentially, every single action the player can perform - move, use a skill, etc. - costs a certain amount of Action. Each player starts at 0 action, and usage of skills increase this Action. Players can only interact when Action is zero, so this essentially acts as a cooldown on everything the player can do. Action depletes at 1 per second, so eventually the player will be able to act again.

For now, I implemented Action for movement so that movement costs action, making the following modifications to the existing functions:

# Player.gd

var action = 0

func _process(delta):
	if action > 0:
		action -= 1.0 * delta
	if action < 0:
		action = 0

func move(world, dx, dy):
	if can_make_move(world, x + dx, y + dy):
  	# Moving costs 0.5 seconds of action
		action = 0.5
		x += dx
		y += dy

func can_make_move(world, x, y):
	if action > 0:
		return false
	if not world.can_move(self, x, y):
		return false
	return true

Now, there is a 0.5 second delay before the player can move again.

Next steps

The next step in developing this game is to add enemies and skills to make a playable game. But before that, I’ll make some usability changes, specifically focused on creating a basic player UI for displaying Action and animating the player movement. Stay tuned for more!

This is a part of a series on developing Gridworld Plus. Continue reading at part 3 - player UI and animating movement.