Sprites in Python and Cockroach

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

Introduction

Welcome to the second part of a multi-article tutorial on creating a top-down 80’s style game. In this article we will learn to use Sprites in Python and Cockroach for storing/retrieving screens and some of the Sprites in the game.

In this lesson we learn about Sprites and their relation to Sprite Lists. We’ll back track over some of part one of this series of articles on making a game with Python and CockroachDB and add some new features and functions.

Prerequisites

  • Download and study the source code, image files, and sound files, which you can get for free here. For each lesson, we have copied parts of that full source code into the lesson for analysis, explaining how each section works.

  • Be sure to read the beginning of this group of articles where we gain an understanding of the basics.

  • Use Python’s PIP installer to add Flask, psycopg2, random, and the Arcade libraries to your system. Arcade Library docs. The Python library we will use for much of the functionality of this Zombie Feeder game is Arcade.

What are Sprites?

The first thing is to gain an understanding of what Sprites are. In visual games, a Sprite is a bitmap rendered to a graphical user interface (GUI).

Create a Sprite

Before creating a sprite, we’ll assume you went through the first article in this series and learned how to create a GUI.

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
import arcade
# Set up screen dimensions variables
# as well as window caption.
screen_width = 1200
screen_height = 900
screen_title = "Zombie Feeder"
# Create the window.
arcade.open_window(screen_width, screen_height, screen_title)
# Set the background color.
arcade.set_background_color(arcade.color.BLACK)
# Initialize and set an image for a Sprite.
sprite_player = arcade.Sprite("./resources/images/player.png")
# Initialize two variables for
# Sprite coordinates.
x = 300
y = 300
# Set position based on the
# above coordinates variables.
sprite_player.set_position(x, y)
# Render that Sprite
# to the screen.
sprite_player.draw()
# Keep the GUI open until
# the player closes it.
arcade.run()

Analysis of the above code:

  • import arcade: The arcade frame-work adds many functions needed for game making. Many of our functions, including use of Sprites, depend on this framework being used.

  • screen_width = 1200: Initializing a variable to store the GUI width. We chose a width of 1200 pixels.

  • screen_height = 900: Initializing a variable to store the GUI height. We chose a height of 900 pixels.

  • arcade.open_window: This function uses three parameters; width, height, and title. Width and height are integers while Title uses a text string.

  • arcade.set_background_color: This function uses one parameter, a color.

  • arcade.Sprite: This function creates a Sprite, names it “sprite_player”, and gives it the PNG image found at “./resources/images/player.png”.

  • x = 300: Initializing a variable for managing the distance in pixels from the left border of the GUI.

  • y = 300: Initializing a variable for managing the distance in pixels from the bottom border of the GUI. So 0 would be the bottom and 900 would be the top.

  • sprite_player.set_position: Moves the player Sprite to be at the x and y position we had set to 300 by 300.

  • sprite_player.draw: Draws the Sprite to the GUI.

  • arcade.run: Executes the application.

Now that we see how easy it is to initialize, name, and give our player Sprite an image, let’s place Sprites in a list.

What is an Arcade Sprite List?

Arcade uses a variation of Python’s list type to provide Sprite lists. These make it easy to conduct operations on groups of Sprites. In our Zombie Feeder game we use this for brains (bullets), zombies (enemies), and lego blocks (obstructions). For bullets, this makes it possible for the player to “shoot” multiple bullets at a time. For enemies, we can track as many as we want. And for lego blocks, we can put hundreds on the screen, even thousands! Experiment with changing the default constants for all these objects to see how the changes affect game enjoyment and performance. From our perspective, Python seems to be efficient with not only displaying all these objects at once, moving them, as well as collision detection between one Sprite and Sprite Lists. In a future article in this series we will analyze how to accomplish all of that.

Look below at some Python Arcade script showing Sprites being created and then added to a list of Sprites. We’ll use a for loop to accomplish the part where we want to loop through multiple Sprites. In order to get multiple Sprites for our Sprite List, we use the Cockroach table we created in the first article in this series. Since we went over that in a fair amount of detail in that first article, we’ll gloss over the database part here:

1
2
    # Initialize a Sprite List for lego blocks
    blocks_sprite_list = arcade.SpriteList()

