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...
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
Everythings works now!