Reputation: 1102
As you might probably be able to guess, I'm quite new to Python! I'm trying to write a (very) simple Space Invaders game. Nothing flashy. No sounds, no explosions, not even any scores tracking (although, at some point, the aliens will move and shoot!). Right now though the problem with my code is that I can shoot (and destroy) any aliens in the bottom row - but I can't destroy aliens in any other row. It's most perplexing - and I'd welcome any advice that anyone can offer. This is my code:
# !/usr/bin/python
import pygame
bulletDelay = 40
class Bullet(object):
def __init__(self, xpos, ypos):
self.image = pygame.image.load("bullet.bmp")
self.rect = self.image.get_rect()
self.x = xpos
self.y = ypos
def current_position(self):
return [self.x, self.y]
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Player(object):
def __init__(self, screen):
self.image = pygame.image.load("spaceship.bmp") # load the spaceship image
self.rect = self.image.get_rect() # get the size of the spaceship
size = screen.get_rect()
self.x = (size.width * 0.5) - (self.rect.width * 0.5) # draw the spaceship in the horizontal middle
self.y = size.height - self.rect.height # draw the spaceship at the bottom
def current_position(self):
return self.x
def draw(self, surface):
surface.blit(self.image, (self.x, self.y)) # blit to the player position
class Alien(object):
def __init__(self, xpos, ypos):
self.image = pygame.image.load("alien.bmp") # load the alien image
self.rect = self.image.get_rect() # get the size of the alien
self.x = xpos
self.y = ypos
def current_position(self):
return [self.x, self.y]
def draw(self, surface):
surface.blit(self.image, (self.x, self.y)) # blit to the player position
def collision(alien,bullet):
if ((alien.current_position()[0] < bullet.current_position()[0] + 10) and
(alien.current_position()[0] > bullet.current_position()[0] - 10) and
(alien.current_position()[1] == bullet.current_position()[1])):
return True
else:
return False
def destroyObject(objectArray,killList):
if len(killList) > 0: # remove any bullets that have hit an alien
for item in killList:
del objectArray[item]
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
player = Player(screen) # create the player sprite
missiles = [] # create missile array
aliensW = 6
aliensH = 3
# layout the initial field of aliens
aliens = [[Alien((screen.get_rect().width/7)*(x+0.75),(screen.get_rect().height/5)*(y+0.5)) for x in range(aliensW)] for y in range(aliensH)]
running = True
counter = bulletDelay
while running: # the event loop
counter=counter+1
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
dist = 5 # distance moved for each key press
if key[pygame.K_RIGHT]: # right key
player.x += dist
elif key[pygame.K_LEFT]: # left key
player.x -= dist
elif key[pygame.K_SPACE]: # fire key
if counter > bulletDelay:
missiles.append(Bullet(player.current_position(),screen.get_rect().height))
counter=0
screen.fill((255, 255, 255)) # fill the screen with white - without this the old graphics will remain onscreen.
for m in missiles:
if m.y < (screen.get_rect()).height+1 and m.y > 0:
m.draw(screen)
m.y -= 5
else:
missiles.pop(0)
alienGrid = []
for a in aliens:
killList=[]
spentBullets=[]
alienNumber=0
alienRow = a
for b in alienRow:
#need to move aliens as well!
missileNumber=0
for m in missiles:
if (collision(b,m)):
killList.insert(0,alienNumber)
spentBullets.insert(0,missileNumber)
missileNumber+=1
alienNumber += 1
b.draw(screen)
destroyObject(alienRow,killList)
destroyObject(missiles,spentBullets)
alienGrid.insert(0,alienRow)
aliens = alienGrid
player.draw(screen) # draw the spaceship to the screen
pygame.display.update() # update the screen
clock.tick(40)
Of course, I also welcome any other advice that you might be able to offer for improving my code!
Upvotes: 0
Views: 976
Reputation: 142641
Use print()
to check values in variables and which part of code is executed. This way you can find problem (if you don't know how to use debuger)
Problem is
alien.current_position()[1] == bullet.current_position()[1]
which not always is true because missile moves 5 pixels, not 1px.
Besides you use 0.75
and 0.5
in calculations of alien positions so you have float values.
You could use self.rect
to keep position and then you could use
def collision(alien, bullet):
return alien.rect.colliderect(bullet.rect)
and
def draw(self, surface):
surface.blit(self.image, self.rect)
My version with many modifications. I use class inherition. And I use time (instead of counting frame) to control missiles.
# !/usr/bin/python
import pygame
# --- constants ---
BULLET_DELAY = 1000 # 1000ms = 1s
BULLET_DIST = 5 # distance moved for each key press
RED = (255, 0, 0)
WHITE = (255, 255, 255)
FPS = 40
# --- classes ---
class Sprite(object):
def __init__(self, xpos, ypos, image=None):
if image:
self.image = pygame.image.load(image)
else:
self.image = pygame.Surface(50,50)
self.image.fill(RED)
self.rect = self.image.get_rect(x=xpos, y=ypos)
def current_position(self):
return self.rect
def draw(self, surface):
surface.blit(self.image, self.rect)
class Bullet(Sprite):
def __init__(self, xpos, ypos):
Sprite.__init__(self, xpos, ypos, "bullet.bmp")
# modificate position
self.rect.centerx = xpos
self.rect.bottom = ypos
class Alien(Sprite):
def __init__(self, xpos, ypos):
Sprite.__init__(self, xpos, ypos, "alien.bmp")
class Player(Sprite):
def __init__(self, xpos, ypos):
Sprite.__init__(self, xpos, ypos, "spaceship.bmp")
# modificate position
self.rect.centerx = xpos
self.rect.bottom = ypos
# --- main ---
pygame.init()
screen = pygame.display.set_mode((640, 480))
screen_rect = screen.get_rect()
# -
player = Player(screen_rect.centerx, screen_rect.bottom) # create the player sprite
# -
missiles = [] # create missile array
# -
aliensW = 6
aliensH = 3
# layout the initial field of aliens
sx = screen_rect.width/7
sy = screen_rect.width/7
aliens = [[Alien(sx*(x+0.75),sy*(y+0.5)) for x in range(aliensW)] for y in range(aliensH)]
# move direction
move_left = False
# ---
current_time = pygame.time.get_ticks()
# missile delay
next_missile_time = current_time
# --- mainloop ---
clock = pygame.time.Clock()
running = True
while running: # the event loop
current_time = pygame.time.get_ticks()
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
key = pygame.key.get_pressed()
if key[pygame.K_RIGHT]: # right key
player.rect.x += BULLET_DIST
if player.rect.right > screen_rect.right:
player.rect.right = screen_rect.right
if key[pygame.K_LEFT]: # left key
player.rect.x -= BULLET_DIST
if player.rect.left < screen_rect.left:
player.rect.left = screen_rect.left
if key[pygame.K_SPACE]: # fire key
if current_time >= next_missile_time:
print('[D] fire')
missiles.append(Bullet(player.rect.centerx, player.rect.top))
# missile delay
next_missile_time = current_time + BULLET_DELAY
# --- updates (without draws) ---
# move missiles (and remove if leaves screen)
temp = []
for m in missiles:
m.rect.y -= BULLET_DIST
if m.rect.bottom >= 0:
temp.append(m)
missiles = temp
# move aliens (and check collisio)
temp = []
next_direction = move_left
for row in aliens:
temp_row = []
for a in row:
if move_left:
# move
a.rect.x -= 1
# check collision
for i, m in enumerate(missiles):
if a.rect.colliderect(m.rect):
# remove missile
del missiles[i]
# don't check collisions with other misilles
break
else: # chech only if "no break" so "no collide"
# chech if change direction
if a.rect.left == screen_rect.left:
# need to change direction but don't change `move_left` yet
next_direction = False
# add if not collide
temp_row.append(a)
else:
# move
a.rect.x += 1
# check collision
for i, m in enumerate(missiles):
if a.rect.colliderect(m.rect):
# remove missile
del missiles[i]
# don't check collisions with other misilles
break
else: # chech only if "no break" so "no collide"
# chech if change direction
if a.rect.right == screen_rect.right:
# need to change direction but don't change `move_left` yet
next_direction = True
# add if not collide
temp_row.append(a)
temp.append(temp_row)
# change after all checkings
move_left = next_direction
# new list without hitted aliens
aliens = temp
# --- draws (without updates) ---
screen.fill(WHITE) # fill the screen with white - without this the old graphics will remain onscreen.
for row in aliens:
for a in row:
a.draw(screen)
for m in missiles:
m.draw(screen)
player.draw(screen) # draw the spaceship to the screen
pygame.display.update() # update the screen
# --- FPS ---
clock.tick(FPS)
Upvotes: 1