Python Arcade Bullets and Postgres

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

Introduction

Welcome to part 6 of a multi-part set of articles showing how to create a 1980’s era video game. In this part 6 we learn how to use Python Arcade for bullets and Postgres for reading and writing Sprites for the screen.

In this part, we focus on initializing the bullet or bullets fired by the player at the enemies.

In the tutorials following this one, we’ll work on sound, detecting collisions, keeping player score, and adding a game difficulty system.

Prerequisites

  • The working source code, images, and sound files are free here.

  • Online manual for Python Arcade. We use the Arcade framework for quite a bit of the functionality in this top-down view game.

  • Be sure to study part 1 through part 5 where we learned to build and set up a game window with Postgres as our database for storing that data, learned how to build Sprites and Sprite Lists, wrote code for responding to user key presses, moving the Player Sprite around the screen, and moving enemies based on random and “intelligent” calculations.

  • Use PIP to install the arcade, datetime, flask, math, sys, os, psycopg2, pyautogui, and random libraries. As we move through each lesson, we’ll use more functions based on those libraries.

Python libraries for the game

1
2
3
4
5
6
7
8
import arcade # Game-oriented library for Python.
from datetime import datetime, timedelta # For seeding random number generation.
import math # For some math / movement physics-related functions.
import os # For accessing the file system to load images and sounds.
import psycopg2 # For Postgres data retrieval for screens.
import pyautogui # For getting monitor resolution to make sure our window is not too large.
import random # For generating random numbers for enemy movement.
import sys # For getting resource files into the game.

Set up 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
# Set up constants
SCREEN_TITLE = "Pyngo Skater"
DIFFICULTY = 5
ENEMY_COUNT_INITIAL = DIFFICULTY + 2
ENEMY_SPEED = 2 + (DIFFICULTY/10)
SCALE = 0.25
SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 900
MONITOR_RES_WIDTH, MONITOR_RES_HEIGHT = pyautogui.size()
#   Be sure SCREEN_WIDTH is not bigger than monitor width.
if SCREEN_WIDTH > MONITOR_RES_WIDTH:
    SCREEN_WIDTH = MONITOR_RES_WIDTH
#   Be sure SCREEN_HEIGHT is not bigger than monitor width.
if SCREEN_HEIGHT > MONITOR_RES_HEIGHT:
    SCREEN_HEIGHT = MONITOR_RES_HEIGHT
#   Set number of ice blocks based on the width of the current monitor screen.
BLOCKS_NUMBER = int(SCREEN_WIDTH/24)
#   Limit enemies to edges of window.
SPACE_OFFSCREEN = 1
LIMIT_LEFT = -SPACE_OFFSCREEN
LIMIT_RIGHT = SCREEN_WIDTH + SPACE_OFFSCREEN
LIMIT_BOTTOM = -SPACE_OFFSCREEN
LIMIT_TOP = SCREEN_HEIGHT + SPACE_OFFSCREEN
SCREEN_FROM_DATABASE = True
ID_SCREEN = 1

Next, we’ll create a class to handle the bullet or in this case, snowball.

Set up class for bullets

