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:
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 | # 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:
id | t_title | t_color | i_width | i_height |
---|---|---|---|---|
1 | Screen 1 | BLACK | 1280 | 720 |
Here is tbl_screens_objects which contains our Sprites. This table relates to the above table via the id and id_screen fields.
id | id_screen | t_object_name | t_object_type | t_object_image_URL |
---|---|---|---|---|
1 | 1 | Player | sprite | player.png |
2 | 1 | Wall | sprite | wall.png |
3 | 1 | Tree | sprite | tree.png |
4 | 1 | Powerup | sprite | powerup.png |
5 | 1 | Enemy1 | sprite | enemy1.png |
6 | 1 | Enemy2 | sprite | enemy2.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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | 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