I am making a game and I need the player to be constantly moving in one direction until the player decides to move in a different also constantly. In the code I have supplied the player has to hold down a key to move in that direction.
Here is my code for the player and the function for moving:
class player:
def __init__(self, x, y, width, height, colour):
self.width = width # dimensions of player
self.height = height # dimensions of player
self.x = x # position on the screen
self.y = y # position on the screen
self.colour = colour # players colour
self.rect = (x, y, width, height) # all the players properties in one
self.vel = 2 # how far/fast you move with each key press
self.path = []
def draw(self, win):
pygame.draw.rect(win, self.colour, self.rect)
def move(self):
keys = pygame.key.get_pressed() # dictionary of keys - values of 0/1
if keys[pygame.K_LEFT]: # move left: minus from x position value
if self.x <= 5:
pass
else:
self.x -= self.vel
self.y = self.y
elif keys[pygame.K_RIGHT]: # move right: add to x position value
if self.x == 785:
pass
else:
self.x += self.vel
elif keys[pygame.K_UP]: # move up: minus from y position value
if self.y <= 105:
pass
else:
self.y -= self.vel
elif keys[pygame.K_DOWN]: # move down from
if self.y >= 785:
pass
else:
self.y += self.vel
self.update()
def update(self):
self.rect = (self.x, self.y, self.width, self.height) # redefine where the player is
Here is my code for the main function:
def main(): # asking server for updates, checking for events
run = True
n = Network()
startPos = read_pos(n.getPos())
pygame.mixer.music.load("Heroic_Intrusion.ogg")
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play(-1)
p = player(startPos[0], startPos[1], 10, 10, (255, 0, 0)) # connect, get starting position
p2 = player(0, 0, 10, 10, (0, 0, 255))
clock = pygame.time.Clock()
while run:
clock.tick(60)
p2Pos = read_pos(n.send(make_pos((p.x, p.y))))
p2.x = p2Pos[0]
p2.y = p2Pos[1]
p2.update()
for event in pygame.event.get():
if event.type == pygame.QUIT: # quitting condition
run = False
pygame.quit()
p.move() # move character based off what keys are being pressed
redrawWindow(win, p, p2)
One approach is to just set a movement amount in the player class. Each frame of the game, the player moves by a little in the set speed. Separate speeds are maintained for each of the direction-components of the movement. This allows the player to move diagonal.
class Player():
def __init__( self ):
...
self.x_vel = 0
self.y_vel = 0
def move(self):
keys = pygame.key.get_pressed() # dictionary of keys - values of 0/1
if keys[pygame.K_LEFT]: # move left: minus from x position value
self.x_vel = -1
if keys[pygame.K_RIGHT]:
self.x_vel = 1 # move right: plus from x position value
# etc. for up/down
def update( self ):
self.x += self.x_vel
self.y += self.y_vel
In the main loop, simply call the player.update() function.
A downside to this approach is that the on-screen speed is relative to the frame-rate. In this case the code could use the pygame millisecond clock to use an actual real-time speed in pixels/second. Or calculate the speed based on the actual FPS, and set the player.x_vel (etc.) accordingly.
Related
This question already has an answer here:
How do I get the snake to grow and chain the movement of the snake's body?
(1 answer)
Closed 24 days ago.
Right now I'm learning oop so I want to practice some on this snake game project.
So when i want to add snake parts when he eat the food i don't know what to do.
here is my code .give me your opinion about it (if you want).(=
import pygame, random, math
pygame.init()
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
BLACK = (0,0,0)
WHITE = (255,255,255)
WINDOW_W = 800
WINDOW_H = 600
BACKGROUND_COLOR = BLACK
clock = pygame.time.Clock()
SNAKE_WIDTH = 25
SNAKE_HEIGHT = 25
SNAKE_COLOR = GREEN
SNAKE_VEL = 25
FOOD_WIDTH = SNAKE_WIDTH
FOOD_HEIGHT = SNAKE_HEIGHT
FOOD_COLOR = RED
FPS = 10
win = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption('G_G')
#-------------------------------------------------------------------------------------------
class Snake:
def __init__(self, WIDTH ,HEIGHT, COLOR, VELOCITY, x, y):
self.WIDTH = WIDTH
self.HEIGHT = HEIGHT
self.COLOR = COLOR
self.VEL = VELOCITY
self.x = x
self.y = y
def draw(self):
pygame.draw.rect(win, self.COLOR, (self.x, self.y, self.WIDTH, self.HEIGHT))
def spawn(self):
if self.x<0:
self.x = WINDOW_W + SNAKE_WIDTH
if self.x > WINDOW_W + SNAKE_WIDTH:
self.x = 0
if self.y < 0:
self.y = WINDOW_H + SNAKE_HEIGHT
if self.y > WINDOW_H + SNAKE_HEIGHT:
self.y = 0
snake = Snake(SNAKE_WIDTH, SNAKE_HEIGHT, SNAKE_COLOR, SNAKE_VEL,1, 1)
#--------------------------------------------------------------------------------------------
class Food:
def __init__(self, WIDTH ,HEIGHT, COLOR, x=random.randrange(1, WINDOW_W+1, FOOD_WIDTH), y=random.randrange(1, WINDOW_H+1, FOOD_HEIGHT)):
self.WIDTH = WIDTH
self.HEIGHT = HEIGHT
self.COLOR = COLOR
self.x = x
self.y = y
def draw(self):
pygame.draw.rect(win, self.COLOR, (self.x, self.y, self.WIDTH, self.HEIGHT))
def snake_eats_food():
dis_snake_food = math.sqrt(((snake.x-food.x))**2+(snake.y-food.y)**2)
if abs(dis_snake_food)<4:
food.x = random.randrange(1, WINDOW_W+1, FOOD_WIDTH)
food.y = random.randrange(1, WINDOW_H+1, FOOD_HEIGHT)
return True
else:
return False
food = Food(FOOD_WIDTH, FOOD_WIDTH, FOOD_COLOR)
#-----------------------------------------------------------------------------------------
def draw():
win.fill(BACKGROUND_COLOR)
for x in range(0, WINDOW_W+1, SNAKE_WIDTH):
pygame.draw.line(win, WHITE, (x, 0), (x, WINDOW_H), 2)
for y in range(0, WINDOW_H+1, SNAKE_HEIGHT):
pygame.draw.line(win, WHITE, (0, y), (WINDOW_W, y), 2)
snake.draw()
food.draw()
def main():
run = True
up,down,right,left = False,False,False,False
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#-----------------------------------
draw()
snake_eats_food()
pygame.display.update()
#------------------------------------
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
up,down,right,left = True,False,False,False
if keys[pygame.K_DOWN]:
down,up,right,left = True,False,False,False
if keys[pygame.K_RIGHT]:
right,down,up,left = True,False,False,False
if keys[pygame.K_LEFT]:
left,down,right,up = True,False,False,False
if up:
snake.y -= snake.VEL
snake.spawn()
if down:
snake.y += snake.VEL
snake.spawn()
if right:
snake.x += snake.VEL
snake.spawn()
if left:
snake.x -= snake.VEL
snake.spawn()
#-----------------------------------
pygame.quit()
if __name__ == "__main__":
main()
I know the code is a bit messy because I wanted to try to implement OOP, since I never used it.
To render the snake:
I would recommend using a list of coordinate pairs named self.snake_array which represents points along the snake. Every unit you go forward, append the current snake coordinate pair to the end of self.snake_array and delete the first element in the coordinate pair array if the array length exceeds self.snake_length.
. I recommend having a self.snake_length variable that determines how many units long the snake is. When you run snake_eats_food, check if it returned true. If it returned true, increase the self.snake_length. All you have left to do is fix your snake draw function so that it loops through the self.snake_array and draws a square at each coordinate.
Hope that helps!
I am having the issue that, my colliders aren't accurate. No matter how I tweak the numbers, they wont work. The eagle, the birds collider is accurate, but the collider of the box/obstacle isn't. The rectangle I use as a collider is the same size as the image of the box, and should be centered on it, but for some reason, it isn't.
import pygame as pg # imports life juice
eagleImage = pg.image.load("playerflappybirdscaled.png") # loads fucking eagle image
eagleCollider = pg.Surface.get_bounding_rect(eagleImage) # better collider
boxImage = pg.image.load("boxcollider.png") # box image go load
boxCollider = pg.Surface.get_bounding_rect(boxImage) # makes box rect
class Box(object): # makes box
def __init__(self, x, y):
self.image = boxImage # image of a box
self.collider = boxCollider # collider for said box
self.x = x # box xpos
self.y = y # box ypos
self.collider.center = self.x, self.y
def checkCollision(self, rect): # checks for collision
self.collider.center = self.x, self.y # sets collider
colliderToCheckFor = rect # gets other collider
collision = boxCollider.colliderect(colliderToCheckFor) # checks for collision
if collision: # returns car crash
return True
else:
return False
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Bird(object): # bird go flyyyyyyyyyyy
def __init__(self):
self.image = eagleImage # bird go display
self.collider = eagleCollider # bird gets collider
self.x = 320 # bird posx
self.y = 200 # bird posy
def key_handler(self, moveDist, direction): # manhandles keys
key = pg.key.get_pressed() # gets manhandled keys
dist = 2 # distance in FREEDOM UNITS jk
if key[pg.K_UP]: # key go up
self.y -= dist
elif key[pg.K_SPACE]: # key go up
self.y -= dist
elif key[pg.K_w]: # key go up
self.y -= dist
elif key[pg.K_a]: # key go left
self.x -= dist
if moveDist == 0: # checks if distance moved is wanted
return # if not returns
else:
if direction == "y": # checks direction wanted
self.y += moveDist # does shit
elif direction == "x":
self.x += moveDist
def draw(self, surface): # toddler draws image
surface.blit(self.image, (self.x, self.y))
self.collider.center = self.x, self.y
pg.init() # starts pygame
s = pg.display.set_mode((640, 400)) # sets screen
box = Box(320, 50)
bird = Bird() # biiiiiird
clock = pg.time.Clock() # clock for fps and shit
running = True # if runs
while running: # when runs
event = pg.event.poll() # get event
if event.type == pg.QUIT: # if not run go quit
pg.quit()
running = False
break
bird.key_handler(1, "y") # bird mauls keys
s.fill((255, 0, 0)) # give screen color
box.draw(s)
bird.draw(s) # take drawing from toddler
pg.display.update() # put drawing on display
if box.checkCollision(eagleCollider) == True:
running = False
pg.quit()
clock.tick(60) # tick tack
So as I have said, I have tried tweaking every number, and I see no reason why the colliders wouldn't be accurate.
Edit to highlight the difference between questions this one has been associated with. I have actually followed and implemented answers from one of these questions to the best of my ability, and as far as I can tell, I have followed what was answered there.
See Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?.
The instruction surface.blit(self.image, (self.x, self.y)) draws self.image at the topleft position (self.x, self.y):
class Box(object): # makes box
def __init__(self, x, y):
self.image = boxImage # image of a box
self.rect = self.image.get_rect(topleft = (x, y))
self.collider = self.image.get_bounding_rect()
self.collider.x += x
self.collider.y += y
def checkCollision(self, rect): # checks for collision
collision = self.collider.colliderect(rect)
return collision
def draw(self, surface):
surface.blit(self.image, self.rect)
I am making a simple variation of space invaders using pygame. Here is some working code:
import pygame, random, time, tkinter
pygame.init()
def main():
X = 672
Y = 500
win = pygame.display.set_mode((X,Y))
pygame.display.set_caption('Space Invaders')
class player(object):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.lives = 3
self.vel = 5
self.points = 0
self.explosionYN = False
self.explosionCount = 0
self.highScore = int(text)
def explosion(self, win):
if self.explosionYN:
win.blit(explosion[self.explosionCount], (self.x-20, self.y-30, 100, 100))
self.explosionCount += 1
if not self.explosionYN:
self.explosionCount = 0
if self.explosionCount + 1 > 6:
self.explosionYN = False
self.explosionCount = 0
def move(self, win):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.x > -1:
self.x -= self.vel
if keys[pygame.K_RIGHT] and self.x < 623:
self.x += self.vel
if keys[pygame.K_q]:
if self.x >= 623:
self.x = 0
if self.x < 0:
self.x = 623
class invader(object):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 1
def reset(self, win):
self.y = 0
self.x = random.randint(5, (X-35))
class bullet(object):
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 0
def shoot(self, win):
laser.play(0)
self.x = ship.x + 22
self.y = ship.y
self.vel = 15
def reset(self, win):
self.vel = 0
self.y = 510
ship = player(X//2 - (0.5*52), 435, 52, 52)
alien = invader(random.randint(0,707),0,31,25)
bullet = bullet(750, 510, 5, 7)
while run:
pygame.time.delay(25)
alien.y += alien.vel
bullet.y -= bullet.vel
if alien.y > 500:
ship.explosionYN = True
ship.explosion(win)
loseLife.play(0)
if ship.explosionCount+ 1 >= 6:
win.fill((0,0,0))
pause()
ship.lives -= 1
alien.reset(win)
elif ship.lives <= 1:
test()
if bullet.y <= 0:
bullet.reset(win)
alien1 = pygame.draw.rect(win, (0,0,0), (alien.x,alien.y,31,25))
bullet1 = pygame.draw.rect(win, (0,100,255), (bullet.x, bullet.y, bullet.width, bullet.height))
if pygame.Rect.colliderect(alien1, bullet1):
ship.points += 1
if ship.highScore < ship.points:
ship.highScore += 1
bullet.reset(win)
kill.play(0)
alien.y = 0
alien.reset(win)
alien.vel += 0.5
ship.vel += 0.25
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
keys = pygame.key.get_pressed()
ship.move(win)
if keys[pygame.K_SPACE] or keys[pygame.K_UP]:
bullet.shoot(win)
if keys[pygame.K_p]:
pause()
drawGame()
main()
I have omitted some code that I don't think is relevant
The problem is that only one bullet can be displayed on screen at a time.
So I tried this instead.
if keys[pygame.K_SPACE] or keys[pygame.K_UP]:
bullet1 = pygame.draw.rect(win, (#stuff))
bullet.shoot(win)
But now the bullet doesn't show at all.
Literally nothing happens.
I have looked at some other posts but as I am new to pygame I can't make head or tail of them.(Multiple Bullets pygame)
What is an easy and effiecient way to be able show multiple bullets on pygame?
thanks.
The typical way that this is done is by creating a list of bullets.
bullets = []
Then when you fire a bullet you add it to the list
bullets.append(Bullet(750, 510, 5, 7))
And inside your while loop, you will update and redraw each bullet in turn using a for loop to iterate over each bullet
for bullet in bullets:
bullet.y -= bullet.vel # Move Bullet
# Draw bullet
# Check for collision with player/enemy
This obviously isn't a complete code listing but hopefully it is enough to get you started.
You'll also end up having to create a list of enemies too if you want to create a space invaders clone.
You may find the recently released book "Code the Classics" from the Raspberry Pi Foundation helpful as it explains how to create some classic games using Pygame. It's a free download (or you can buy the hardcopy)
Edit: Consider following the Python style guide PEP-8 and renaming your classes to be title case. For example
class bullet():
should be
class Bullet():
This will help with any confusion between the variable bullet and the class of the same name.
Creating a platformer in pygame and im using vectors for the gravity and movement. Right now im stuck on the collisions with the platforms because the plat will hit the platform and sink almost entirely in the platform like this:
Player sunk into platform block
(EDIT) People wanted more code info so...
Main Class:
import pygame as pg
import sys
from sprites import *
class Game:
def __init__(self):
pg.init()
pg.mixer.init()
pg.display.set_caption(TITLE)
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
self.clock = pg.time.Clock()
self.running = True
self.load_data()
self.new_game()
def load_data(self):
# Eventually will load data
pass
def new_game(self):
# Creates new instances of all of the sprites
# groups and re-renders sprites at starting locations
self.all_sprites = pg.sprite.Group()
self.platform_sprites = pg.sprite.Group()
self.player = Player(self, 60, 60)
# Creates some test platforms
for x in range(10):
Platform(self, x, 6)
def run(self):
# Basic game loop
while self.running:
self.dt = self.clock.tick(FPS) # Get time passed since last update / Set FPS
self.events()
self.render()
self.update()
def events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.running = False
# Check for quit event and close window
def render(self):
self.screen.fill(WHITE)
self.all_sprites.draw(self.screen) # Draws all sprites to screen
pg.display.flip() # updates screen
def update(self):
self.all_sprites.update() # calls the update function of all of the sprites
if __name__ == '__main__':
g = Game()
while g.running:
g.run()
pg.quit()
sys.exit()
.... Player and platform sprite code:
import pygame as pg
from constants import *
vec = pg.math.Vector2
class Player(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites
self.game = game
super().__init__(self.groups)
self.image = pg.Surface((40, 70))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
pg.draw.rect(self.image, BLACK, (0, 0, self.rect.width, self.rect.height), 3)
self.pos = vec(x, y)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
def move(self):
self.acc = vec(0, 0)
keypress = pg.key.get_pressed()
if keypress[pg.K_a]:
self.acc.x += -PLAYER_MOVE_SPEED
if keypress[pg.K_d]:
self.acc.x += PLAYER_MOVE_SPEED
self.acc += self.vel * PLAYER_FRICTION
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
def collisions(self, axis):
if axis == 'y':
for plat in self.game.platform_sprites:
if self.acc.y > 0:
if pg.Rect.colliderect(self.rect, plat.rect):
self.acc.y = 0
self.pos.y = plat.rect.top
def update(self):
self.move()
self.collisions('y')
class Platform(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites, game.platform_sprites
self.game = game
super().__init__(self.groups)
self.image = pg.Surface((TILESIZE, TILESIZE))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.x = x * TILESIZE
self.y = y * TILESIZE
def update(self):
self.rect.x = self.x
self.rect.y = self.y
The "Constants" file only has stuff like the screen size, colors, and player speed defined. IF any of that's needed I'll add it.
In the Player.collisions:
if self.acc.y > 0:
if pg.Rect.colliderect(self.rect, plat.rect):
self.acc.y = 0
self.pos.y = plat.rect.top
The main cause of your issue is the player's velocity:
You're resetting the vertical acceleration (acc.y) and position (pos.y), but it seems like you're forgetting about the vertical speed (vel.y). The player's sinking because it still has downward velocity, which you can remedy with self.vel.y = 0.
.
Another more minor issue might be, as #LarryMcMuffin correctly guessed, that there is an ordering issue between your moving/drawing:
You may be updating Player.pos.y correctly so the player stands perfectly on top of the platform, but you also need to update self.rect.midbottom if you want your character to be drawn at its new position -- which you do, but in Player.move, which is called before. You could reorder the move/collision functions, but my suggestion is to simply move self.rect.midbottom = self.pos from Player.move to the very end of Player.update.
End result:
def collisions(self, axis):
if axis == 'y':
for plat in self.game.platform_sprites:
if self.acc.y > 0:
if pg.Rect.colliderect(self.rect, plat.rect):
self.acc.y = 0
self.vel.y = 0
self.pos.y = plat.rect.top
def update(self):
self.move()
self.collisions('y')
self.rect.midbottom = self.pos
Another thing I'd like to point out, even though it's probably not related to the issue you described: Use the delta-time.
See, you have two values that influence your character's position:
velocity (vel): expresses a change of position over time
acceleration (acc): expresses a change of velocity over time
The over time is key here -- since the time elapsed between two calls of move is variable, this matters: as more/less time passed, the speed should act more/less upon the position. Fortunately, you're saving the time elapsed between two frames/updates in Game.dt. Pass it to your player and use it more or less like so:
self.acc += self.vel * PLAYER_FRICTION
self.vel += self.acc * dt
self.pos += (self.vel + 0.5 * self.acc) * dt
It won't fix it completely so that two calls with dt = .5 produce the same effect as one call with dt = 1, but it will "fill the gap". I don't think you can achieve it without either updating the physics on a fixed-interval basis, or resorting to expensive and complex calculations (using the power function).
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!
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)