Enemies play a crucial role in creating engaging and challenging games. They provide obstacles and adversaries for players, making the gaming experience more exciting. Python's Arcade library offers a straightforward way to incorporate enemies into your games.
Before starting, make sure you have pip installed on your device. Use this command to install the arcade library:
After that, start by creating a simple game where the player can move left and right using the arrow keys.
The code used in this article is available in this GitHub repository and is free for you to use under the MIT license.
import arcade
# Window dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
# Player attributes
PLAYER_RADIUS = 25
PLAYER_SPEED = 5
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
arcade.set_background_color(arcade.color.WHITE)
self.player_x = width // 2
def on_draw(self):
arcade.start_render()
arcade.draw_circle_filled(self.player_x, PLAYER_RADIUS, PLAYER_RADIUS, arcade.color.BLUE)
def on_key_press(self, key, modifiers):
if key == arcade.key.LEFT:
self.player_x -= PLAYER_SPEED
elif key == arcade.key.RIGHT:
self.player_x += PLAYER_SPEED
def update(self, delta_time):
pass
def main():
window = GameWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
arcade.run()
if __name__ == "__main__":
main()
To create an enemy that kills the player upon collision, create another circle on the screen. In the on_draw function, you can draw this enemy circle and check for collisions in the update method. You can also use sprites for enemies.
# Add to GameWindow class
class GameWindow(arcade.Window):
# ...
def __init__(self, width, height):
# ...
# Enemy attributes
self.enemy_x = width // 2
self.enemy_y = height - PLAYER_RADIUS
self.enemy_radius = 20
def on_draw(self):
# ...
arcade.draw_circle_filled(self.enemy_x, self.enemy_y, self.enemy_radius, arcade.color.RED)
def update(self, delta_time):
if self.is_collision(self.player_x, self.player_y, self.enemy_x, self.enemy_y, PLAYER_RADIUS, self.enemy_radius):
print("Game Over!")
def is_collision(self, x1, y1, x2, y2, radius1, radius2):
distance_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2
radius_sum_squared = (radius1 + radius2) ** 2
return distance_squared <= radius_sum_squared
In some games, enemies can chase the player, adding a dynamic element to the gameplay. To create a following enemy, you need to update its position based on the player's position. Whenever the player moves, the enemy moves in the same direction. You can achieve this by modifying the update method. Create a new file named enemy-follow-player.py and add the code with the below updates:
# Add to GameWindow class
class GameWindow(arcade.Window):
# ...
def update(self, delta_time):
if self.player_x < self.enemy_x:
self.enemy_x -= PLAYER_SPEED
elif self.player_x > self.enemy_x:
self.enemy_x += PLAYER_SPEED
if self.is_collision(self.player_x, self.player_y,
self.enemy_x, self.enemy_y,
PLAYER_RADIUS, ENEMY_RADIUS):
print("Game Over!")
def is_collision(self, x1, y1, x2, y2, radius1, radius2):
distance_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2
radius_sum_squared = (radius1 + radius2) ** 2
return distance_squared <= radius_sum_squared
Below is the output:
To create an enemy that shoots bullets, create a Bullet class and a list to keep track of active bullets. The enemy will periodically create a new bullet and update its position. Create a new file named bullets.py and add the code with the below updates:
# Add to GameWindow class
class Bullet:
def __init__(self, x, y, radius, speed):
self.x = x
self.y = y
self.radius = radius
self.speed = speed
def update(self):
self.y -= self.speed
class GameWindow(arcade.Window):
# ...
def __init__(self, width, height):
# ...
# Enemy attributes
self.bullets = []
self.bullet_radius = 5
self.bullet_speed = 3
self.bullet_cooldown = 60 # Number of frames between bullet spawns
self.bullet_timer = 0
def on_draw(self):
# ...
for bullet in self.bullets:
arcade.draw_circle_filled(bullet.x, bullet.y,
self.bullet_radius, arcade.color.BLACK)
def update(self, delta_time):
# ...
self.bullet_timer += 1
if self.bullet_timer >= self.bullet_cooldown:
self.bullets.append(Bullet(self.enemy_x, self.enemy_y - self.enemy_radius,
self.bullet_radius, self.bullet_speed))
self.bullet_timer = 0
for bullet in self.bullets:
bullet.update()
if self.is_collision(self.player_x, self.player_y, self.enemy_x,
self.enemy_y, PLAYER_RADIUS, ENEMY_RADIUS):
print("Game Over!")
def is_collision(self, x1, y1, x2, y2, radius1, radius2):
distance_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2
radius_sum_squared = (radius1 + radius2) ** 2
return distance_squared <= radius_sum_squared
Below is the output:
In many games, enemies can possess health points (HP), allowing them to sustain multiple hits before being defeated. Adding health points to enemies can introduce strategic gameplay elements and provide a sense of progression and challenge. Create a new file named heath-point.py and add the code with the below updates:
# Window dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
# Player attributes
PLAYER_RADIUS = 25
PLAYER_SPEED = 5
# Enemy attributes
ENEMY_RADIUS = 20
ENEMY_HEALTH = 100
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
arcade.set_background_color(arcade.color.WHITE)
self.player_x = width // 2
self.player_y = height // 2
self.enemy_x = width // 2
self.enemy_y = height - PLAYER_RADIUS
self.enemy_health = ENEMY_HEALTH
print(self.enemy_health)
def on_draw(self):
arcade.start_render()
arcade.draw_circle_filled(self.player_x,
self.player_y,
PLAYER_RADIUS,
arcade.color.BLUE)
if self.enemy_health > 0:
arcade.draw_circle_filled(self.enemy_x,
self.enemy_y,
ENEMY_RADIUS,
arcade.color.RED)
def update(self, delta_time):
if self.is_collision(self.player_x, self.player_y,
self.enemy_x, self.enemy_y,
PLAYER_RADIUS, ENEMY_RADIUS):
self.enemy_health -= 10
print(self.enemy_health)
The ENEMY_HEALTH constant has a value of 100 to represent the enemy's initial health points. When the player collides with the enemy, you can deduct some points from the enemy's health. To display the updated health value, you can print a text object self.health_text that shows the current enemy health.
By incorporating health points for enemies, you can introduce a layer of challenge and strategy for players. The displayed health value provides visual feedback and allows players to track the enemy's remaining health.
Additionally, you can expand the code by adding further logic and visuals, such as displaying health bars or implementing defeat conditions when the enemy's health reaches zero.
When designing enemies for your game, it's important to consider several best practices to ensure they contribute to a challenging and enjoyable gameplay experience. Here are some guidelines to follow when creating enemies:
Create enemies with varying attributes such as speed, size, health, and attack power. Different enemy types should pose different levels of difficulty, requiring players to adapt their strategies accordingly. By introducing a mix of enemy attributes, you can keep the gameplay fresh and engaging.
Give each enemy type its own unique behavior patterns. Some enemies may move in a predictable manner, while others might exhibit more complex or erratic movements. Consider incorporating enemy AI algorithms to make their behavior more intelligent and unpredictable, adding an extra layer of challenge for players.
Implement health points for enemies to introduce a sense of progression and durability. This allows enemies to sustain multiple hits before being defeated. By assigning varying amounts of HP to different enemy types, you can create a hierarchy of difficulty and encourage players to strategize and prioritize their targets.
Adding enemies to your games can significantly enhance the gameplay experience. They introduce challenges and motivate players to improve their skills. Enemies can come in various forms, from simple obstacles to complex AI-driven adversaries. By implementing enemies effectively, you can make your games more engaging and enjoyable for players.