Game Python Postgres Moving Player

Introduction

In this tutorial, we will continue creating a game with Python and Postgres with moving a player as part 3 of a multi-part series of lessons where the final result will be a 2D graphical top-down view game somewhat like Pac-Man. We’ll use Python’s “Arcade” library for the graphics-related features it has. We are using PostgreSQL to store and retrieve screen data in a relational database. We’ve created two tables for this purpose. In this lesson we will place a player Sprite on the screen and move it around using the arrow keys. In the next lesson, we’ll learn how to address enemies and collision physics in the game and other pieces of what makes an enjoyable Python graphical game, like dying and getting awarded points for colliding with power-ups.

Prerequisites

See part 1 and 2 where we learned how to draw on a screen, create PostgreSQL tables for storing screen data and screen objects, and pull data from those tables for displaying drawings and then Sprites on the game screen: Create game with Python and Postgres Part 1. For this lesson we will be using much of what we learned in part 1 but not all. One change will be that instead of using draw commands for objects, we’ll be loading objects in from Postgres and placed into Sprites instead of drawn.

What is a Sprite?

In CG, a sprite is a 2-dimensional bitmap that is integrated into a larger window, most often within the context of a 2D video game. Sprites were developed by Daniel Hillis at Texas Instruments.

Examples of the earliest systems with built-in sprites include the Texas Instruments TI-99/4A, Atari 8-bit computers like the 400 and 800, Commodore Vic-20 and 64, Commodore Amiga, NES, Sega Genesis, and many arcade machines of the 1980s.

Scope of the term sprite has grown to refer to any 2-dimensional bitmap used as part of a graphics display, even if drawn into a frame buffer (which you can choose to do or not do with Python’s Arcade functions) rather than being integrated on-the-fly at display time.

Create a Sprite with Arcade

Before creating a sprite, we must first create a window. We’ll do both here:

# install and use Python's arcade library
import arcade

# Set up screen dimensions variables
screen_title = "Python Arcade Wack-Man 2D Game"
screen_width = 1280
screen_height = 720

# Open/create the window. Set size and title
arcade.open_window(screen_width, screen_height, screen_title)

# Set background color
# See http://arcade.academy/arcade.color.html for a color list
arcade.set_background_color(arcade.color.BLACK)

# Create a sprite
arcade.PlayerSprite = arcade.Sprite("sprites/player.png")

# Set variables for position of middle of screen
mid_x = screen_width / 2
mid_y = screen_height / 2

# Change Player sprite position

# Set variables to keep track of player x and y position.
player_x = mid_x
Player_y = mid_y

# Use the set_position function to change the coordinates of our player Sprite
arcade.PlayerSprite.set_position(player_x, player_y)

# Keep the window up until the user clicks close
arcade.run()

Get screen from PostgreSQL

Now we’ll retrieve screen data, which is objects and their locations in 2D space, from two tables. We’ll start by creating the tables. Why two? One table (“tbl_screens”) will have a row per screen, while the second table (“tbl_screens_objects”) will store all the objects and use id_screen to point back to the id field in tbl_screens.

PostgreSQL tables that store game screens

Recap: tbl_screens with one row of sample data to represent screen 1 of the game:

idt_titlet_colori_widthi_height
1Screen 1BLACK1280720

Here is tbl_screens_objects which contains our Sprites. This table relates to the above table via the id and id_screen fields.

idid_screent_object_namet_object_typet_object_image_URL
11Playerspriteplayer.png
21Wallspritewall.png
31Treespritetree.png
41Powerupspritepowerup.png
51Enemy1spriteenemy1.png
61Enemy2spriteenemy2.png

NOTE: For tbl_screens_objects, for now, we are only showing the relevant fields for setting up the sprites, leaving out our columns that deal with collisions and sounds.

Query PostgreSQL for screen data

We covered this part in detail in part 2 of this series, so here, we will leave out the queries and go straight to adding movement to our player sprite.

Sprite movement with keys

def on_key_press(self, symbol, modifiers):
    # every time a key is pressed, this function is called
    if symbol == arcade.key.right:
        self.right = true
    if symbol == arcade.key.left:
        self.left = true
    if symbol == arcade.key.up:
        self.up = true
    if symbol == arcade.key.down:
        self.down = true

