This is a part of a series on developing Gridworld Plus. See part 1 for more details.
Adding the player
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.
Adding the player to the world, we now have a player!
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)
:
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:
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.