This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
How to draw a moving circle in Pygame with a small angle at a low speed and blinking?
(1 answer)
Closed 2 years ago.
I am making an Astroid clone where you only move with the keyboard. The player is able to rotate and needs to move forward to were it is facing.
For example:
When I rotate the player by 30 degrees and push W to go forward I want the player to go forward in the direction the player is facing. I can't just use self.rect.x += self.vel
I also tried to use this function:
def calculate_new_xy(speed, degrees):
add_x = (speed*(math.sin((degrees)*math.pi/180)))
add_y = (speed*(math.cos((degrees)*math.pi/180)))
return add_x, add_y
In theory, it should work because I am taking the sin and cos of this
Triangle.
But when I added that to my code the movement of the player wasn't as smooth as I hoped it to be. And sometimes the player moved indirectly forward.
I also noticed that the player moves faster when facing to the top left than to the bottom right.
Due to that the player doesn't move in a circle when turning always right. Instead the player moves elliptical to the top left.
Here is the code:
import pygame
import time
import random
import math
from os import path
#Colors:
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
#Settings:
pygame.init()
pygame.mixer.init() #for sound
WIDTH = 700
HEIGHT = 500
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Astroids")
clock = pygame.time.Clock()
FPS = 60
#Images/Sounds:
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, "img")
player = pygame.image.load(path.join(img_folder, 'Player.png'))
stone = pygame.image.load(path.join(img_folder, 'Stein.png'))
#Game Classes
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.original_image = player
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed_x = 0
self.speed_y = 0
self.vel = 3
self.degrees = 0
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
def movement(self):
'''Player Movement'''
keystate = pygame.key.get_pressed()
if keystate[pygame.K_q]:
self.degrees += 3
if keystate[pygame.K_e]:
self.degrees -= 3
if keystate[pygame.K_w]:
self.rect.centerx += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.rect.centery += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.rect.centery += -self.vel * math.sin(math.radians(self.degrees - 90))
self.rect.centerx += self.vel * math.cos(math.radians(self.degrees - 90))
def rotate(self):
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.degrees)
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#self.rect.y += self.speed_y
class Astroid(pygame.sprite.Sprite):
def __init__(self, life):
pygame.sprite.Sprite.__init__(self)
self.original_image = stone
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed = 2
self.life = life
self.speed_x = random.randrange(-3, 3)
self.speed_y = random.randrange(-3, 3)
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-3, 3)
self.last_rotation = pygame.time.get_ticks() # keeps track of time in milliseconds
self.rotation_deegre = random.randint(0, 360)
self.rotation_speed = 5
def rotate(self):
current_time = pygame.time.get_ticks()
if current_time - self.last_rotation > 50:
self.last_rotation = current_time
self.rotation_deegre += self.rotation_speed
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.rotation_deegre)
self.rect = self.image.get_rect()
self.rect.center = old_center
'''def new_astroids(self):
for i in range(2):
m = Astroid()
all_sprites.add(m)
all_astroids.add(m)''' #to do
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
def movement(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#Game Funktions
def calculate_new_xy(speed, degrees):
add_x = (speed*(math.sin((degrees)*math.pi/180)))
add_y = (speed*(math.cos((degrees)*math.pi/180)))
return add_x, add_y
#Game Sprites
all_astroids = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range (1):
m = Astroid(3)
all_astroids.add(m)
all_sprites.add(m)
#Main Game Loop
running = True
while running:
#Keep the game runnung at 60 FPS
clock.tick(FPS)
#Check for events:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#Update for Sprites
all_sprites.update()
#Check to see if any Astroids hit the player
astroid_colision = pygame.sprite.spritecollide(player, all_astroids, False)
if astroid_colision:
running = False
#Draw/Render
screen.fill(BLACK)
all_sprites.draw(screen)
#Update the display
pygame.display.update()
pygame.quit()
Any help is welcomed.
But when I added that to my code the movement of the player wasn't as smove as i hoped
The issue is caused by the fact, that the attributes of a pygame.Rect are integral values. Every time the position of the rectangle is changed, the fraction of the floating point motion vector is lost.
You have to compute the position with floating point accuracy and to synchronize the integral rectangle location by rounding the floating point position (round).
Add the attributes self.x and self.y:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.x, self.y = self.rect.center
Update self.x and self.y when the rectangle goes out of bounds:
class Player(pygame.sprite.Sprite):
# [...]
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
self.x = self.rect.centerx
if self.rect.bottom < 0:
self.rect.top = HEIGHT
self.y = self.rect.centery
if self.rect.right < 0:
self.rect.left = WIDTH
self.x = self.rect.centerx
if self.rect.top > HEIGHT:
self.rect.bottom = 0
self.y = self.rect.centery
Change the the attributes self.x and self.y when the player moves and update self.rect.center by self.x and self.y:
class Player(pygame.sprite.Sprite):
# [...]
def movement(self):
# [...]
if keystate[pygame.K_w]:
self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
self.x += self.vel * math.cos(math.radians(self.degrees - 90))
self.rect.center = round(self.x), round(self.y)
Class Player:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.original_image = player
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed_x = 0
self.speed_y = 0
self.vel = 3
self.degrees = 0
self.x, self.y = self.rect.center
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
self.x = self.rect.centerx
if self.rect.bottom < 0:
self.rect.top = HEIGHT
self.y = self.rect.centery
if self.rect.right < 0:
self.rect.left = WIDTH
self.x = self.rect.centerx
if self.rect.top > HEIGHT:
self.rect.bottom = 0
self.y = self.rect.centery
def movement(self):
'''Player Movement'''
keystate = pygame.key.get_pressed()
if keystate[pygame.K_q]:
self.degrees += 3
if keystate[pygame.K_e]:
self.degrees -= 3
if keystate[pygame.K_w]:
self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
self.x += self.vel * math.cos(math.radians(self.degrees - 90))
self.rect.center = round(self.x), round(self.y)
def rotate(self):
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.degrees)
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#self.rect.y += self.speed_y
Related
i'm learning python by myself, and started with a simple game with pygame.
The game consists, so far, in a ball that's been chased by other balls, i have created a loop that avoid the chasing balls to overlap.
The way the loop works its by a nested loop that moves thru a list that includes all the chasing balls, then measures the distance between them, if the distance is less than the ball radius, it is moved away.
It seems to work most of the time, but sometimes a ball overlaps. I dont know why, if anyone can take a look at my code and give me a hint i would apreciate it, i think the error happens when there are more than 3 balls and at the moment when the player's ball (pelota) collides.
import pygame, random, math
pygame.init()
ancho , alto = 800 , 600
negro = (0,0,0)
blanco=(255,255,255)
FPS = 60
velocidad = 7
velocidadnalguis = velocidad - 2
contadordecolisiones = 1
perseguidores = []
pantalla=pygame.display.set_mode((ancho,alto))
pygame.display.set_caption("un jueguito")
reloj=pygame.time.Clock()
class Pelota(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("pelota.png").convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.rect.center = (ancho/2,alto/2)
self.speed = 0
def update(self):
self.speedx = 0
self.speedy = 0
tecla = pygame.key.get_pressed()
if tecla[pygame.K_LEFT]:
self.speedx = -velocidad
if tecla[pygame.K_RIGHT]:
self.speedx = velocidad
if tecla[pygame.K_UP]:
self.speedy = -velocidad
if tecla[pygame.K_DOWN]:
self.speedy = velocidad
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > ancho:
self.rect.right = ancho
if self.rect.bottom > alto:
self.rect.bottom = alto
if self.rect.left < 0:
self.rect.left = 0
if self.rect.top < 0:
self.rect.top = 0
pelota = Pelota()
class Perseguidor(pygame.sprite.Sprite):
def __init__(self,):
super().__init__()
self.image = pygame.image.load('nalguis.png').convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.velocidad = velocidadnalguis
self.radius = self.rect.width/2
self.center = self.rect.center
self.contadordecolisiones = 1
self.enerandom = random.randint(0,4)
if self.enerandom == 0:
self.rect.center = (random.randrange(-130,-30),random.randrange(-130,alto+130))
if self.enerandom == 1:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(-130,-30))
if self.enerandom == 2:
self.rect.center = (random.randrange(ancho+30,ancho+130),random.randrange(-130,alto+130))
if self.enerandom == 3:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(alto+30,alto+130))
def update(self):
self.pely = pelota.rect.y
self.pelx = pelota.rect.x
self.perx = self.rect.x
self.pery = self.rect.y
self.dist = math.sqrt(((self.pely-self.pery)**2) + ((self.pelx-self.perx)**2))/velocidadnalguis
self.angulo = math.atan2(self.pely-self.pery,self.pelx-self.perx)
self.speedx = 0
self.speedy = 0
self.speedx = math.cos(self.angulo)
self.speedy = math.sin(self.angulo)
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
all_sprites = pygame.sprite.Group()
perseguidor_grupo = pygame.sprite.Group()
all_sprites.add(pelota)
terminar=False
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if len(perseguidor_grupo)<1:
perseguidor=Perseguidor()
perseguidor_grupo.add(perseguidor)
all_sprites.add(perseguidor)
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
if len(perseguidor_grupo)<25:
perseguidor = Perseguidor()
perseguidor1 = Perseguidor()
all_sprites.add(perseguidor, perseguidor1)
perseguidor_grupo.add(perseguidor, perseguidor1)
perseguidores.append(perseguidor1)
perseguidores.append(perseguidor)
else :
perseguidor = Perseguidor()
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
dist=1
for i in range (len(perseguidores)):
for j in range(i+1,len(perseguidores)):
perseguidoresxy = [perseguidores[i].rect.centerx,perseguidores[i].rect.centery]
dist=math.hypot(perseguidores[i].rect.centerx - perseguidores[j].rect.centerx , perseguidores[i].rect.centery - perseguidores[j].rect.centery)
if dist <= perseguidor.radius*2:
if perseguidores[i].rect.centerx < perseguidores[j].rect.centerx:
perseguidores[i].rect.centerx -= 3
if perseguidores[i].rect.centery < perseguidores[j].rect.centery:
perseguidores[i].rect.centery -= 3
if perseguidores[i].rect.centerx > perseguidores[j].rect.centerx:
perseguidores[i].rect.centerx += 3
if perseguidores[i].rect.centery > perseguidores[j].rect.centery:
perseguidores[i].rect.centery -= 3
if len(perseguidores)> len(perseguidor_grupo):
del perseguidores [0]
all_sprites.update()
pantalla .fill(negro)
all_sprites.draw(pantalla)
pygame.display.flip()
pygame.quit()
quit()
[...] then measures the distance between them, if the distance is less than the ball radius, it is moved away. It seems to work most of the time, but sometimes a ball overlaps. [...]
Of course.You only consider 2 balls when moving a ball away.
This means that if you move a ball away from one ball, it can happen that you move it straight onto another ball.
You need to check that there is no other ball in the position where you are moving the ball.
Make sure the balls don't overlap as they spawn:
requested_balls = 1
while not terminar:
# [...]
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
requested_balls = min(25, requested_balls+1)
if len(perseguidor_grupo) < requested_balls:
perseguidor = Perseguidor()
if not pygame.sprite.spritecollide(perseguidor, perseguidor_grupo, True, pygame.sprite.collide_circle):
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
Only move a ball if the new position of the ball is not yet occupied by a ball:
class Perseguidor(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
old_rect = self.rect.copy()
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1: # at last 1, because the ball hits itself
if random.randrange(2) == 0:
self.rect.x = old_rect.x
else:
self.rect.y = old_rect.y
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1:
self.rect = old_rect
Complete example:
import pygame, random, math
pygame.init()
ancho , alto = 800 , 600
negro = (0,0,0)
blanco=(255,255,255)
FPS = 60
velocidad = 7
velocidadnalguis = velocidad - 2
contadordecolisiones = 1
perseguidores = []
pantalla=pygame.display.set_mode((ancho,alto))
pygame.display.set_caption("un jueguito")
reloj=pygame.time.Clock()
class Pelota(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("pelota.png").convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.rect.center = (ancho/2,alto/2)
self.speed = 0
def update(self):
self.speedx = 0
self.speedy = 0
tecla = pygame.key.get_pressed()
if tecla[pygame.K_LEFT]:
self.speedx = -velocidad
if tecla[pygame.K_RIGHT]:
self.speedx = velocidad
if tecla[pygame.K_UP]:
self.speedy = -velocidad
if tecla[pygame.K_DOWN]:
self.speedy = velocidad
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > ancho:
self.rect.right = ancho
if self.rect.bottom > alto:
self.rect.bottom = alto
if self.rect.left < 0:
self.rect.left = 0
if self.rect.top < 0:
self.rect.top = 0
pelota = Pelota()
class Perseguidor(pygame.sprite.Sprite):
def __init__(self,):
super().__init__()
self.image = pygame.image.load('nalguis.png').convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.velocidad = velocidadnalguis
self.radius = self.rect.width/2
self.center = self.rect.center
self.contadordecolisiones = 1
self.enerandom = random.randint(0,4)
if self.enerandom == 0:
self.rect.center = (random.randrange(-130,-30),random.randrange(-130,alto+130))
if self.enerandom == 1:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(-130,-30))
if self.enerandom == 2:
self.rect.center = (random.randrange(ancho+30,ancho+130),random.randrange(-130,alto+130))
if self.enerandom == 3:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(alto+30,alto+130))
def update(self):
self.pely = pelota.rect.y
self.pelx = pelota.rect.x
self.perx = self.rect.x
self.pery = self.rect.y
self.dist = math.sqrt(((self.pely-self.pery)**2) + ((self.pelx-self.perx)**2))/velocidadnalguis
self.angulo = math.atan2(self.pely-self.pery,self.pelx-self.perx)
self.speedx = 0
self.speedy = 0
self.speedx = math.cos(self.angulo)
self.speedy = math.sin(self.angulo)
old_rect = self.rect.copy()
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1: # at last 1, because the ball hits itself
if random.randrange(2) == 0:
self.rect.x = old_rect.x
else:
self.rect.y = old_rect.y
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1:
self.rect = old_rect
all_sprites = pygame.sprite.Group()
perseguidor_grupo = pygame.sprite.Group()
all_sprites.add(pelota)
terminar=False
requested_balls = 1
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if len(perseguidor_grupo)<1:
perseguidor=Perseguidor()
perseguidor_grupo.add(perseguidor)
all_sprites.add(perseguidor)
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
requested_balls = min(25, requested_balls+1)
if len(perseguidor_grupo) < requested_balls:
perseguidor = Perseguidor()
if not pygame.sprite.spritecollide(perseguidor, perseguidor_grupo, True, pygame.sprite.collide_circle):
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
dist=1
if len(perseguidores)> len(perseguidor_grupo):
del perseguidores [0]
all_sprites.update()
pantalla .fill(negro)
all_sprites.draw(pantalla)
pygame.display.flip()
pygame.quit()
quit()
This question already has an answer here:
How do I make my player rotate towards mouse position?
(1 answer)
Closed 2 years ago.
I've been working on some game code for school and was working through some tutorials and figured out how to make the player sprite tracks the mouse but now I'm having trouble figuring out how to spawn enemies from all sides of the screen instead of just the top. (To clarify code is in two parts/files, also completely unrelated). I was also wondering, how I could maybe have the mobs/NPCs chase and rotate towards the player?
import random
import math
import pygame as py
from PlayerSprite import *
WIDTH = 800
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# initialize pygame and create window
py.init()
py.mixer.init()
screen = py.display.set_mode((WIDTH, HEIGHT))
py.display.set_caption("Dimensional Drifter")
clock = py.time.Clock()
all_sprites = py.sprite.Group()
NPCs = py.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(8):
n = NPC()
all_sprites.add(n)
NPC.add(n)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
for event in py.event.get():
# check for closing window
if event.type == py.QUIT:
running = False
# Update
all_sprites.update()
#
mouse_x, mouse_y = py.mouse.get_pos()
player.rotate(mouse_x, mouse_y)
# render
screen.fill(BLACK)
all_sprites.draw(screen)
# flip the display
py.display.flip()
py.quit()
import pygame as py
import math
import random
WIDTH = 800
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
class Player(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((50, 50), py.SRCALPHA)
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT / 2
self.Yspeed = 0
self.rotatableimage = self.image
def update(self):
self.Xspeed = 0
self.Yspeed = 0
# line below allow for key press to equate to move of sprite
keypreesed = py.key.get_pressed()
if keypreesed[py.K_a]:
self.Xspeed = - 11
if keypreesed[py.K_d]:
self.Xspeed = 11
if keypreesed[py.K_w]:
self.Yspeed = - 11
if keypreesed[py.K_s]:
self.Yspeed = 11
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# line below allow the sprite to wrap around the screen
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.top = 0
if self.rect.bottom < 0:
self.rect.bottom = HEIGHT
def rotate(self, mouse_x, mouse_y):
rel_x = mouse_x - self.rect.x
rel_y = mouse_y - self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = py.transform.rotate(self.rotatableimage, int(angle))
self.rect = self.image.get_rect(center=(self.rect.centerx, self.rect.centery))
return
class NPC(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((30, 30))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Yspeed = random.randrange(1, 8)
def update(self):
self.rect.y += self.Yspeed
if self.rect.top > HEIGHT + 10:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Yspeed = random.randrange(1, 8)
Thanks in advance.
You have to add a random direction and a separate speed (self.Xspeed, self.Yspeed) for the x and y direction:
self.direction = random.randrange(4)
and to set self.rect.x, self.rect.y, self.Xspeed and self.Yspeed dependent on the random direction:
if self.direction == 0:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Xspeed = 0
self.Yspeed = random.randrange(1, 8)
elif self.direction == 1:
# [...]
elif self.direction == 2:
# [...]
elif self.direction == 3:
# [...]
Create a method spawn, which spawns a enemy with a random direction and a random speed and call it in the constructor of NPC:
class NPC(py.sprite.Sprite):
def __init__(self):
# [...]
self.spawn()
def spawn(self):
self.direction = random.randrange(4)
if self.direction == 0:
# [...]
In update the speed has to be and to the x and y position separately:
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
Furthermore, if the object is out of the window has to be evaluated dependent on the direction. If the object is out of the window, the call spawn() again, thus the object gets a new random direction, position and speed:
if self.direction == 0:
if self.rect.top > HEIGHT + 10:
self.spawn()
elif self.direction == 1:
if self.rect.bottom < -10:
self.spawn()
elif self.direction == 2:
if self.rect.left > WIDTH + 10:
self.spawn()
elif self.direction == 3:
if self.rect.right < -10:
self.spawn()
To rotate the image you have to create surface with per pixel alpha (convert_alpha()) and to keep the original surface:
self.image = py.Surface((30, 30)).convert_alpha()
self.image.fill(RED)
self.originalimage = self.image
Compute the angle from the enemy to the player and rotate the image by pygame.transform.rotate():
(See also How to rotate a triangle to a certain angle in pygame?)
dir_x, dir_y = player.rect.x - self.rect.x, player.rect.y - self.rect.y
self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y)
self.image = py.transform.rotate(self.originalimage, self.rot)
Class NPC:
class NPC(py.sprite.Sprite):
def __init__(self, player):
py.sprite.Sprite.__init__(self)
self.player = player
self.image = py.Surface((30, 30)).convert_alpha()
self.image.fill(RED)
self.originalimage = self.image
self.rect = self.image.get_rect()
self.spawn()
def spawn(self):
self.direction = random.randrange(4)
if self.direction == 0:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Xspeed = 0
self.Yspeed = random.randrange(1, 8)
elif self.direction == 1:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(HEIGHT, HEIGHT+60)
self.Xspeed = 0
self.Yspeed = -random.randrange(1, 8)
elif self.direction == 2:
self.rect.x = random.randrange(-100, -40)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = random.randrange(1, 8)
self.Yspeed = 0
elif self.direction == 3:
self.rect.x = random.randrange(WIDTH, WIDTH+60)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = -random.randrange(1, 8)
self.Yspeed = 0
def update(self):
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
dir_x, dir_y = self.player.rect.x - self.rect.x, self.player.rect.y - self.rect.y
self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y)
self.image = py.transform.rotate(self.originalimage, self.rot)
if self.direction == 0:
if self.rect.top > HEIGHT + 10:
self.spawn()
elif self.direction == 1:
if self.rect.bottom < -10:
self.spawn()
elif self.direction == 2:
if self.rect.left > WIDTH + 10:
self.spawn()
elif self.direction == 3:
if self.rect.right < -10:
self.spawn()
player = Player()
all_sprites.add(player)
for i in range(8):
n = NPC(player)
all_sprites.add(n)
NPC.add(n)
I was just getting some help to figure out how to get my player fire bullets when I realized that they only go (kinda expected this but however as only had y value for movement). I don't know how I'll make the bullets fire off in the direction the player is facing.
I have some idea of what to but I just don't know how to do it... I thought I could somehow use the cursor and player tracking that's in this game for the visuals but I don't know how to make that a one-time thing instead of a constant. For diagonal movement of the bullet, I have no clue.
Code below (split into two parts/file Main.py and PlayerSprite.py):
Main:
py.init()
py.mixer.init()
screen = py.display.set_mode((WIDTH, HEIGHT))
py.display.set_caption("Dimensional Drifter")
clock = py.time.Clock()
all_sprites = py.sprite.Group()
NPCs = py.sprite.Group()
bullets = py.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(14):
n = NPC(player)
all_sprites.add(n)
NPCs.add(n)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
for event in py.event.get():
# check for closing window
if event.type == py.QUIT:
running = False
elif event.type == py.KEYDOWN:
if event.key == py.K_SPACE:
New_bullet = player.Shoot()
all_sprites.add(New_bullet)
bullets.add(New_bullet)
# Update
all_sprites.update()
# # check if there a collision between the bullet and NPC
hits = py.sprite.groupcollide(NPCs, bullets, True, True)
# check if there a collision between the player and NPC
hits = py.sprite.spritecollide(player, NPCs, True)
if hits:
running = False
# updates the position of of mouse and rotates it towards the mouse position
mouse_x, mouse_y = py.mouse.get_pos()
player.rotate(mouse_x, mouse_y)
# render
screen.fill(BLACK)
all_sprites.draw(screen)
# flip the display
py.display.flip()
py.quit()
PlayerSprite
import pygame as py
import math
import random
WIDTH = 800
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
class Player(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((40, 40), py.SRCALPHA)
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT / 2
self.Yspeed = 0
self.rotatableimage = self.image
def update(self):
self.Xspeed = 0
self.Yspeed = 0
# line below allow for key press to equate to move of sprite
keypreesed = py.key.get_pressed()
if keypreesed[py.K_a]:
self.Xspeed = - 11
if keypreesed[py.K_d]:
self.Xspeed = 11
if keypreesed[py.K_w]:
self.Yspeed = - 11
if keypreesed[py.K_s]:
self.Yspeed = 11
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# line below allow the sprite to wrap around the screen
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.top = 0
if self.rect.bottom < 0:
self.rect.bottom = HEIGHT
def rotate(self, mouse_x, mouse_y):
rel_x = mouse_x - self.rect.x
rel_y = mouse_y - self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = py.transform.rotate(self.rotatableimage, int(angle))
self.rect = self.image.get_rect(center=(self.rect.centerx, self.rect.centery))
return
def Shoot(self):
return Bullet(self.rect.centerx, self.rect.top)
class NPC(py.sprite.Sprite):
def __init__(self, player):
py.sprite.Sprite.__init__(self)
self.player = player
self.image = py.Surface((30, 30)).convert_alpha()
self.image.fill(RED)
self.originalimage = self.image
self.rect = self.image.get_rect()
self.spawn()
# allows of spawning from all four side of the screen and set the x, y speed and spawn position
def spawn(self):
self.direction = random.randrange(4)
if self.direction == 0:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Xspeed = random.randrange(-2, 2)
self.Yspeed = random.randrange(4, 8)
elif self.direction == 1:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(HEIGHT, HEIGHT + 60)
self.Xspeed = random.randrange(-2, 2)
self.Yspeed = -random.randrange(4, 8)
elif self.direction == 2:
self.rect.x = random.randrange(-100, -40)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = random.randrange(4, 8)
self.Yspeed = random.randrange(-2, 2)
elif self.direction == 3:
self.rect.x = random.randrange(WIDTH, WIDTH + 60)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = -random.randrange(4, 8)
self.Yspeed = random.randrange(-2, 2)
def update(self):
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# makes it so that NPC point to wards the player as it passes from side to side
dir_x, dir_y = self.player.rect.x - self.rect.x, self.player.rect.y - self.rect.y
self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y)
self.image = py.transform.rotate(self.originalimage, self.rot)
# Respawns the NPC when they hit an side
if self.direction == 0:
if self.rect.top > HEIGHT + 10:
self.spawn()
elif self.direction == 1:
if self.rect.bottom < -10:
self.spawn()
elif self.direction == 2:
if self.rect.left > WIDTH + 10:
self.spawn()
elif self.direction == 3:
if self.rect.right < -10:
self.spawn()
class Bullet(py.sprite.Sprite):
def __init__(self, x, y):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((5, 5))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.Yspeed = -10
def update(self):
self.rect.y += self.Yspeed
# kill if moved of screen
if self.rect.bottom > HEIGHT or self.rect.top < 0:
self.kill()
if self.rect.right > WIDTH or self.rect.left < 0:
self.kill()
Add 2 attributes self.lastX and self.lastY to the class Player and change the attributes when the player changes the direction:
class Player(py.sprite.Sprite):
def __init__(self):
# [...]
self.lastX = 0
self.lastY = -10
def update(self):
# [...]
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
if self.Xspeed != 0 or self.Yspeed != 0:
self.lastX = self.Xspeed
self.lastY = self.Yspeed
Add an argument Xspeed ans Yspeed to the class Bullet
class Bullet(py.sprite.Sprite):
def __init__(self, x, y, Xspeed, Yspeed):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((5, 5))
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.Xspeed = Xspeed
self.Yspeed = Yspeed
def update(self):
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# [...]
Set the attributes when the bullet spawns
class Player(py.sprite.Sprite):
# [...]
def Shoot(self):
return Bullet(self.rect.centerx, self.rect.centery, self.lastX, self.lastY)
Alternatively it is also possible to set the speed dependent on the direction to the mouse cursor.
Get the position of the player and the mouse cursor and compute the x and y distance (Vector ):
pos = self.rect.center
mpos = py.mouse.get_pos()
vx = mpos[0] - pos[0]
vy = mpos[1] - pos[1]
If the mouse position and the bullet position are equal, that does not make any sense, thus the bullet is skipped
if vx == 0 and vy == 0:
return None
Of course this vector is far to long, if you would use it for the direction (Xspeed, Yspeed) directly, then the bullet would step to the mouse cursor in one turn.
In the following I use pygame.math.Vector2, because it provides the handy method scale_to_length, that scales a vector to a specified Euclidean length:
direction = py.math.Vector2(vx, vy)
direction.scale_to_length(10)
Now the x and y component of the vector contain the x and y component of the speed. Since the components are floating point values, they are round to integral values:
return Bullet(pos[0], pos[1], round(direction.x), round(direction.y))
Method Shoot:
class Player(py.sprite.Sprite):
# [...]
def Shoot(self):
pos = self.rect.center
mpos = py.mouse.get_pos()
vx, vy = mpos[0] - pos[0], mpos[1] - pos[1]
if vx == 0 and vy == 0:
return None
direction = py.math.Vector2(vx, vy)
direction.scale_to_length(10)
return Bullet(pos[0], pos[1], round(direction.x), round(direction.y))
Note, if you set the bullet dependent on the direction to the mouse cursor, then it may be useful to spawn the bullet by a mouse click:
while running:
# [...]
for event in py.event.get():
if event.type == py.QUIT:
# [...]
elif event.type == py.MOUSEBUTTONDOWN:
if event.button == 1:
New_bullet = player.Shoot()
if New_bullet:
all_sprites.add(New_bullet)
bullets.add(New_bullet)
You can use pygames Vector2 to move in any direction. You calculate the angle of the player you can use that.
class Player(py.sprite.Sprite):
def __init__(self):
...
self.angle = 0
def rotate(self, mouse_x, mouse_y):
...
self.angle = -angle #make negative otherwise it will be going away from mouse
def Shoot(self):
return Bullet(self.rect.centerx, self.rect.top, py.math.Vector2(1,0).rotate(self.angle))
then in your bullet class, get the direction and add to its position
class Bullet(py.sprite.Sprite):
def __init__(self, x, y, Dir):
...
self.Dir = Dir
def update(self):
self.rect.y += self.Dir[1] * self.speed
self.rect.x += self.Dir[0] * self.speed
...
I am trying to make an asteroid game and was wondering how to rotate the player clock wise or counter clock wise when the right or left keys have been pressed, and then when the up key is pressed the player should move forward.
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_img, (50, 38))
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = 20
# pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
self.speedy = 0
self.shield = 100
self.shoot_delay = 250
self.last_shot = pygame.time.get_ticks()
self.lives = 3
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -8
if keystate[pygame.K_RIGHT]:
self.speedx = 8
if keystate[pygame.K_DOWN]:
self.speedy = 8
if keystate[pygame.K_UP]:
self.speedy = -8
if keystate[pygame.K_SPACE]:
self.shoot()
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
self.last_shot = now
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
def hide(self):
# hide player temporarily
self.hidden = True
self.hide_timer = pygame.time.get_ticks()
self.rect.center = (WIDTH / 2, HEIGHT + 200)````
You can find below a working example (just rename the loaded image), I've kept only the essential code for movement and rotation.
import sys
import math
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.player_img = pygame.image.load("yourimage.png").convert()
self.image = self.player_img
self.rect = self.image.get_rect()
self.rect.move_ip(x, y)
self.current_direction = 0 #0 degree == up
self.speed = 10
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
prev_center = self.rect.center
if keystate[pygame.K_LEFT]:
self.current_direction += 10
if keystate[pygame.K_RIGHT]:
self.current_direction -= 10
if keystate[pygame.K_DOWN]:
self.rect.x += self.speed * math.sin(math.radians(self.current_direction))
self.rect.y += self.speed * math.cos(math.radians(self.current_direction))
if keystate[pygame.K_UP]:
self.rect.x -= self.speed * math.sin(math.radians(self.current_direction))
self.rect.y -= self.speed * math.cos(math.radians(self.current_direction))
if keystate[pygame.K_LEFT] or keystate[pygame.K_RIGHT]:
self.image = pygame.transform.rotate(self.player_img, self.current_direction)
self.rect = self.image.get_rect()
self.rect.center = prev_center
pygame.init()
screen = pygame.display.set_mode((500, 500))
player = Player(200, 200)
clock = pygame.time.Clock()
while True:
screen.fill((0, 0, 0), player.rect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
player.update()
screen.blit(player.image, player.rect)
pygame.display.update()
clock.tick(50)
Since you want rotation, you do not need to assing speed directly to x and y but you need to calculate it according to the direction the player is facing.
The basic idea is to use transform.rotate. Keep track of the rotation angle (I called it current_direction), add / subcract a fixed amount (the rotation speed, if you wish) to this angle when LEFT or RIGTH keys are pressed, and then rotate the original image. Since rotation scales the image, I also keep track of the center of the rect, save the new rect from the iamge and and reassing the previous center to the rect.center after rotation, so that the image remains centered on rotation.
When UP or DOWN keys are pressed, you need to decompose the velocity on x and y axes using trigonometry and move the rect attribute coordinates.
I am building a top down shooter in the style of Raiden 2. I need to know how to get access to the enemy object when I detect collision using 'spritecollide'.
The reason I need the enemy object is so that I can lower their energy level for every bullet that hits them.
This is the dictionary that I have to work with:
enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
Here's my Bullet class:
class Bullet(Entity):
def __init__(self, ship, angle):
Entity.__init__(self)
self.speed = 8
self.level = 0
self.image = pygame.Surface((BULLET_DIMENSIONS, BULLET_DIMENSIONS)).convert()
self.rect = self.image.get_rect()
self.rect.x = ship.rect.centerx
self.rect.y = ship.rect.top
self.angle = angle
def update(self, enemyGroup):
if self.rect.bottom < 0:
self.kill()
else:
self.rect.y -= self.speed
Here's my unfinished Enemy class:
class Enemy_1(Entity):
def __init__(self):
Entity.__init__(self)
self.speed = (1, 1)
self.energy = 10
self.begin = False
self.run = False
self.image = pygame.Surface((WIN_W/5, WIN_H/15)).convert()
self.image.fill((70, 70, 70))
self.rect = self.image.get_rect()
self.rect.centerx = WIN_W/1.333
self.rect.y = -WIN_H/15
def shoot(self):
bullet_1 = Bullet(self, 270)
bullet_2 = Bullet(self, 225)
bullet_3 = Bullet(self, 315)
def update(self, enemyGroup, timer):
if timer == 300:
self.begin = True
elif timer == 900:
self.run = True
elif timer > 900 and self.rect.x > WIN_W:
self.remove(enemyGroup)
if self.run:
self.rect.x += self.speed[0]
self.rect.y += self.speed[1]
elif self.begin:
self.rect.y += self.speed[1]
if self.rect.y > WIN_H/4:
self.rect.y = WIN_H/4
if self.energy == 0:
self.kill()
Here's the complete program:
import sys, pygame, os, random, math
from ast import literal_eval
# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'
# Constants
LEFT = 'left'
RIGHT = 'right'
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PILL_WIDTH = 5
PILL_HEIGHT = 20
WIN_W = 500
WIN_H = 800
SHIP_WIDTH = WIN_W/15
SHIP_HEIGHT = WIN_H/15
BULLET_DIMENSIONS = 5
TIMER = 0
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Text(Entity):
def __init__(self, text, size, color, position, font=None):
Entity.__init__(self)
self.color = color
self.font = pygame.font.Font(font, size)
self.text = text
self.set(text, position)
def set(self, text, position):
self.image = self.font.render(str(text), 1, self.color)
self.rect = self.image.get_rect()
self.rect.move_ip(position[0]-self.rect.width/2, (position[1]-self.rect.height)/2)
class Ship(Entity):
def __init__(self, container):
Entity.__init__(self)
self.speed = 5
self.score = 0
self.image = pygame.Surface((SHIP_WIDTH, SHIP_HEIGHT)).convert()
self.rect = self.image.get_rect()
self.rect.centerx = container.centerx
self.rect.y = container.centery
def update(self, bulletGroup):
key = pygame.key.get_pressed()
if key[pygame.K_w]:
self.rect.centery -= self.speed
if key[pygame.K_s]:
self.rect.centery += self.speed
if key[pygame.K_d]:
self.rect.centerx += self.speed
if key[pygame.K_a]:
self.rect.centerx -= self.speed
if key[pygame.K_SPACE]:
if TIMER % 7 == 0:
bullet = Bullet(self, 90, 'friend')
bulletGroup.add(bullet)
# Ship Movement Boundaries
if self.rect.y < WIN_H/25:
self.rect.y = WIN_H/25
if self.rect.y > WIN_H - SHIP_HEIGHT:
self.rect.y = WIN_H - SHIP_HEIGHT
if self.rect.x < 0:
self.rect.x = 0
if self.rect.x > WIN_W - SHIP_WIDTH:
self.rect.x = WIN_W - SHIP_WIDTH
class Bullet(Entity):
def __init__(self, ship, angle, type):
Entity.__init__(self)
self.speed = 8
self.level = 0
self.image = pygame.Surface((BULLET_DIMENSIONS, BULLET_DIMENSIONS)).convert()
self.rect = self.image.get_rect()
self.dx = math.cos(math.radians(angle)) * self.speed
self.dy = math.sin(math.radians(angle)) * self.speed
self.setXY(ship, type)
def setXY(self, ship, type):
self.rect.x = ship.rect.centerx
if type == 'friend':
self.rect.y = ship.rect.top
elif type == 'enemy':
self.rect.y = ship.rect.bottom
def update(self):
if self.rect.bottom < 0:
self.kill()
else:
self.rect.x -= self.dx
self.rect.y -= self.dy
if type == 'friend':
self.rect.y = -self.rect.y
self.rect.x = -self.rect.x
class Powerup(Entity):
def __init__(self, xden, pillCount):
Entity.__init__(self)
self.speed = 3
self.density = xden
self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
self.rect = self.image.get_rect()
self.rect = self.rect.move(100,100)
def restart(self):
pass
def update(self):
if self.rect.y > WIN_H:
del self
else:
self.rect = self.rect.move((0, self.speed))
class Enemy(Entity):
def __init__(self):
Entity.__init__(self)
self.begin = False
self.run = False
self.color = (153, 0, 76)
class Planes(Enemy):
def __init__(self, speed, energy, size, location):
Enemy.__init__(self)
self.speed = speed
self.energy = energy
self.image = pygame.Surface(size).convert()
self.image.fill(self.color)
self.rect = self.image.get_rect()
self.rect.centerx = location[0]
self.rect.y = location[1]
def shoot(self, enemyBulletGroup):
bullet_1 = Bullet(self, 240, 'enemy')
bullet_2 = Bullet(self, 270, 'enemy')
bullet_3 = Bullet(self, 300, 'enemy')
enemyBulletGroup.add(bullet_1, bullet_2, bullet_3)
def update(self, enemyGroup, enemyBulletGroup):
if TIMER == 300:
self.begin = True
elif TIMER == 900:
self.run = True
elif self.rect.x > WIN_W or self.energy == 0:
self.kill()
if self.run:
self.rect.x += self.speed[0]
self.rect.y += self.speed[1]
if TIMER % 100 == 0:
self.shoot(enemyBulletGroup)
elif self.begin:
self.rect.y += self.speed[1]
if self.rect.y > WIN_H/4:
self.rect.y = WIN_H/4
if TIMER % 100 == 0:
self.shoot(enemyBulletGroup)
def main():
# Initialize Everything
pygame.init()
global TIMER
fps = 60
lLeft = lRight = lUp = lDown = shoot = False
clock = pygame.time.Clock()
play = True
pygame.display.set_caption('Pong')
screen = pygame.display.set_mode((WIN_W, WIN_H), pygame.SRCALPHA)
# Create Game Objects
ship = Ship(pygame.rect.Rect(0, 0, WIN_W, WIN_H))
score1 = Text("Score: " + str(ship.score), 40, BLACK, (WIN_W/2, (WIN_H/25)))
enemy_1 = Planes((1, 1), 10, (WIN_W/5, WIN_H/15), (WIN_W/1.4, -WIN_H/15))
# Create Groups
powerupGroup = pygame.sprite.Group()
shipGroup = pygame.sprite.Group()
shipGroup.add(ship)
textGroup = pygame.sprite.Group()
textGroup.add(score1)
shipBulletGroup = pygame.sprite.Group()
enemyBulletGroup = pygame.sprite.Group()
enemyGroup = pygame.sprite.Group()
enemyGroup.add(enemy_1)
# Gameplay
while play:
# Checks if window exit button pressed
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
# Update
powerupGroup.update()
ship.update(shipBulletGroup)
shipBulletGroup.update()
textGroup.update()
enemyGroup.update(enemyGroup, enemyBulletGroup)
enemyBulletGroup.update()
enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
for key in enemyCollisions:
print enemyCollisions[key].energy
# Print Background/Sprites
screen.fill(WHITE)
powerupGroup.draw(screen)
shipGroup.draw(screen)
shipBulletGroup.draw(screen)
textGroup.draw(screen)
enemyGroup.draw(screen)
enemyBulletGroup.draw(screen)
# Print Score Bar
hori_partition = pygame.Surface((WIN_W, 1))
screen.blit(hori_partition, (0, WIN_H/25))
TIMER += 1
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
if __name__ == "__main__":
main()
The pygame.sprite.groupcollide() function returns a dictionary which values are list objects:
Every Sprite inside group1 is added to the return dictionary. The value for each item is the list of Sprites in group2 that intersect.
Thats the reason why calling print enemyCollisions[key].energy when iterating over the return dict fails, since a list object -- the value of enemyCollisions[key] -- does not have an energy() method.
To access all values of a dictionary in Python 2.X you could use the .itervalues() method of a dict instance. To get each colliding sprite, iterate again over these values:
enemyCollisions = pygame.sprite.groupcollide(shipBulletGroup, enemyGroup, True, False)
#iterate over all values of the enemyCollisions dict
for enemyCollisions in enemyCollisions.itervalues():
#access all enemies which collided with an shipBullet
for enemy in enemyCollisions:
print enemy.energy
The syntax for accessing the collided sprite's property is as follows:
collisions = pygame.sprite.spritecollide(self, pillGroup, True)
for key in collisions:
self.density += key.density