def on_key_release(self, symbol, modifiers):
    # every time a key is released, this function is called
    if symbol == arcade.key.right:
        self.right = false
    if symbol == arcade.key.left:
        self.left = false
    if symbol == arcade.key.up:
        self.up = false
    if symbol == arcade.key.down:
        self.down = false

Analysis: When a key is pressed, the on_key_press event function is fired, passing self to the function. It also catches the key pressed into “symbol”. We can then use “if” statements to set self.right, self.left, self.up, or self.down to true, to set our Sprite in motion. The Sprite will continue to move in that that direction until the user releases the arrow key, in which case the on_key_release event function is fired, causing self.right, self.left, self.up, or self.down to be set to false, causing the Sprite to stop its motion.

Now that we have explored some of the key features and learned to overcome certain challenges around loading and moving Sprites, let’s examine the full source code of our Python application so far.

Full source code

import arcade
import psycopg2
from flask import Flask
from flask import render_template

# connect to PostgreSQL database
t_host = "PostgreSQL database host address"
t_port = "5432"
t_dbname = "database name"
t_user = "database user name"
t_pw = "password"
db_conn = psycopg2.connect(host=t_host, port=t_port, dbname=t_dbname, user=t_user, password=t_pw)
db_cursor = db_conn.cursor()

# initialize screen variables
#  to be global
id_screen = 0
t_title = ""
i_width = 0
i_height = 0
t_color = ""

@app.route("/main")

def getScreenFromDB():
    # Get a screen ID, color, width, height, and title from tbl_screens from FIRST screen
    s = ""
    s += "SELECT TOP 1"
    s += " id"
    s += ", t_title"
    s += ", t_color"
    s += ", i_width"
    s += ", i_height"
    s += " FROM tbl_screens"
    s += " ORDER BY i_order"
    try:
        db_cursor.execute(s)
        id_screen = db_cursor.fetch("id")
        t_title = db_cursor.fetch("t_title")
        i_width = db_cursor.fetch("i_width")
        i_height = db_cursor.fetch("i_height")
        t_color = db_cursor.fetch("t_color")
    except psycopg2.Error as e:
        t_message = "Database error: " + e + "/n SQL: " + s
        return render_template("error.html", t_message = t_message)
    db_cursor.close

def getObjectsFromDB():
    # Following are all the columns in our tbl_screens_objects
    # Out of all the columns you see below, we are only using a few
    # in THIS part of the multi-part series. We are leaving the extra
    # columns in so that you can (a) see where we are heading; and
    # (b) have less to change when you get to the next part of this series.
    s = ""
    s += "SELECT"
    s += " id"
    s += ", t_object_name"
    s += ", t_object_type"
    s += ", t_object_image_URL"
    s += ", i_x"
    s += ", i_y"
    s += ", i_width"
    s += ", i_height"
    s += ", i_radius"
    s += ", t_color_fill"
    s += ", t_color_border"
    s += ", t_color_collision"
    s += ", t_properties"
    s += ", t_collision_sound"
    s += ", t_collision_action"
    s += ", b_collision_possible"
    s += ", b_collision_destroys_it"
    s += ", b_collision_awards"
    s += ", n_collision_awards"
    s += ", n_collision_damage"
    s += " FROM tbl_screens_objects"
    s += " WHERE ("
    s += " id_screen = " + id_screen
    s += ")"
    s += " ORDER BY i_order"
    try:
        db_cursor.execute(s)
        return db_cursor
    except psycopg2.Error as e:
        t_message = "Database error: " + e + "/n SQL: " + s
        return render_template("error.html", t_message = t_message)