The above code initializes a Sprite List that acts just like a normal Python list, except that it has some properties and methods inherited from Python’s Arcade library, such as center_x, center_y, and others. Some we will add to the class in a later article!

Query Cockroach for GUI

1
2
3
4
5
6
7
8
9
10
11
#   SQL to get Sprite screen x/y coordinates from the database.
s = ""
s += "SELECT"
s += " i_x"
s += ", i_y"
s += " FROM tbl_gui_objects"
s += " WHERE ("
s += " id_gui = 1"
s += " AND t_obj_type = 'lego block'"
s += ")"
crdb_cursor.execute(s)

Now that we pulled a set of records into crdb_cursor, we can move on to iterate through each row returned, where a row represents one lego block, and draw the blocks to the screen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#  Loop through the Cockroach rows returned
# by the above query and position each block
# using the x and y coordinates found in each
# row of data.
for each crdb_row in crdb_cursor:
    # Get columns 0 and 1 from the current row
    x = crdb_row[0]
    y = crdb_row[1]
    # Create the current lego block as a sprite
    block_lego = arcade.Sprite("./resources/images/block-lego.png", SCALE)
    # Move the current lego block to be at
    # the x/y coordinates we pulled from
    # the database.
    block_lego.center_x = x
    block_lego.center_y = y
    # Add the Sprite we created
    # above to our Sprite List.
    blocks_sprite_list.append(block_lego)

Now, we will add to our Sprite creation knowledge by combining a normal Python list to store image file names with a Sprite List for managing enemies. The purpose is to share another perspective on how Sprite Lists work to increase your understanding of the 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
# --------------------
# Create a Sprite List
# for the enemies.
# --------------------
list_enemies = arcade.SpriteList()
# Assign image paths
image_list = ("./resources/images/char-z-fem-01-blue-front.png",
              "./resources/images/char-z-fem-01-purple-front.png",
              "./resources/images/char-z-fem-01-yellow-front.png",
              "./resources/images/char-z-guy-01-green-front.png",
              "./resources/images/char-z-guy-01-orange-front.png",
              "./resources/images/char-z-guy-02-front.png",
              "./resources/images/char-z-guy-03-front.png",
              "./resources/images/char-z-guy-04-front.png")
# Sounds for enemy enraged state.
# If no enraged sound exists, place a "z" in front of name
# so the string search later doesn't find the non-existant
# sound.
name_list = ("char-z-fem-01-blue",
              "char-z-fem-01-purple",
              "char-z-fem-01-yellow",
              "char-z-guy-01-green",
              "char-z-guy-01-orange",
              "char-z-guy-02",
              "char-z-guy-03",
              "char-z-guy-04")
# Cycle through each image
for i in range(ENEMY_COUNT_INITIAL):
    image_no = i
    enemy_sprite = EnemySprite(image_list[image_no], SCALE)
    enemy_sprite.guid = name_list[image_no]
    enemy_sprite.speed = 2 + (DIFFICULTY/12)
    enemy_sprite.immunity = False
    enemy_sprite.enraged = False
    enemy_sprite.frustration = 0
    enemy_sprite.which = "self.sound_char_" + str(i).zfill(2) + "_enraged"
    enemy_sprite.scale = SCALE
    enemy_sprite.timer_rand = 0
    enemy_sprite.timer_smart = 0
    enemy_sprite.url_image = image_list[image_no]
    enemy_sprite.center_y = 800
    enemy_sprite.center_x = int((SCREEN_WIDTH/8 * (i+1))-(SCREEN_WIDTH/13.9))
    # enemy_sprite.change_x = int(random.random() * 2 + (DIFFICULTY/10) - 1)
    # enemy_sprite.change_y = int(random.random() * 2 + (DIFFICULTY/10) - 1)
    enemy_sprite.change_x = 0
    enemy_sprite.change_y = 0
    self.list_all_sprites.append(enemy_sprite)
    self.list_enemies.append(enemy_sprite)

Conclusion

This was the second part of the multi-article tutorial for building an arcade-style game. In this article we learned about Sprites and Sprite Lists, including how to fill a Sprite with a PNG image, set the coordinates for a Sprite’s position on the GUI, and finally, how to group Sprites into Sprite Lists so that we can later manage entire groups of Sprites. Coming up is the third part, where we will learn to read key presses to give the user control over his player.

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.