Skip to main content

Ralsina.Me — Roberto Alsina's website

CobraPy? CobraPew! Pew!

It has been a week and I had not re­al­ly touched Co­braPy my 80s-style python en­vi­ronem­nt at al­l. UN­TIL YES­TER­DAY.

So, how is it look­ing now? Like this, us­ing as­sets from the AWE­SOME ken­ney.nl:

I have added:

  • Sup­port for mu­sic (y­ou are lis­ten­ing to 80616-lunchip.xm)
  • Sup­port for sounds
  • Some im­prove­ments to the sprite en­gine (reusing tex­tures and such)
  • Bet­ter key­board sup­port, or at least one that's more use­ful for games
  • Fixed a bunch of bugs
  • Added col­li­sion de­tec­tion (did not make it to the video but it's there)

So let's go over the whole source code for that "game" and see how it work­s? Keep in mind that this code will look bizarre for your stan­dard Python pro­gram­mer be­cause it's de­lib­er­ate­ly done in a ... "ba­sic" style? Even in a BA­SIC style. Which means it aims to be more "struc­tured pro­gram­ming" than what you are used to see­ing.

Al­so, there are no guar­an­tees that any of the APIs ex­posed will even ex­ist to­mor­row, since this is still in a pro­to­typ­ing phase and things will change.

import time

load_sprite("player", "assets/PNG/playerShip3_orange.png")
x = 100
y = 500
step = 3
move_sprite("player", x, y)

We cre­at­ed the in­trepid play­er's ship, and put it "some­where". Sprites are iden­ti­fied by a name. All sprite op­er­a­tions will use that name to know what sprite should be af­fect­ed.

# A fleet of bad ships
fleet = [
    [f"enemy-1-{x}" for x in range(7)],
    [f"enemy-2-{x}" for x in range(7)],
    [f"enemy-3-{x}" for x in range(7)],
    [f"enemy-4-{x}" for x in range(7)],
]

for invader in fleet[0]:
    load_sprite(invader, "assets/PNG/Enemies/enemyBlack1.png")
for invader in fleet[1]:
    load_sprite(invader, "assets/PNG/Enemies/enemyBlue2.png")
for invader in fleet[2]:
    load_sprite(invader, "assets/PNG/Enemies/enemyGreen3.png")
for invader in fleet[3]:
    load_sprite(invader, "assets/PNG/Enemies/enemyRed4.png")

Again, just a few ships with dif­fer­ent look­s.


def move_fleet(x, y):
    """Move fleet so the left-top alien is in position x, y"""
    for inv_y, row in enumerate(fleet):
        for inv_x, invader in enumerate(row):
            move_sprite(invader, x + 90 * inv_x, y + 90 * inv_y)


fleet_x = 50
fleet_y = 50
fleet_step_x = 1
fleet_step_y = 5

A func­tion to move the fleet as a whole, and some in­fo about it, lo­ca­tion and speed.

load_sprite("laser", "assets/PNG/Lasers/laserBlue01.png")
load_sound("laser", "assets/Audio/laserRetro_004.ogg")
laser_last_shot = time.time()
laser_x = -100
laser_y = 0


def shoot():
    global laser_x, laser_y, x, y, laser_last_shot
    play_sound("laser")
    laser_x = x
    laser_y = y
    laser_last_shot = time.time()

Same for our lonely bullet, but we have a couple of new things. With load_sound we load into the program a sound (surprise!) and with play_sound it's played. Just like sprites, they are identified by name.

We keep track of when we shoot be­cause this is a ship, not a ma­chine­gun, Jim!

def check_hit():
    global laser_last_shot
    t1 = time.time()
    global laser_x
    for row in fleet:
        for invader in row:
            if check_collision("laser", invader):
                laser_x = -100
                laser_last_shot = 0

Col­li­sion de­tec­tion! If the bul­let hits an in­vad­er ... well, we can't make it ex­plode yet, but we move the bul­let out and al­low the us­er to shoot again, at least.

load_music_stream("background", "80616-lunchip.xm")
play_music_stream("background")

Just load and play a nice chip­tune! As usu­al, mu­sic is iden­ti­fied by a la­bel, so we can have more than one and play the one we wan­t.

def gameloop():
    global x, fleet_x, fleet_y, fleet_step_x, fleet_step_y, laser_y

The gameloop function is special. Whatever you put here CobraPy will try to run it 60 times a second. No faster, maybe slower if your gameloop takes too long. So this is where you do things like update the screen and interact with the user. You know ... a game loop.

    if is_pressed(114) and x < 720:  # right arrow
        x += step
    if is_pressed(113) and x > 0:  # left arrow
        x -= step
    if is_pressed(38): # a
        if time.time() - laser_last_shot > 1:
            shoot()

User interaction! Basically is_pressed takes a key identifier (nice constants coming soonish) and tells you if it's pressed or not. So, if right-arrow is pressed, increase x (within limits), if left-arrow is, decrease it (within limits). If "a" is pressed and you have not shot for a second, shoot.

    move_sprite("player", x, 500)

Well, that's why we in­creased / de­creased it, right?

    fleet_x += fleet_step_x
    move_fleet(fleet_x, fleet_y)
    if fleet_x < 50 or fleet_x > 150:
        fleet_step_x = -fleet_step_x
        fleet_y += fleet_step_y

The fleet moves to the right, then to the left, and at ev­ery turn, down. Does it re­mind you of any game you've seen?

    laser_y -= step
    move_sprite("laser", laser_x, laser_y)

Bul­let go up.

    check_hit()

En­e­my go boom. Even­tu­al­ly.


Contents © 2000-2023 Roberto Alsina