Today’s post is going to be a quite short one but an important feature has been added to the game, namely some barriers to restricting the movement of the playable character. First of all, I have created a proper map, a new background for the game. I have used a map creator app, which is used to create maps for Heroes III (one of the favorite games of my childhood) and now the player is on an island. In the final version, the player is deployed on one part of the island and has to find a king without being noticed by the guards, similar to the classic AC games.
In the previous post, I have created a class Character
and included the method which moves our player. I have realized that only the playable character can be moved by the user, so I have redefined the Character class just to have the basic properties and inherited a Player
class that now contains the move
method. For the inclusion of the obstacles I will need a rectangle object associated with the player object, therefore I added a Rect
object to the Character class properties too:
class Character:
def __init__(self, ...some_args_here...):
...
self.rect = pygame.Rect(self.x, self.y, self.size, self.size)
class Player(Character):
def move(self, keys_pressed):
...
In the game, I also want to allow the player to move faster if the user wants that as a classic running feature. This can be achieved very easily by adding a new key up/down event (on LeftShift) which changes the player’s speed attribute.
if event.type == pygame.KEYDOWN:
...
if event.key == pygame.K_LSHIFT:
player.speed *= 2 # running
if event.type == pygame.KEYUP:
...
if event.key == pygame.K_LSHIFT:
player.speed /= 2 # stop running
Now we want to restrict the possible moving territory of the player, namely I don’t want the character to be able to run out of the island into the water. I have also added some walls to the map as obstacles and bridges between the two parts but obviously, I want to let the player go through the latter one. Adding the obstacles involves creating some Rect
objects which define the restricted places and every time we move the code should check whether we want to enter these territories. If the next movement doesn’t overlap with any of these rectangles then the player can move.
Fortunately, the Pygame’s Rect object is prepared to solve such problems with its colliderect
method which returns a boolean on whether the two Rect objects overlap or not. We also came prepared by creating a Rect object for the player object, therefore the change we have to make is to check within the move
method whether the new position of the player’s Rect object does collide with any of the obstacle objects. If it doesn’t then make the move but if it does then just keep the player standing at the previous position:
OBSTACLES = [
...
pygame.Rect(644, 430, 49, 237),
...
]
# within the "move" method:
...
x_delta = -np.sin(self.rotation * np.pi / 180) * self.speed
y_delta = -np.cos(self.rotation * np.pi / 180) * self.speed
# create a temporary rectangle with the new positions and check if the move is valid:
temp_rect = pygame.Rect(self.rect.x + x_delta, self.rect.y + y_delta,
self.size, self.size)
num_collides = np.sum([temp_rect.colliderect(obstacle) for obstacle in OBSTACLES])
# if valid then move, else just stand at that point:
if num_collides == 0:
self.rect.x += x_delta
self.rect.y += y_delta
self.x = self.rect.x
self.y = self.rect.y
self.walk_last_time = pygame.time.get_ticks()
else:
self.pic = PLAYER_STAND
As far as I know, these rectangle objects as their name suggest can only cover rectangles on the map. Therefore, if you have a complicated map you have to add these rectangles one by one figuring out their size and position. It can be a painful process, but along with these small but important changes we already have a quite neat “game” where we can walk and run around an island without falling into the water.
The complete code is available at my git repo here: main_part_iii.py