Shoot in Python and Cockroach

Have a Database Problem? Speak with an Expert for Free
Get Started >>

Introduction

Welcome to the sixth part of a multi-article set of tutorials showing how to build a fully functional game called Zombie Feeder. In this article we will learn how to shoot in Python and Cockroach is used for reading and writing various settings and GUI objects.

In this part, we focus on initializing the player fired “brains”, which are our “bullets” in this game, which explains the title of the game being “Zombie Feeder”.

In the articles in the series following this part, we’ll build in sound, collision detection, tracking and displaying player score, and adding a game difficulty and speed system.

Prerequisites

  • The full Python source code, images, and sound files are free for download here.

  • Python Arcade manual. We use the Arcade library for quite a bit of the functionality of this videogame.

  • Study and begin with part one through part five where we learned to build and set up a GUI with Cockroach as our database for storing that data, learned how to create Sprites and Sprite Lists to group multiple Sprites, wrote script for responding to user key strokes, learned a way to move the Player Sprite around the screen based on those key presses, and finally, moving enemies based on random vs. “brainy”-ish calculations.

Python game constants

We are limiting this article to show only the parts of the primary game source code that are needed for this lesson and leaving out the remainder, which you can download from here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# Initialize constants.
SCREEN_TITLE = "Zombie Feeder"
# Difficulty level, which can later be
# changed via certain buttons.
# This will affect game speed, enemy
# "brains", and scoring.
DIFFICULTY = 4
# How many enemy zombies?
# This number must match the number of images.
ENEMY_COUNT_INITIAL = 8
ENEMY_SPEED = 2 + (DIFFICULTY/12)
# Set the following to false for easier
# difficulty where enemies,
# when enraged, are not immune to obstacles.
# So, when large, they have a high chance
# of getting "stuck" between lego blocks.
ENEMY_ENRAGED_IMMUNE_TO_BLOCKS = False
# Allow enemies to "teleport"
# from the edge of the GUI to the
# opposite edge of the GUI.
# Set to False for less challenging
# play, where the enemies will "bounce"
# from the GUI edge.
ENEMY_SCREEN_EDGE_TRAVERSE = True
# Beginning size for all Sprites
# on the screen.
SCALE = 0.25
# The number of lives the player has extra.
PLAYER_STARTING_LIVES = 4
PLAYER_IMMUNE_TO_BLOCKS = True
# Calculate the monitor's resolution.
MONITOR_RES_WIDTH, MONITOR_RES_HEIGHT = pyautogui.size()
SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
# Make sure GUI width is not more than monitor width.
if SCREEN_WIDTH > MONITOR_RES_WIDTH:
    SCREEN_WIDTH = MONITOR_RES_WIDTH
# Make sure GUI HEIGHT is not more than monitor width.
if SCREEN_HEIGHT > MONITOR_RES_HEIGHT:
    SCREEN_HEIGHT = MONITOR_RES_HEIGHT
# We base the number of obstacles
# on screen width.
BLOCKS_NUMBER = int(SCREEN_WIDTH/20)
# Set up the edges of the GUI.
SPACE_OFFSCREEN = 1
LIMIT_LEFT = -SPACE_OFFSCREEN
LIMIT_RIGHT = SCREEN_WIDTH + SPACE_OFFSCREEN
LIMIT_BOTTOM = -SPACE_OFFSCREEN
LIMIT_TOP = SCREEN_HEIGHT + SPACE_OFFSCREEN
# Draw screens from the database.
# If set to False, screens will be
# drawn using the random function to
# place obstacles around the GUI.
SCREEN_FROM_DATABASE = False

Now, we’ll build a Python class to handle the bullets or in this case, represented by brains.

Set up class for bullets

1
2
3
4
5
6
7
8
9
10
11
12
# Class to represent brain.
# Class to represent bullet.
# Derived from arcade.TurnSprite which is a
# sprite that aligns to its direction.
class bulletSprite(TurnSprite):
    def update(self):
        super().update()
        # Check bullet position and delete bullet
        # when it hits the edge of the GUI.
        if self.center_x < -100 or self.center_x > SCREEN_WIDTH + 100 or \
                self.center_y > SCREEN_HEIGHT + 150 or self.center_y < -150:
            self.remove_from_sprite_lists()

In order to properly relate how to use bullets, we need to do a bit more fleshing out of the Player’s Sprite for you here:

