Reputation: 53
I'm trying to make projectiles for my ship in my version of space invader. I think the only thing that doesen't work right now is displaying all the projectile because I don't know how, and that's why i tried using blit which didn't work. Also I want to delay the timings between each projectile but i'm not sure how to do that. So far this is the most advanced screen-related program i've done.
import pygame
pygame.init()
class Player():
def __init__(self):
self.originalImage = pygame.image.load("ship.png")
self.image = self.originalImage
self.xposition = 480
self.yposition = 450
self.speed = 1
class Projectile():
def __init__(self):
self.originalImage = pygame.image.load("shot.png")
self.image = self.originalImage
self.xposition = player.xposition
self.yposition = player.yposition
self.speed = 1
size = width, height = 960, 540
screen = pygame.display.set_mode(size)
background = 0, 0, 0
player = Player()
projectile = Projectile()
projectiles = []
keys = pygame.key.get_pressed()
while True:
screen.fill(background)
for projectile in projectiles:
if projectile.yposition < height:
projectile.yposition -= projectile.speed
else:
projectiles.pop(projectiles.index(projectile))
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) == True:
player.xposition += player.speed
if (keys[pygame.K_LEFT] or keys[pygame.K_a]) == True:
player.xposition -= player.speed
if (keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]) == True:
projectiles.append(projectile)
for projectile in projectiles:
screen.blit(projectile.image, (projectile.xposition, projectile.yposition))
screen.blit(player.image, ((player.xposition, player.yposition)))
pygame.display.flip()
I edited the code after the comment but still nothing moves...
Upvotes: 1
Views: 273
Reputation: 211116
I recommend to use pygame.sprite.Sprite
. Derive Projectile
from Sprite
. A Sprite
should have the attributes .image
and .rect
. Add a method which moves the Projectile
and kill()
s it when it goes out of bounds. e.g:
class Projectile(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.originalImage = pygame.image.load("shot.png")
self.image = self.originalImage
self.rect = self.image.get_rect(center = (player.xposition, player.yposition))
self.speed = 1
def move(self):
self.rect.y -= self.speed
if self.rect.y < 0:
self.kill()
Create a pygame.sprite.Group
to contain the projectiles:
projectiles = pygame.sprite.Group()
The Sprites in a Group
can be drawn by .draw()
.
projectiles.draw(screen)
Every time when the player fires a bullet, then a new instance of bullet has to be created and be add to the Group of bullets. I recommend to us the keyboard events for that:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE or event.key == pygame.K_w or event.key == pygame.K_UP:
projectiles.add(Projectile())
The states of the keys (keys = pygame.key.get_pressed()
) have to be evaluated in the main application loop. The speed of the application can be controlled by the pygame.time.Clock()
:
import pygame
pygame.init()
class Player(pygame.sprite.Sprite):
def __init__(self):
self.originalImage = pygame.image.load("ship.png")
self.image = self.originalImage
self.rect = self.image.get_rect(center = (480, 450))
self.speed = 1
class Projectile(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.originalImage = pygame.image.load("shot.png")
elf.image = self.originalImage
self.rect = self.image.get_rect(center = player.rect.center)
self.speed = 1
def move(self):
self.rect.y -= self.speed
if self.rect.y < 0:
self.kill()
size = width, height = 960, 540
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
background = 0, 0, 0
player = Player()
projectiles = pygame.sprite.Group()
run = True
while run:
clock.tick(120)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE or event.key == pygame.K_w or event.key == pygame.K_UP:
projectiles.add(Projectile())
keys = pygame.key.get_pressed()
if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) == True:
player.rect.x += player.speed
if (keys[pygame.K_LEFT] or keys[pygame.K_a]) == True:
player.rect.x -= player.speed
for projectile in projectiles:
projectile.move()
screen.fill(background)
projectiles.draw(screen)
screen.blit(player.image, player.rect.topleft)
pygame.display.flip()
Upvotes: 1
Reputation: 262
Where is the problem:
Nothing is moving because pygame.key.get_pressed()
is outside the game loop, ie you only check the state of keys at startup.
You must create a new Projectile
instance each time you shoot, otherwise you'd be able to shoot only once.
To make the projectiles appear properly, you must blit the player sprite first, and then blit the projectiles, otherwise the player sprite will cover them.
To code in a delay between shots, store the shooting cooldown in a variable (preferably a player
attribute), which you set to some positive value each time you shoot and decrement each turn.
You use sys.exit()
but you haven't imported sys
, which makes your game crash during exiting. Alternatively you can raise SystemExit
instead, which does exactly the same thing as sys.exit() and doesn't need any imports.
Here is the corrected code:
import pygame
pygame.init()
class Player():
def __init__(self):
self.originalImage = pygame.image.load("ship.png")
self.image = self.originalImage
self.xposition = 480
self.yposition = 450
self.speed = 1
self.attack_cooldown = 0 # variable to store shooting cooldown
class Projectile():
def __init__(self):
self.originalImage = pygame.image.load("shot.png")
self.image = self.originalImage
self.xposition = player.xposition
self.yposition = player.yposition
self.speed = 1
size = width, height = 960, 540
screen = pygame.display.set_mode(size)
background = 0, 0, 0
player = Player()
projectiles = []
while True:
screen.fill(background)
# decrement the current shooting cooldown
if player.attack_cooldown > 0:
player.attack_cooldown -= 1
for projectile in projectiles:
if projectile.yposition < height:
projectile.yposition -= projectile.speed
else:
projectiles.pop(projectiles.index(projectile))
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit # does the same thing as sys.exit() but you don't need to import sys
keys = pygame.key.get_pressed() # put this in the game loop
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
player.xposition += player.speed
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
player.xposition -= player.speed
if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
if player.attack_cooldown == 0: # shoot only when not on cooldown
projectiles.append(Projectile()) # shoot a new projectile instead of the one you already shot
player.attack_cooldown = 100 # add shooting cooldown, the number can be any positive integer you want
# blit the player first
screen.blit(player.image, ((player.xposition, player.yposition)))
for projectile in projectiles:
screen.blit(projectile.image, (projectile.xposition, projectile.yposition))
pygame.display.flip()
Upvotes: 1