I'm creating Space Invaders but it's not working like it should

292 Views Asked by At

For my programming class I'm working on a Space Invaders game... My code isn't perfect but mostly it's working. Mostly...

I won't go into a lot of detail (I'll include a link to the Codeskulptor page so you can see for yourself) but the one thing that's bugging me is that my enemies are slowing down, where they should speed up!

I'm printing the horizontal velocity (vx) to the console and I can see it's increasing. The speeding up works properly up until a certain point where they just slow down again, while vx is still increasing?! This is really easy to spot when there are only like four enemies left...

All enemy/alien objects are stored in a list, when they are hit by a laser, they get deleted from the list by a function called 'kill'.

Heres the function:

def kill(enemy):
    global points
    points += 1
    for enemies in EnemyList_1:
        if enemies.vx > 0:
            enemies.vx += 1.0/4/60
        elif enemies.vx < 0:
            enemies.vx -= 1.0/4/60
    EnemyList_1.remove(enemy)

As you can see the enemy objects in the EnemyList_1 list and they are removed when the function is called (the function is called by the following function in my Enemies Class):

    def laserhit(self, laser):
        global wait, FriendLaserList
        if abs(self.x - laser.x) < self.size[0] / 2.0 and abs(self.y - laser.y) < self.size[1] / 2.0:
            laser.x = WIDTH
            laser.y = HEIGHT
            laser.pos = [WIDTH, HEIGHT]
            laser.vy = 0
            wait = 0.0
            FriendLaserList = []
            kill(self)

The wait variable is something I put in to create a cooldown between laser firing. Don't bother looking at it

My code probably looks like a trainwreck but for the most part it's working and I'm happy with my own work, it's the first time I've ever done something like this...

The link to the game (not finishes of course, also all the comments are in Dutch, for my teacher etc. but all the variables are in English: http://www.codeskulptor.org/#user38_Acl7AIFiuqohQ0N_29.py

The first couple of times you try to run it, it will not work because the SimpleGui module is loading the images. (Our teacher only accepts immages stored in Google Drive)

My burning question: How come my enemies are slowing down when more are killed over time, while my code explicitly states they should speed up?

PS: I've tried, instead of deleting the enemies from the list, to just place them outside the canvas, but this gave me trouble with the laser - enemy collision checking. I'd rather just delete them since it makes the game run more smoothly. I've also tried .pop() and 'del' but I get the same problem

I apoligise for my n00b code and my English mistakes...

No but seriously don't freak out over how bad my code is right now, a lot of stuff wasn't workin and I had to quickly change values etc. but once everything is working as it should I will clean it up etc. Right now I just need this fixed and I am so confused wether it's something I did or if it's just a bug...

My entire code for people that don't want to go to codeskulptor (Warning: it's a mess!):

import simplegui
import random

# Frame opties
WIDTH = 450.0
HEIGHT = 600.0
BORDER = (WIDTH / 16, HEIGHT / 12, 1, 'white')

# Vijand opties
# Grootte van de vijanden kan je aanpassen, ook het aantal.
# Aantal/Grootte. Natuurlijk niet te groot maken...
enemy_size = 20.0
enemy_centrepoint = enemy_size / 2.0
enemy_rows = 3
enemy_row_amount = 13
enemy_amount = enemy_row_amount * enemy_rows

# Score
level = 1
points = 0

# Speler
lives = 3
player_size = 22.0

# Plaatjes
heart = simplegui.load_image('http://googledrive.com/host/0BzEhWaGeU4yUVEFJazFnVDJiRFE/hart.png')
heartwh = [heart.get_width(), heart.get_height()]
heartcenter = [heartwh[0] / 2, heartwh[1] / 2]

# Overige variabelen.
PADDING = (BORDER[0] + enemy_size * 4.0, BORDER[1] + enemy_size * 0.64285714285)
SPACING = (WIDTH - PADDING[0] * 2.0) / (enemy_row_amount - 1)
CheckX = PADDING[0] - BORDER[0] - enemy_centrepoint


# Class voor de vijanden van level 1
class Enemy_One(object):
    image = simplegui.load_image('http://googledrive.com/host/0BzEhWaGeU4yUVEFJazFnVDJiRFE/vijand1wit.png')
    width = image.get_width()
    height = image.get_height()
    wh = (width, height)
    center = (width / 2.0, height / 2.0)
    size = (enemy_size,enemy_size * height/width)
    vx = enemy_size/20.0/60.0
    vy = enemy_size / 2.0
    att = 2000
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.pos = [float(self.x),float(self.y)]
        self.x_org = self.pos[0]
    # Verandert positie vijanden    
    def update(self):
        self.x = self.pos[0]
        self.y = self.pos[1]
    # Bepaalt of de vijanden schieten of niet.
    def shoot(self):
        self.attack = random.randrange(0,int(self.att))
        if 6 <= self.attack <= 7:
                EnemyLaserList.append(EnemyLaser(self.pos[0], self.pos[1]))
    # Bepaalt of de vijanden worden geraakt of niet
    def laserhit(self, laser):
        global wait, FriendLaserList
        if abs(self.x - laser.x) < self.size[0] / 2.0 and abs(self.y - laser.y) < self.size[1] / 2.0:
            laser.x = WIDTH
            laser.y = HEIGHT
            laser.pos = [WIDTH, HEIGHT]
            laser.vy = 0
            wait = 0.0
            FriendLaserList = []
            kill(self)