Set up class for Player

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Create a class for Sprite
# to represent the player.
# comes from arcade.Sprite object.
class PlayerSprite(arcade.Sprite):
    # Set up the player.
    def __init__(self, filename, scale):
        # Call the parent Sprite constructor
        super().__init__(filename, scale)
        # Initialize player movement variables.
        # Angle comes in automatically from the parent class.
        self.thrust = 0
        self.speed = 0
        self.max_speed = 5
        self.drag = 0.045
        # Initialize respawning variables
        self.respawning = 0
        # Initialize difficulty variable
        self.difficulty = DIFFICULTY
        # Set the player to respawn.
        self.respawn()

    # Called when Player dies so need to make a new player.
    def respawn(self):
        # self.respawning is a timer for invulnerability.
        # If we are in the middle of respawning, this is non-zero.
        self.respawning = 1
        # Place player in center of the screen.
        self.center_x = SCREEN_WIDTH / 2
        self.center_y = SCREEN_HEIGHT / 2
        self.angle = 0

    # Update player position, speed, and opacity.
    def update(self):
        # Update respawning counter.
        if self.respawning:
            self.respawning += 1
            # Use respawn counter to set player opacity (alpha property).
            self.alpha = self.respawning
            # When respawn counter reaches past 250,
            # set player to be fully visible.
            if self.respawning > 250:
                self.respawning = 0
                # Alpha of 255 means 100% visible.
                self.alpha = 255

        # If the player is moving, add gravity-like "drag".
        if self.speed > 0:
            self.speed -= self.drag
            # Make sure player speed is not negative
            # after it was reduced by drag:
            if self.speed < 0:
                self.speed = 0
        if self.speed < 0:
            self.speed += self.drag
            if self.speed > 0:
                self.speed = 0
        self.speed += self.thrust
        # Make sure player speed does
        # not get out of hand.
        if self.speed > self.max_speed:
            self.speed = self.max_speed
        if self.speed < -self.max_speed:
            self.speed = -self.max_speed
        # Change Player direction based on angle and speed.
        self.change_x = -math.sin(math.radians(self.angle)) * self.speed
        self.change_y = math.cos(math.radians(self.angle)) * self.speed
        self.center_x += self.change_x
        self.center_y += self.change_y
        # If the player goes off-screen, teleport
        # them to the other side of the window
        if self.right < 0:
            self.left = SCREEN_WIDTH
        if self.left > SCREEN_WIDTH:
            self.right = 0
        if self.bottom < 0:
            self.top = SCREEN_HEIGHT
        if self.top > SCREEN_HEIGHT:
            self.bottom = 0
        # Call the parent class.
        super().update()

Fire button

Here we assign the space bar as our “fire” button. In a past article, we studied listening for keyboard input. Here we’ll add the part that checks the space bar key and throws (shoots) the brain (bullet).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# When any key is pressed:
def on_key_press(self, symbol, modifiers):
    # "symbol" is the key pressed.
    # If the player hit the space bar and not respawning, throw brain.
    if symbol == arcade.key.SPACE and not self.player_sprite.respawning:
        # Assign an image to the new sprite.
        bullet_sprite = bulletSprite("./resources/images/bullet.png", SCALE)
        # Name the sprite.
        bullet_sprite.guid = "bullet"
        # Assign a speed to the sprite.
        bullet_speed = 8
        # Set direction.
        bullet_sprite.change_y = \
            math.cos(math.radians(self.player_sprite.angle)) * bullet_speed
        bullet_sprite.change_x = \
            -math.sin(math.radians(self.player_sprite.angle)) \
            * bullet_speed
        # Set location.
        bullet_sprite.center_x = self.player_sprite.center_x
        bullet_sprite.center_y = self.player_sprite.center_y
        # Update the sprite in memory.
        bullet_sprite.update()
        # Add new sprite to the two relevant sprite lists.
        self.list_all_sprites.append(bullet_sprite)
        self.list_bullets.append(bullet_sprite)
        # Play a throwing sound.
        arcade.play_sound(self.sound_throw)

Once the “bullet” is fired, we need to continually check on and update its progress in terms of collisions. We’ll do that in the next article in this series, where we learn about collision detection between Sprites and Sprite Lists.

Conclusion

In this sixth part of the multi-article set teaching how to create the “Zombie Feeder” video game, we learned how to create and use bullets in Python and use Cockroach to read and write Sprites for the screen and later, saving score. In this part, we learned how to initialize brains thrown by the player at the zombies. In following articles that are part of this series, we’ll learn how to add sounds, detect collisions between Sprites, keep track of player score, change game difficulty, and read mouse movements.

Pilot the ObjectRocket Platform Free!

Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.

Get Started

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.