class gameWindow(arcade.Window):
    def__init__(self, i_width, i_height, t_title):
    super().__init__(width, height, title, resizable=false)
    self.set_location(100, 100)

    arcade.set_background_color(arcade.color.t_color)

    self.player_x = 100
    self.player_y = 200
    self.player_speed = 250

    self.right = false
    self.left = false
    self.up = false
    self.down = false

    # initialize a sprite list to store objects via append method
    self.SpriteObjects = arcade.SpriteList()

    # iterate through each row in db_cursor
    for each db_row in db_cursor:
        id_screen = db_row.fetch("id")
        t_object_name = db_row.fetch("t_object_name")
        t_object_type = db_row.fetch("t_object_type")
        t_object_image_URL = db_row.fetch("t_object_image_URL")
        i_x = db_row.fetch("i_x")
        i_y = db_row.fetch("i_y")
        i_width = db_row.fetch("i_width")
        i_height = db_row.fetch("i_height")
        i_radius = db_row.fetch("i_radius")
        t_color_fill = db_row.fetch("t_color_fill")
        t_color_border = db_row.fetch("t_color_border")
        t_color_collision = db_row.fetch("t_color_collision")
        t_properties = db_row.fetch("t_properties")
        t_collision_sound = db_row.fetch("t_collision_sound")
        t_collision_action = db_row.fetch("t_collision_action")
        b_collision_possible = db_row.fetch("b_collision_possible")
        b_collision_destroys_it = db_row.fetch("b_collision_destroys_it")
        b_collision_awards = db_row.fetch("b_collision_awards")
        n_collision_awards = db_row.fetch("n_collision_awards")
        n_collision_damage = db_row.fetch("n_collision_damage")
        # draw each sprite in db_row to screen
        #  ignoring if player because we already created player as sprite1 above.
        #  This assumes we didn't filter out "player" by changing our SQL WHERE clause.
        if t_object_type == "sprite" and t_object_name != "Player":
            self.SpriteObstacle = arcade.Sprite(t_object_image_URL, i_x, i_y)
            # add current obstacle (sprite) to growing list of Sprites (SpriteList)
            self.SpriteObjects.append(self.SpriteObstacle)
        elif t_object_type == "sprite" and t_object_name == "Player":
            self.SpritePlayer = arcade.Sprite(t_object_image_URL, i_x, i_y)

    self.SpriteObjects.draw()

def on_draw(self):
    self.SpritePlayer.draw()
    # Moving the following line to just above where screen is initialized
    #   because - for THIS lesson - none of the objects are moving, except player,
    #   so no need to redraw them constantly... yet.
    # self.SpriteObjects.draw()

def on_update(self, delta_time):
    # delta_time is last time same function was run;
    #   used to scale movement to processing speed
    if self.right = true:
        self.player_x += self.player_speed * delta_time
    if self.left = true:
        self.player_x -= self.player_speed * delta_time
    if self.up = true:
        self.player_y += self.player_speed * delta_time
    if self.down = true:
        self.player_y -= self.player_speed * delta_time

    self.SpritePlayer.set_position(self.player_x, self.player_y)
    # Putting in the following line for future when some objects (enemies) will move
    # self.SpriteObjects.update()

def on_key_press(self, symbol, modifiers):
    # every time a key is pressed, this function is called
    if symbol == arcade.key.right:
        self.right = true
    if symbol == arcade.key.left:
        self.left = true
    if symbol == arcade.key.up:
        self.up = true
    if symbol == arcade.key.down:
        self.down = true

def on_key_release(self, symbol, modifiers):
    # every time a key is released, this function is called
    if symbol == arcade.key.right:
        self.right = false
    if symbol == arcade.key.left:
        self.left = false
    if symbol == arcade.key.up:
        self.up = false
    if symbol == arcade.key.down:
        self.down = false

Def main():
    getScreenFromDB()
    db_cursor = getObjectsFromDB()
    gameWindow(i_width, i_height, t_title)
    arcade.run()

Conclusion

In this tutorial, we continued creating a game with Python and Postgres, moving a player as part 3 of a multi-part series of lessons where the final result will be a 2D graphical top-down game like Pac-Man. We are using Python’s “Arcade” library for the graphics-related features. We are using Postgres to store and retrieve screen and object data in a database. We’ve created two tables for this purpose. In this lesson we learned to place a player Sprite on the screen and move it around using the arrow keys. We also learned how to use a Sprite list. In the next lesson, we’ll learn how to address enemies and collision physics in the game and other pieces of what makes an enjoyable Python graphical game, like dying and getting awarded points for colliding with power-ups.

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.