# Class voor de Fighter (jij)
class Fighter(object):
    image = simplegui.load_image('http://googledrive.com/host/0BzEhWaGeU4yUVEFJazFnVDJiRFE/speler.png')
    width = image.get_width()
    height = image.get_height()
    wh = (width, height)
    center = (width / 2.0, height / 2.0)
    vx = 0
    vx_org = 5
    size = (player_size, player_size * height/width)
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.pos = [self.x, self.y]
    def move(self):
        if BORDER[0] + player_size / 2.0 < self.x < WIDTH - BORDER[0] - player_size / 2.0:
            self.x += self.vx
            self.pos = [self.x, self.y]
        elif self.x <= BORDER[0] + player_size / 2.0:
            self.x += 1
        elif self.x >= WIDTH - BORDER[0] - player_size / 2.0:
            self.x -= 1
   # Kijken of de speler wordt geraakt door een laser
    def laserhit(self, laser):
        global lives
        if abs(self.x - laser.x) < self.size[0] / 2.0 and abs(self.y - laser.y) < self.size[1] / 2.0:
            lives -= 1
            elaserkill(laser)
    # Schieten
    def shoot(self):
        global FriendLaserList
        FriendLaserList.append(FriendlyLaser(self.x, self.y))
FriendLaserList = []
# Class voor de laser van de speler
class FriendlyLaser(object):
    image = simplegui.load_image('http://googledrive.com/host/0BzEhWaGeU4yUVEFJazFnVDJiRFE/lasergroen.png')
    width = image.get_width()
    height = image.get_height()
    wh = (width, height)
    center = (width / 2, height / 2)
    vy = -10
    size = (enemy_size / 10.0, enemy_size * 4 / 10.0)
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.pos = [self.x, self.y]
    # Verandert positie vriendelijke laser.
    def update(self):
        self.y += self.vy
        self.pos = [self.x, self.y]
        if self.y < BORDER[1] or self.pos == [WIDTH, HEIGHT]:
            flaserkill(self)
# Class voor de vijand lasers
class EnemyLaser(object):
    image = simplegui.load_image('http://googledrive.com/host/0BzEhWaGeU4yUVEFJazFnVDJiRFE/laser.png')
    width = image.get_width()
    height = image.get_height()
    wh = (width, height)
    center = (width / 2, height / 2)
    vy = 5
    size = (enemy_size / 10.0, enemy_size * 4 / 10.0)
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.pos = [self.x, self.y]
    # Verandert positie vijandelijke laser.
    def update(self):
        self.y += self.vy
        self.pos = [self.x, self.y]
        if self.y > HEIGHT - 10 or self.pos == [WIDTH, HEIGHT]:
            elaserkill(self)
# Speler
player = Fighter(WIDTH / 2.0, (HEIGHT - BORDER[1]) + (BORDER[1] / 2.0))
# Vijand Laser list
EnemyLaserList = []
# Level 1 vijanden maken
EnemyList_1 = []
def level_1():
    global EnemyList_1, lives, points, EnemyLaserList, level, FriendLaserList, wait
    wait = 0.0
    level = 1
    lives = 3
    points = 0
    EnemyList_1 = []
    EnemyLaserList = []
    FriendLaserList = []
    for i in range(0,enemy_amount + 1):
        # Rij 1
        if i < enemy_row_amount:
            EnemyList_1.append(Enemy_One(PADDING[0] + SPACING * (i),PADDING[1]))
        # Rij 2
        elif enemy_row_amount < i <= enemy_row_amount * 2:
            EnemyList_1.append(Enemy_One(PADDING[0] + SPACING * (i-enemy_row_amount - 1),PADDING[1] + SPACING))
        # Rij 3
        elif i > enemy_row_amount * 2:
            EnemyList_1.append(Enemy_One(PADDING[0] + SPACING * (i-enemy_row_amount * 2 - 1),PADDING[1] + SPACING * 2))