1
2
3
4
5
6
7
8
9
10
11
# Class to represent snowball.
class SnowballSprite(TurnSprite):
    def update(self):
        super().update()
        # Notice here we are limiting the Sprite from leaving the screen
        # by checking the coordinates of the Sprite against the
        # screen height and width and then if it is outside these bounds,
        # we delete the bullet by using the "remove_from_sprite_lists" function.
        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()

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Create a class for Sprite
# to represent the player.
# comes from arcade.Sprite object.
class PlayerSprite(arcade.Sprite):
    def __init__(self, filename, scale):
        # Call the parent Sprite's constructor
        super().__init__(filename, scale)
        # Initialize various player variables.
        # Angle arrives automatically from the parent class.
        self.thrust = 0
        # Initialize player Sprite speed to
        #   begin with being still; zero.
        self.speed = 0
        # Set the maximum speed the player Sprite can go.
        # This will not be influenced by difficulty because
        # our focus here is to make movement as smooth and easy
        # as possible. And constant.
        self.max_speed = 5
        # Drag determines how much wind resistance
        # and gravy slow down the Player Sprite.
        self.drag = 0.046
        # Initialize a respawning variable, setting
        # it at zero just to tell Python this is an integer.
        self.respawning = 0
        # Initialize a global variable for tracking difficulty, using
        # a constant we created in an earlier part of this series
        # and will address more deeply in a future part of this
        # series.
        self.difficulty = DIFFICULTY
        # Set the player to be respawning now.
        self.respawn()

    # This is called when Player collides with an enemy so can make
    # a new player and set the player to "invulnerable" for
    # a short time span.
    def respawn(self):
        # This is a timer for invulnerability.
        # If we are still in "respawning mode", this is larger than zero.
        self.respawning = 1
        # Set respawning player Sprite to be in the center of the window.
        self.center_x = SCREEN_WIDTH / 2
        self.center_y = SCREEN_HEIGHT / 2
        # Player Sprite is facing up.
        self.angle = 0

    # Update player Sprite position, speed, and opacity.
    def update(self):
        # Update counter for tracking respawn time.
        if self.respawning:
            self.respawning += 1
            # Use counter to set player transparency (alpha).
            self.alpha = self.respawning
            # When respawning counter gets above 250,
            # set player Sprite to fully visible.
            if self.respawning > 250:
                self.respawning = 0
                # Alpha of 255 means 100% opacity (visibility).
                self.alpha = 255

        # If the player is moving, add "drag" to
        # gradually show them down to zero.
        # This simulates the "skating" effect.
        if self.speed > 0:
            self.speed -= self.drag
            # Be sure to not allow player speed to get
            # to 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
        # Change Player Sprite speed based on the
        # value in the thrust variable.
        self.speed += self.thrust
        if self.speed > self.max_speed:
            self.speed = self.max_speed
        if self.speed < -self.max_speed:
            self.speed = -self.max_speed
        # Change Player Sprite direction
        # based on speed and angle.
        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 Sprite goes off-screen, translocate
        # them to the opposite side of the screen
        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 Sprite's parent class.
        super().update()

Check fire button

In this case, we are assigning the space bar as our “fire” button. In a past article, we studied reading the keyboard. Here we’ll merely add the part that checks the space bar key and throws the snowball.

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
# 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 snowball.
    if symbol == arcade.key.SPACE and not self.player_sprite.respawning:
        snowball_sprite = SnowballSprite("./resources/images/snowball.png", SCALE)
        snowball_sprite.guid = "Snowball"
        # You can change the snowball speed to whatever you like using the number below.
        # You may even want to make it a constant, instead of "12", so that you can
        # control bullet speed at the top of the overall file, rather than have to
        # dig for it.
        snowball_speed = 12
        snowball_sprite.change_y = \
            math.cos(math.radians(self.player_sprite.angle)) * snowball_speed
        snowball_sprite.change_x = \
            -math.sin(math.radians(self.player_sprite.angle)) \
            * snowball_speed

        snowball_sprite.center_x = self.player_sprite.center_x
        snowball_sprite.center_y = self.player_sprite.center_y
        snowball_sprite.update()
        # Once the sprite is created and updated,
        # We add it to our list of all Sprites
        self.list_all_sprites.append(snowball_sprite)
        # and then to our list of all bullets.
        self.list_snowballs.append(snowball_sprite)
        # Play a "woosh" sound - we'll go deeper into
        # sounds in a later part of this series.
        arcade.play_sound(self.sound_snowball_throw)

Once the snowball is thrown, we need to periodically check on and update its progress in terms of collisions. We’ll do that in the next article in this series, where we play with collisions.

Conclusion

In this part 6 of the multi-part set of articles teaching how to create the “Pyngo Skater” video game, we learned how to use Python Arcade for bullets and Postgres to read and write Sprites for the screen. In this part, we learned how to initiale the bullets fired by the player at the enemies. In following parts, we’ll learn how to make sound, detect collisions between Sprites and Sprite lists, keep player score, and add game difficulty.

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.