# Vijanden positie verplaatsen
def posupdate():
    global lives
    for enemy in EnemyList_1:
        if enemy.x_org - CheckX <= enemy.pos[0] <= enemy.x_org + CheckX:
            enemy.pos[0] += enemy.vx
        elif enemy.y + enemy.wh[1] >= HEIGHT - BORDER[1]:
            lives = 0
        else:
            enemy.vx = -enemy.vx
            enemy.pos[0] += enemy.vx
            enemy.pos[1] += enemy.vy
# Haal vijanden weg
def kill(enemy):
    global points
    points += 1
    for enemies in EnemyList_1:
        if enemies.vx > 0:
            enemies.vx += 1.0/4/60
        elif enemies.vx < 0:
            enemies.vx -= 1.0/4/60
        if enemies.att > 2000 * 0.95 ** 10:
            enemies.att = enemy.att * 0.95
        elif enemies.att < 2000 * 0.95 ** 10:
            enemies.att = enemy.att * 0.97
    EnemyList_1.remove(enemy)
    print len(EnemyList_1)
def elaserkill(laser):
    EnemyLaserList.remove(laser)
def flaserkill(laser):
    FriendLaserList.remove(laser)
def draw(canvas):
    global level, points, lives, wait
    # Tekst, score, levens, etc.
    canvas.draw_text('LEVENS: ',(BORDER[0] + 10, 20),20, 'white', 'monospace')
    canvas.draw_text('SCORE: ' + str(points),(BORDER[0] + 10, 20 * 2),20, 'white', 'monospace')
    canvas.draw_text('LEVEL: ' + str(level),(WIDTH - BORDER[0] - frame.get_canvas_textwidth('LEVEL:  ' + str(level),20,'monospace'), 20 * 2),20, 'white', 'monospace')
    for i in range(1, lives + 1):
        canvas.draw_image(heart,
                          heartcenter,
                          heartwh,
                            [BORDER[0] + 10 + text_levens + 20 * i,
                             13],
                            (20,
                             20.0 * heartwh[1]/heartwh[0]))
    # Borders voor debuggen
    canvas.draw_line((BORDER[0],0),
                     (BORDER[0],HEIGHT),
                     BORDER[2],
                     BORDER[3]
                     )
    canvas.draw_line((WIDTH-BORDER[0],0),
                     (WIDTH-BORDER[0],HEIGHT),
                     BORDER[2],
                     BORDER[3]
                     )
    canvas.draw_line((0,BORDER[1]),
                     (WIDTH,BORDER[1]),
                     BORDER[2],
                     BORDER[3]
                     )
    canvas.draw_line((0,HEIGHT - BORDER[1]),
                     (WIDTH,HEIGHT - BORDER[1]),
                     BORDER[2],
                     BORDER[3]
                     )
    # Teken vijandelijke lasers
    for laser in EnemyLaserList:
        laser.update()
        canvas.draw_image(laser.image,
                          laser.center,
                          laser.wh,
                          laser.pos,
                          laser.size)
    if points == enemy_amount and level == 1:
        #level = 2
        #levellabel.set_text("Level 2")
        if lives < 4:
            lives += 1
        canvas.draw_text("Je hebt gewonnen!",
                         (WIDTH / 2 - frame.get_canvas_textwidth('Je hebt gewonnen!', 40, 'monospace') / 2,HEIGHT / 2),
                         40, 'white', 'monospace')
        canvas.draw_text("Druk op 'R' voor een replay!",
                         (WIDTH / 2 - 10 - frame.get_canvas_textwidth('Druk op 'R' voor een replay!', 20, 'monospace') / 2,HEIGHT / 2 + HEIGHT / 8),
                         20, 'white', 'monospace')
    if lives <= 0:
        canvas.draw_text("Je hebt verloren!",
                         (WIDTH / 2 - frame.get_canvas_textwidth('Je hebt verloren!', 40, 'monospace') / 2,HEIGHT / 2),
                         40, 'white', 'monospace')
        canvas.draw_text("Druk op 'R' voor een replay!",
                         (WIDTH / 2 - 10 - frame.get_canvas_textwidth('Druk op 'R' voor een replay!', 20, 'monospace') / 2,HEIGHT / 2 + HEIGHT / 8),
                         20, 'white', 'monospace')
    else:
        # Teken & beweeg speler
        player.move()
        canvas.draw_image(player.image,
                          player.center,
                          player.wh,
                          player.pos,
                          player.size)
        # Laser hit checken voor de speler
        for laser in EnemyLaserList:
                player.laserhit(laser)
        # Schieten check
        if wait > 0.0:
            wait -= 1/60.0
        else:
            wait = 0.0
        # Laser v.d. speler tekenen
        for laser in FriendLaserList:
            laser.update()
            canvas.draw_image(laser.image,
                              laser.center,
                              laser.wh,
                              laser.pos,
                              laser.size)
        # Level 1 vijanden
        if level == 1:        
            # Update posities
            for enemy in EnemyList_1:
                posupdate()
                enemy.update()
                enemy.shoot()
                for laser in FriendLaserList:
                    enemy.laserhit(laser)

            # Teken vijanden
            for enemy in EnemyList_1:
                canvas.draw_image(enemy.image,
                                enemy.center,
                                enemy.wh,
                                enemy.pos,
                                enemy.size)
    # Debug Labels
    pointslabel.set_text("Punten: " + str(points))
    liveslabel.set_text("Levens: " + str(lives))
# Debug vermoorden van vijanden
# Ze worden sneller naarmate er meer doodgaan.
# Ze gaan meer aanvallen naarmate er meer doodgaan.
indexlist = []
wait = 0.0
def keydown(key):
    global wait
    # Speler bewegen naar links
    if key == simplegui.KEY_MAP['left']:
        player.vx = -player.vx_org
    # Speler bewegen naar rechts
    elif key == simplegui.KEY_MAP['right']:
        player.vx = player.vx_org
    # Spel opnieuw spelen
    elif key == simplegui.KEY_MAP['r']:
        level_1()
    # Schieten
    elif key == simplegui.KEY_MAP['space']:
        if wait == 0.0:
            wait = 1.0
            player.shoot()
def keyup(key):
    if key == simplegui.KEY_MAP['left']:
        player.vx = 0
    elif key == simplegui.KEY_MAP['right']:
        player.vx = 0

def button_kill():
    global points, indexlist
    # 'Dood' vijand, voor testen!
    kill(EnemyList_1[0])

def button_lives_add():
    global lives
    lives += 1
def button_lives_del():
    global lives
    lives -= 1
# Frame maken
frame = simplegui.create_frame("Game",WIDTH,HEIGHT)
frame.set_draw_handler(draw)
frame.set_keyup_handler(keyup)
frame.set_keydown_handler(keydown)

text_levens = frame.get_canvas_textwidth('LEVENS:', 20, 'monospace')

# Debug buttons.
debuglabel = frame.add_label("Debug knoppen.")
button = frame.add_button("Random Kill Enemy",button_kill,150)
pointslabel = frame.add_label("")
liveslabel = frame.add_label("")
levellabel = frame.add_label("Level: " + str(level))
button2 = frame.add_button("Extra leven",button_lives_add,150)
button2 = frame.add_button("Een leven minder",button_lives_del,150)

# Starten (level 1 & frame)
level_1()
frame.start()

If you need any more specific parts of my code please ask. I hear that people don't like it whehn they have to read through an entire code...

2

There are 2 best solutions below

0
On

I fixed it! I took the posupdate() function from my code and rewrote it under the update class from my enemies, I don't know what was actually going on but it's fixed and the game evens runs a lot better.

What I did: I turned posupdate() into update(self) under Class Enemy_One

def posupdate():
global lives
for enemy in EnemyList_1:
    if enemy.x_org - CheckX <= enemy.pos[0] <= enemy.x_org + CheckX:
        enemy.pos[0] += enemy.vx
    elif enemy.y + enemy.wh[1] >= HEIGHT - BORDER[1]:
        lives = 0
    else:
        enemy.vx = -enemy.vx
        enemy.pos[0] += enemy.vx
        enemy.pos[1] += enemy.vy

Everythings works now!

class Enemy_One(object):
image = simplegui.load_image('http://googledrive.com/host/0BzEhWaGeU4yUVEFJazFnVDJiRFE/vijand1wit.png')
width = image.get_width()
height = image.get_height()
wh = (width, height)
center = (width / 2.0, height / 2.0)
size = (enemy_size,enemy_size * height/width)
vx = 0.5
vy = enemy_size / 2.0
att = 2000
def __init__(self, x, y):
    self.x = x
    self.x_org = self.x
    self.y = y
    self.pos = [self.x,self.y]
# Verandert positie vijanden    
def update(self):
    global lives
    if self.y + self.wh[1] >= HEIGHT - BORDER[1]:
        lives = 0
    if self.x <= self.x_org - CheckX:
        self.x = self.x_org - CheckX
        self.vx = -self.vx
        self.x += self.vx
        self.y += self.vy
    elif self.x >= self.x_org + CheckX:
        self.x = self.x_org + CheckX
        self.vx = -self.vx
        self.x += self.vx
        self.y += self.vy
    else:
        self.x += self.vx
    self.pos = [self.x, self.y]
2
On

My thought is that this is performance-related. When you have a bunch of enemies, it takes longer to draw them. I don't know how simplegui handles this, but it looks like you expect it to give you a frame 60 times a second, which may not be a reasonable assumption.