Pygame Platformer Sprite Collisions only working in one way - python

I am currently working on a simple school project, where I am coding a Platformer. My Problem is, that the collisions between my player and my platform(s) are checked one after another, so if I check the X Collision first, the Collisions on the sides of the Platforms work completly fine, but as soon as I move, my character will be teleported to the side of the platform, because the gravity pulls me in the platform and i am moving left or right, so the program thinks I am hitting a platform from the side and sets the player rect onto the side of the platform. The same thing happens if I check for X Collision first, but with the Collisions on the sides of the platforms not working.
Is there a way of around it, so my collisions are working like in a Mario-like Game? I tried several things and removed the calculation with Friction and Acceleration for smooth movement, but nothing seems to work for me.
Here is my full code:
import random
import os
WIDTH = 640
HEIGHT = 600
FPS = 60
TITLE = "Game"
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder,"textures")
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARKGREY = (40, 40, 40)
LIGHTGREY = (100, 100, 100)
BLUE = (0,0,255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.5
PGROUND = "dirtplatform.png"
PBLOCK = "dirt.png"
PLATFORM_LIST = [(0, HEIGHT - 64, PGROUND),
(WIDTH / 2, HEIGHT / 2, PBLOCK),
(WIDTH / 4, HEIGHT / 4, PBLOCK)]
vec = pygame.math.Vector2
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.image = pygame.image.load(os.path.join(img_folder,"player.png")).convert()
self.image.set_colorkey((130,255,230))
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2,HEIGHT/2)
self.vel = vec(0,0)
def jump(self):
self.rect.y += 1
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 1
if hits:
self.vel.y = -20
def collision(self, direction):
if direction == 'x':
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
if hits:
print("Hit x")
if self.vel.x > 0:
self.rect.right = hits[0].rect.left
if self.vel.x < 0:
self.rect.left = hits[0].rect.right
self.vel.x = 0
if direction == 'y':
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
if hits:
print("Hit y")
if self.vel.y > 0:
self.rect.bottom = hits[0].rect.top
if self.vel.y < 0:
self.rect.top= hits[0].rect.bottom
self.vel.y = 0
def update(self):
self.acc = vec(0,PLAYER_GRAV)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.vel.x = -5
elif keys[pygame.K_RIGHT]:
self.vel.x = 5
self.rect.x = self.rect.x + self.vel.x
self.rect.y = self.rect.y + self.vel.y
self.collision('x')
self.collision('y')
print("vel",self.vel)
print("rectX",self.rect.x)
print("rectY",self.rect.y)
self.vel.y = self.vel.y + PLAYER_GRAV
self.vel.x = 0
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, image):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(os.path.join(img_folder,image)).convert()
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Game:
def __init__(self):
pygame.init()
pygame.mixer.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
self.clock = pygame.time.Clock()
self.running = True
def new(self):
self.all_sprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.run()
def run(self):
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def collision(self):
if self.player.vel.y > 0 and pygame:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
def update(self):
self.all_sprites.update()
keys = pygame.key.get_pressed()
if self.player.rect.right >= WIDTH-100:
self.player.rect.x = WIDTH - 110
for plat in self.platforms:
plat.rect.right -= abs(self.player.vel.x)
if self.player.rect.left <= 100:
self.player.rect.x = 110
for plat in self.platforms:
plat.rect.right += abs(self.player.vel.x)
def events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.player.jump()
def draw(self):
self.screen.fill(BLACK)
self.all_sprites.draw(self.screen)
pygame.display.flip()
def show_start_screen(self):
pass
def show_go_screen(self):
pass
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen
pygame.quit()
Note: dirtplatform.png represents the ground of the game and dirt.png is a block, which the player should be able to jump on

Collisions are hard, there doesn't seem to be any perfect way of doing it, and every time i do it, it changes everytime. Saying that, this seems to work very well with the 2 mins of testing i just did:
def collision(self):
for platform in self.game.platforms: #check every platform
if self.rect.colliderect(platform.rect): #if the two rects collided
if self.rect.right <= platform.rect.left + self.vel.x:
self.rect.right = platform.rect.left
self.vel.x = 0
if self.rect.left >= platform.rect.right + self.vel.x:
self.rect.left = platform.rect.right
self.vel.x = 0
if self.rect.bottom <= platform.rect.top + self.vel.y:
self.rect.bottom = platform.rect.top
self.vel.y = 0
if self.rect.top >= platform.rect.bottom + self.vel.y:
self.rect.top = platform.rect.bottom
self.vel.y = 0

Related

Pygame collision sides trouble [duplicate]

So I was making a pygame platformer and I got stuck on one thing. I coudn't find a way to make the bottom of my platforms solid. The player could land on the top of it but when it tries to go through the bottom it bounces back down. I tried this but it didnt work:
hits = pg.sprite.spritecollide(player, platforms, False)
if hits:
if player.pos.y == hits[0].rect.top:
player.vel.y = 10
else:
player.pos.y = hits[0].rect.top + 1
player.vel.y = 0
Does anyone got a solution for me? Here's the complete program.
Here's a short platformer example. Especially the movement is important. You have to move along the x-axis first, check if the player collides with a wall and move it back if a collision occurred. Afterwards do the same with the y-axis. If you don't split the movement into these two parts, your player will jump to the sides, top or bottom of the wall if you press more than one movement key at the same time.
import pygame as pg
pg.init()
WINDOW_WIDTH, WINDOW_HEIGHT = 800, 600
screen = pg.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
GRAY = pg.Color('gray24')
GRAVITY = 800
class Player(pg.sprite.Sprite):
def __init__(self, pos, blocks):
super().__init__()
self.image = pg.Surface((30, 50))
self.image.fill(pg.Color(0, 110, 170))
self.rect = self.image.get_rect(topleft=pos)
self.vel = pg.math.Vector2(0, 0)
self.pos = pg.math.Vector2(pos)
self.blocks = blocks
self.on_ground = False
def update(self, dt):
# Move along x-axis.
self.pos.x += self.vel.x * dt
self.rect.x = self.pos.x
collisions = pg.sprite.spritecollide(self, self.blocks, False)
for block in collisions: # Horizontal collision occurred.
if self.vel.x > 0: # Moving right.
self.rect.right = block.rect.left # Reset the rect pos.
elif self.vel.x < 0: # Moving left.
self.rect.left = block.rect.right # Reset the rect pos.
self.pos.x = self.rect.x # Update the actual x-position.
# Move along y-axis.
self.pos.y += self.vel.y * dt
# +1 to check if we're on a platform each frame.
self.rect.y = self.pos.y + 1
# Prevent air jumping when falling.
if self.vel.y > 0:
self.on_ground = False
collisions = pg.sprite.spritecollide(self, self.blocks, False)
for block in collisions: # Vertical collision occurred.
if self.vel.y > 0: # Moving down.
self.rect.bottom = block.rect.top # Reset the rect pos.
self.vel.y = 0 # Stop falling.
self.on_ground = True
elif self.vel.y < 0: # Moving up.
self.rect.top = block.rect.bottom # Reset the rect pos.
self.vel.y = 0 # Stop jumping.
self.pos.y = self.rect.y # Update the actual y-position.
# Stop the player at screen bottom.
if self.rect.bottom >= WINDOW_HEIGHT:
self.vel.y = 0
self.rect.bottom = WINDOW_HEIGHT
self.pos.y = self.rect.y
self.on_ground = True
else:
self.vel.y += GRAVITY * dt # Gravity
class Block(pg.sprite.Sprite):
def __init__(self, rect):
super().__init__()
self.image = pg.Surface(rect.size)
self.image.fill(pg.Color('paleturquoise2'))
self.rect = rect
def main():
clock = pg.time.Clock()
done = False
dt = 0
all_sprites = pg.sprite.Group()
blocks = pg.sprite.Group()
player = Player((300, 100), blocks)
all_sprites.add(player)
rects = ((300, 200, 30, 70), (100, 350, 270, 30),
(500, 450, 30, 170), (400, 570, 270, 30),
(500, 150, 70, 170), (535, 310, 270, 70))
for rect in rects: # Create the walls/platforms.
block = Block(pg.Rect(rect))
all_sprites.add(block)
blocks.add(block)
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_a:
player.vel.x = -220
elif event.key == pg.K_d:
player.vel.x = 220
elif event.key == pg.K_w: # Jump
if player.on_ground:
player.vel.y = -470
player.pos.y -= 20
player.on_ground = False
elif event.type == pg.KEYUP:
if event.key == pg.K_a and player.vel.x < 0:
player.vel.x = 0
elif event.key == pg.K_d and player.vel.x > 0:
player.vel.x = 0
all_sprites.update(dt)
screen.fill(GRAY)
all_sprites.draw(screen)
pg.display.flip()
dt = clock.tick(60) / 1000
if __name__ == '__main__':
main()
pg.quit()
Here's a working version of the code that you've posted in the comments (only with vertical collisions, you need to add horizontal collisions as well). So when the player is jumping and collides with a platform, you have to set the player.rect.top to the platform.rect.bottom and change the vel.y.
import pygame as pg
from pygame.math import Vector2 as vec
pg.init()
WIDTH, HEIGHT = 800, 600
YELLOW = pg.Color('yellow')
GREEN = pg.Color('green')
BLACK = pg.Color('gray11')
screen = pg.display.set_mode((WIDTH,HEIGHT))
clock = pg.time.Clock()
FPS = 60
PLAYER_FRICTION = .95
PLAYER_ACC = .2
class Player(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((30, 40))
self.image.fill(YELLOW)
self.rect = self.image.get_rect(center=(WIDTH/2, HEIGHT-30))
self.pos = vec(WIDTH/2, HEIGHT/2)
self.vel = vec(0,0)
self.acc = vec(0,0)
def jump(self):
self.rect.y += 1
hits = pg.sprite.spritecollide(self, platforms, False)
self.rect.y -= 1
if hits:
self.vel.y = -13
def update(self):
self.acc = vec(0, 0.5)
keys = pg.key.get_pressed()
if keys[pg.K_a]:
self.acc.x = -PLAYER_ACC
if keys[pg.K_d]:
self.acc.x = PLAYER_ACC
# apply friction
self.vel.x *= PLAYER_FRICTION
self.vel += self.acc
self.pos += self.vel
# wrap around the sides of the screen
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
self.rect.midbottom = self.pos
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w, h))
self.image.fill(GREEN)
self.rect = self.image.get_rect(topleft=(x, y))
all_sprites = pg.sprite.Group()
platforms = pg.sprite.Group()
player = Player()
all_sprites.add(player)
# spawns and adds platforms to group
p1 = Platform(0, HEIGHT - 40, WIDTH, 40)
p2 = Platform(WIDTH / 2 - 50, HEIGHT - 300, 100, 20)
p3 = Platform(WIDTH / 2 - 100, HEIGHT - 150, 200, 20)
all_sprites.add(p1, p2, p3)
platforms.add(p1, p2, p3)
running = True
while running:
clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
player.jump()
all_sprites.update()
# Check if we hit a wall/platform.
hits = pg.sprite.spritecollide(player, platforms, False)
for platform in hits: # Iterate over the collided platforms.
if player.vel.y > 0: # We're falling.
player.rect.bottom = platform.rect.top
player.vel.y = 0
elif player.vel.y < 0: # We're jumping.
player.rect.top = platform.rect.bottom
player.vel.y = 3
player.pos.y = player.rect.bottom
#Draw / render
screen.fill(BLACK)
all_sprites.draw(screen)
pg.display.flip()
pg.quit()
BTW, in the jump method you have to change self.rect.y not self.rect.x.

sprites move in different speed

I encounter a problem in my 2D game but I don't know why.
I want to move sprites when the player reaches the middle of the game screen, but the sprites start to move in a slower speed, when they are 1px out of screen (That doesn't make sense to me...)
This is the only part in my code, in which i defined such a movement of the sprites:
if self.player.rect.centerx >= WIDTH/2:
# Ensuring the player stays in the middle of the screen
self.player.pos.x -= abs(self.player.vel.x)
for obj in self.movables:
# Relocating Sprite when Player moves
obj.rect.x -= abs(self.player.vel.x)
# Killin Sprite when it is out of the screen
if obj.rect.x < 0 - obj.rect.width:
obj.kill()
# Reseting Location of the Ground
if self.ground.rect.centerx <= 0:
self.ground.rect.x = 0
EDIT: cleaned up the code
Here is the part where the velocity of the player is being calculated:
def event(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
self.acc.x = -PLAYER_ACC
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
self.acc.x = PLAYER_ACC
def update(self):
self.acc = vec(0, PLAYER_GRAV)
self.event()
self.acc.x += self.vel.x * PLAYER_FRICTION
self.vel += self.acc
# Die Vel auf null setzen wenn sie ~0 ist
if abs(self.vel.x) < 0.1:
self.vel.x = 0
# Physik
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
I've added a code example of my problem. But in this code the player disappears after a few seconds, I think it has to do in the way I handle the collision with the platforms, but for now I don't know how to fix this too :D
To clearly see the problem of the moving sprites: Keep pressing "d", when the plat is half way out of the screen don't press the key "d" and you can see how the sprite is moving slower compared to the rest of the sprites on the screen
import pygame
WIDTH = 800
HEIGHT = 600
BGCOLOR = (181, 45, 76)
BLACK = (0, 0, 0)
# Player Properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.5
PLAYER_JUMP = -20
class Platform(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self._layer = 1
self.groups = game.all_sprites, game.movables, game.platforms
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pygame.Surface((w, h))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Ground(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
self._layer = 1
self.groups = game.all_sprites, game.movables, game.platforms
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pygame.Surface((w, h))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
vec = pygame.math.Vector2
class Player(pygame.sprite.Sprite):
def __init__(self, game):
self._layer = 2
self.groups = game.all_sprites
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pygame.Surface((30, 40))
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.pos = (WIDTH/2, HEIGHT/2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.jumping = False
def event(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
self.acc.x = -PLAYER_ACC
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
self.acc.x = PLAYER_ACC
def update(self):
self.acc = vec(0, PLAYER_GRAV)
self.event()
self.acc.x += self.vel.x * PLAYER_FRICTION
self.vel += self.acc
# Die Vel auf null setzen wenn sie ~0 ist
if abs(self.vel.x) < 0.1:
self.vel.x = 0
# Physik
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
def jump(self):
# Checking if the Player can Jump
self.rect.y += 1
hits = pygame.sprite.spritecollide(self.game.player, self.game.platforms, False)
self.rect.y -= 1
if hits:
if not self.jumping:
self.jumping = True
# Dieser Teil des Codes wird nie erreicht!!!
self.vel.y = PLAYER_JUMP
class Game:
def __init__(self):
self.running = True
pygame.init()
self.clock = pygame.time.Clock()
self.screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Moving Bug")
def new(self):
self.all_sprites = pygame.sprite.LayeredUpdates()
self.movables = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.ground = Ground(self, 0, HEIGHT-20, WIDTH*2, 20)
self.plat_1 = Platform(self, 400, 300, 100, 20)
self.plat_2 = Platform(self, 800, 400, 100, 20)
self.plat_3 = Platform(self, 100, 40, 100, 20)
self.player = Player(self)
self.run()
def run(self):
self.playing = True
while self.playing:
self.clock.tick(60)
self.event()
self.update()
self.draw()
def event(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.player.jump()
def update(self):
self.all_sprites.update()
# Only checking for collision if the player is jumping
if self.player.vel.y > 0:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.vel.y = 0
self.player.rect.bottom = hits[0].rect.top
self.player.jumping = False
# Left side of the screen
if self.player.rect.x < 0:
self.player.vel.x = 0
self.player.rect.x = 0
# Centering the player in the middle:
if self.player.rect.centerx >= WIDTH/2:
# Ensuring the player stays in the middle of the screen
self.player.pos.x -= abs(self.player.vel.x)
for obj in self.movables:
# Relocating Sprite when Player moves
obj.rect.x -= abs(self.player.vel.x)
# Killin Sprite when it is out of the screen
if obj.rect.x < 0 - obj.rect.width:
obj.rect.x = WIDTH
# Reseting Location of the Ground
if self.ground.rect.centerx <= 0:
self.ground.rect.x = 0
def draw(self):
self.screen.fill(BGCOLOR)
self.all_sprites.draw(self.screen)
pygame.display.flip()
g = Game()
while g.running:
g.new()
g.run()
pygame.quit()

Pygame - Python3.x text and sound only work some of the time at a certain part

This is everything
from pygame import *
import random as rand
from colors import *
import os
import time
pygame.mixer.pre_init(44100, 16, 2, 4096)
pygame.init()
fps = 60
window_size = window_width, window_height = 800, 600
font = pygame.font.SysFont( "Power Green, Times New Roman, Arial", 25 )
score = 0
pygame.mixer.music.load('music.wav')
pygame.mixer.music.play(-1)
explode = pygame.mixer.Sound("explode.wav")
class Player(pygame.sprite.Sprite):
#sprite for player
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("plane.png")
self.rect = self.image.get_rect()
self.setprop()
self.xspeed = 0
def setprop( self ):
self.rect = self.image.get_rect()
self.orgin_x = self.rect.centerx
self.orgin_y = self.rect.centery
def set_position(self, x, y):
self.rect.x = x - self.orgin_x
self.rect.y = y - self.orgin_y
def update(self):
self.xspeed = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.xspeed = -9
if keystate[pygame.K_RIGHT]:
self.xspeed = 9
self.rect.x += self.xspeed
if self.rect.right > window_width:
self.rect.right = window_width
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
sprites.add(bullet)
bullets.add(bullet)
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("boulder.png")
self.rect = self.image.get_rect()
self.rect.x = rand.randrange(window_width - self.rect.width)
self.rect.y = rand.randrange( -100, -40 )
self.speedy = rand.randrange(6, 8)
self.speedx = rand.randrange( -3, 3 )
def play_sound(self):
self.sound.play()
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.top > window_height + 10 or self.rect.left < -20 or self.rect.right > window_width + 20:
self.rect.x = rand.randrange(window_width - self.rect.width)
self.rect.y = rand.randrange( -100, -40 )
self.speedy = rand.randrange(6, 10 )
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill(red)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
self.rect.y += self.speedy
if self.rect.bottom > 0:
self.kill
def health_bars(player_health):
if player_health > 75:
player_health_color = green
elif player_health > 50:
player_health_color = yellow
else:
player_health_color = red
pygame.draw.rect(window, player_health_color, (20,20, player_health, 25))
def set_message(text):
global message, previous_message
message = font.render( 'HEALTH:' + str(player_health), True, white )
previous_message = message
def set_score(text):
global scoremsg
scoremsg = font.render( 'SCORE:' + str(score), True, white )
sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
bullets = pygame.sprite.Group()
player = Player()
sprites.add(player)
for i in range(12):
m = Mob()
sprites.add(m)
mobs.add(m)
player.set_position( window_width/2, window_height/2 +215 )
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( window_size, pygame.RESIZABLE )
pygame.display.set_caption('Dodge the boulders or shoot them (shooting gets you score)')
clock = pygame.time.Clock()
zap = pygame.mixer.Sound("zap.wav")
GAMEOVER = pygame.mixer.Sound("gameover.wav")
player_health = 100
running = True
while ( running ):
clock.tick( fps )
set_message( player_health)
set_score(set_score)
if (player_health == 0):
pygame.mixer.music.stop()
#window.blit(gameovertext, (window_width/2, window_height/2))
pygame.display.update()
pygame.mixer.Sound.play(GAMEOVER)
for event in pygame.event.get():
if( event.type == pygame.QUIT ):
running = False
for event in pygame.event.get():
if( event.type == pygame.QUIT ):
running = False
elif event.type == pygame.KEYDOWN:
if( event.key == pygame.K_SPACE ):
player.shoot()
window.fill(black)
health_bars(player_health)
sprites.update()
#collide group with group
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
if hits:
pygame.mixer.Sound.play(zap)
score += 1
for hit in hits:
m = Mob()
sprites.add(m)
mobs.add(m)
#collide sprite with group
hits = pygame.sprite.spritecollide(player, mobs, False)
if hits:
player_health = player_health - 1
sprites.draw(window)
window.blit( message, (20, 50) )
window.blit( scoremsg, (window_width/2 + 320, window_height - 35) )
if (player_health == 0):
pygame.mixer.music.stop()
gameoverfont = pygame.font.SysFont("Power Green", 100)
gameovertext = gameoverfont.render("GAME OVER", True, white )
window.blit(gameovertext, (window_width / 2 - 160, window_height / 2 -75))
pygame.mixer.Sound.play(GAMEOVER)
pygame.display.update()
pygame.display.quit()
pygame.quit()
and this is the part I'm having issues with:
if (player_health == 0):
pygame.mixer.music.stop()
gameoverfont = pygame.font.SysFont("Power Green", 100)
gameovertext = gameoverfont.render("GAME OVER", True, white )
window.blit(gameovertext, (window_width / 2 - 160, window_height / 2 -75))
pygame.mixer.Sound.play(GAMEOVER)
pygame.display.update()
What's weird is that the text appears some of the time, and it happens for varying amounts of time. I got one of the longer times the GAMEOVER text appears on recording at this link: https://www.youtube.com/watch?v=Xf8aKx4SOTc
I'd appreciate if someone could tell me what I'm doing wrong here, I'm quite new to pygame itself.
Edit: Alright youtube appears to be down. Basically what it was, was GAME OVER displaying for about 1.5 seconds before dissapeaing, and then the gameover voice audio would play twice. (not sure why twice)
First you have two if (player_health == 0): , try to join the needed lines in only one, this should resolve the sound-played-twice issue.
Also, you are using the same name hits to detect collision with bullets and mobs, it would be more readable and easier to use different names, and by doing this you avoid confusion with the score.
When it's GAME OVER, the loop is still running, and nothing prevent the score to increase or decrease.
With the if hits: for mobs, you should add another condition in the test, like :
if hits and player_health > 0:
player_health = player_health - 1
Otherwise you should change the health test to
if (player_health <= 0):
I hope this can fix the text displayed issue.

How do I make enemies drop money after they die?

I'm looking to make some enemies in my game drop "UP Points" (Upgrade Points in the form of small yellow squares) when I shoot them. I've tried a few different things but can't seem to figure out how to spawn these collectable points in place of where an enemy just died. Does anyone have any ideas about how I could implement this?
UP class:
class Up(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
self.image = pygame.Surface([5, 5])
self.image.fill(color)
self.rect = self.image.get_rect()
Here's the loop for when an enemy gets shot and dies:
for bullet in bullet_list: #For each bullet:
# Whenever a bullet collides with a zombie,
block_hit_list = pygame.sprite.spritecollide(bullet, zombie_list, True)
for i in block_hit_list:
# Destroy the bullet and zombie and add to the score
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 100
Sorry for not posting my whole code, the main game loop is at the bottom :)
import pygame
import math
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
ORANGE = (255, 119, 0)
ZOMBIE_GREEN = (122, 172, 34)
YELLOW = (255, 255, 0)
cursor_x = 100
cursor_y = 100
class Player(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
# pygame.Surface will create a rectangle with the width and height given
# and the command below it tells it to fill it in with that color
self.image = pygame.Surface([15, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
# This defines the starting position (x, y)
# of whatever sprite is passed through
self.rect.x = 600
self.rect.y = 300
# This is the current speed it will move when drawn
self.change_x = 0
self.change_y = 0
self.walls = None
# Defines how the player will move
def movement(self, x, y):
self.change_x += x
self.change_y += y
# Updates the information so the screen shows the player moving
def update(self):
self.rect.x += self.change_x
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.change_x > 0:
self.rect.right = block.rect.left
else:
self.rect.left = block.rect.right
self.rect.y += self.change_y
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.change_y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class Enemy(pygame.sprite.Sprite):
def __init__(self, color):
super().__init__()
self.image = pygame.Surface([20, 20])
self.image.fill(color)
self.rect = self.image.get_rect()
self.pos_x = self.rect.x = random.randrange(35, screen_width - 35)
self.pos_y = self.rect.y = random.randrange(35, screen_height - 135)
# How Zombies move towards player
def update(self):
zombie_vec_x = self.rect.x - player.rect.x
zombie_vec_y = self.rect.y - player.rect.y
vec_length = math.sqrt(zombie_vec_x ** 2 + zombie_vec_y ** 2)
if self.rect.x != player.rect.x and self.rect.y != player.rect.y:
zombie_vec_x = (zombie_vec_x / vec_length) * 1 # These numbers determine
zombie_vec_y = (zombie_vec_y / vec_length) * 1 # zombie movement speed
self.pos_x -= zombie_vec_x
self.pos_y -= zombie_vec_y
self.rect.x = self.pos_x
self.rect.y = self.pos_y
block_hit_list = pygame.sprite.spritecollide(self, sprites_list, False)
for block in block_hit_list:
if self.rect.x > 0:
self.rect.right = block.rect.left
elif self.rect.x < 0:
self.rect.left = block.rect.right
elif self.rect.y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class Wall(pygame.sprite.Sprite):
def __init__(self, color, x, y, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Cursor(pygame.sprite.Sprite):
def __init__(self, width, height):
self.groups = all_sprites_list
self._layer = 1
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.walls = None
# This updates the cursor to move along with your
# mouse position (defined in control logic)
def update(self):
self.rect.x = cursor_x
self.rect.y = cursor_y
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([8, 8])
self.image.fill(ORANGE)
self.rect = self.image.get_rect()
# Instead of using the rect. positions, we'll use pos_ variables
# to calculate position. This is because the rect. uses integers
# while a variable can have exact float numbers. This will keep
# the bullets trajectory exact istead of useing a general
# (rounded) whole number <3
self.pos_x = player.rect.x + 4 # Set up pos_x and pos_y here
self.pos_y = player.rect.y + 4 # rather than rect.x and rect.y
self.walls = None
self.change_x = 0
self.change_y = 0
speed = 6
bullet_vec_x = (cursor.rect.x - 4) - player.rect.x
bullet_vec_y = (cursor.rect.y - 4) - player.rect.y
vec_length = math.sqrt(bullet_vec_x ** 2 + bullet_vec_y ** 2)
bullet_vec_x = (bullet_vec_x / vec_length) * speed
bullet_vec_y = (bullet_vec_y / vec_length) * speed
self.change_x += bullet_vec_x
self.change_y += bullet_vec_y
def update(self):
self.pos_x += self.change_x # Update pos_x and pos_y. They will become floats
self.pos_y += self.change_y # which will let them maintain sub-pixel accuracy.
self.rect.x = self.pos_x # Copy the pos values into the rect, where they will be
self.rect.y = self.pos_y # rounded off. That's OK since we never read them back.
pygame.init()
screen_size = pygame.display.Info()
size = (1300, 720)
screen = pygame.display.set_mode(size)
#size = (screen_size.current_w, screen_size.current_h)
#screen = pygame.display.set_mode(
# ((screen_size.current_w, screen_size.current_h)),pygame.FULLSCREEN
# )
screen_width = screen_size.current_w
screen_height = screen_size.current_h
pygame.display.set_caption("Zombie Shooter")
wall_list = pygame.sprite.Group()
zombie1_list = pygame.sprite.Group()
sprites_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
all_sprites_list = pygame.sprite.Group()
# Walls are made here = (x_coord for where it starts,
# y_coord for where it starts, width of wall, height of wall)
# These walls are made with fullscreen dimentions, not any set dimentions
# Left
wall = Wall(BLUE, 0, 0, 10, screen_height)
wall_list.add(wall)
all_sprites_list.add(wall)
# Top
wall = Wall(BLUE, 0, 0, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# Bottom
wall = Wall(BLUE, 0, screen_height - 10, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# Right
wall = Wall(BLUE, screen_width - 10, 0, 10, screen_width)
wall_list.add(wall)
all_sprites_list.add(wall)
# HUD Border
wall = Wall(BLUE, 0, screen_height - 100, screen_width, 10)
wall_list.add(wall)
all_sprites_list.add(wall)
# This creates the actual player with the parameters set in ( ).
# However, we must add the player to the all_sprites_list
# so that it will actually be drawn to the screen with the draw command
# placed right after the screen.fill(BLACK) command.
player = Player(WHITE)
player.walls = wall_list
all_sprites_list.add(player)
zombie = Enemy(ZOMBIE_GREEN)
zombie.walls = wall_list
for i in range(5):
zombie = Enemy(ZOMBIE_GREEN)
all_sprites_list.add(zombie)
zombie1_list.add(zombie)
sprites_list.add(zombie)
cursor = Cursor(7, 7)
cursor.walls = wall_list
all_sprites_list.add(cursor)
bullet = Bullet()
font = pygame.font.SysFont("crushed", 30)
score = 0
up_score = 0
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
# -------- Main Program Loop -----------
while not done:
# --- Main event loop ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Press 'P' to quit the game_____
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
done = True
#________________________________
# Keyboard controls. The numbers inside change the speed of the player
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.movement(-4, 0)
elif event.key == pygame.K_d:
player.movement(4, 0)
elif event.key == pygame.K_w:
player.movement(0, -4)
elif event.key == pygame.K_s:
player.movement(0, 4)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movement(4, 0)
elif event.key == pygame.K_d:
player.movement(-4, 0)
elif event.key == pygame.K_w:
player.movement(0, 4)
elif event.key == pygame.K_s:
player.movement(0, -4)
# ___________________________________________________________________
# Mouse Controls----------------------------
pos = pygame.mouse.get_pos()
cursor_x = pos[0]
cursor_y = pos[1]
if cursor_x <= 10:
cursor_x = 10
if cursor_x >= (screen_width - 17):
cursor_x = (screen_width - 17)
if cursor_y <= 10:
cursor_y = 10
if cursor_y >= (screen_height - 107):
cursor_y = (screen_height - 107)
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Bullet()
all_sprites_list.add(bullet)
bullet_list.add(bullet)
#--------------------------------------------
all_sprites_list.update()
# How bullets vanish when they hit a sprite or a wall______________________
for bullet in bullet_list: #For each bullet:
# Whenever a bullet collides with a zombie,
block_hit_list = pygame.sprite.spritecollide(bullet, zombie1_list, True)
for i in block_hit_list:
# Destroy the bullet and zombie and add to the score
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 100
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, wall_list, False)
for i in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
#--------------------------------------------------------------------------
cursor.update()
bullet_list.update()
sprites_list.update()
pygame.mouse.set_visible(0)
screen.fill(BLACK)
all_sprites_list.draw(screen)
text = font.render("Score: " + str(score), True, WHITE)
screen.blit(text, [30, screen_height - 64])
pygame.display.flip()
clock.tick(60)
pygame.quit()
What you will have to do is before you destroy the zombie sprite grab its location and draw the UP coin. Then make sure that whenever the player controlled sprite, in this case the bullet, upon contact "collects" the coin. I haven't worked with python that much but the main code would look something like this:
def drawCoin():
zombieCoords = grabZombieCoords()
drawSprite(zombieCoords())
This would essentially just get the coordinates of the zombie, destroy it, and then place the coin at the last known location of the zombie.
Hope this helps.
Change your Up class to accept an argument pos, and use it to set the starting position:
class Up(pygame.sprite.Sprite):
def __init__(self, color, pos):
super().__init__()
self.image = pygame.Surface([5, 5])
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
Now, when you hit a zombie, create an Up, using the position of the zombie you just killed.
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, zombie_list, True)
for i in block_hit_list:
# just use .kill() to remove a Sprite from all of its Groups
# kill() may get called multiple times, but that does not hurt
bullet.kill()
score += 100
# create an Up for each killed zombie
# takes two arguments: color and pos
# we call .add() to add the Sprite immediately to
# the all_sprites_list and the up_list
Up((255, 0, 0), i.rect.center).add(all_sprites_list, up_list)
You didn't show the rest of your code, but I guess you call .draw and .update on your all_sprites_list and create a Group called up_list.

Two enemy movement glitches

So this is my game I've bean working on and so have things are going very well, the player can move around and shoot down the boxes which are ment to be moving but theres these small glitches, but before I go on heres the code:
import pygame, math, random, sys
from pygame import *
import random, math, cmath
pygame.init()
#variables end----------------------------------------------------------------
#imagers
grass = "grass_shit.png" #grass image
player_img = "shithead.png" #player name
ali_img = "shit_head2.png" #alien image
dead_screen = "dead_shit.png"
cross_hair = "crosshair.png"
playButton = "playbutton.png"
#screen
screen = pygame.display.set_mode((850, 640),0,32) #set screen
background = pygame.image.load(grass).convert() #load image to screen
health = 100
#mouse things
crosshair = pygame.image.load(cross_hair).convert_alpha()
#variables end----------------------------------------------------------------
pygame.mouse.set_visible(False)
black = ( 0, 0, 0)
white = ( 255, 255, 255)
red = ( 255, 0, 0)
blue = ( 0, 0, 255)
player_x, player_y = 0, 0
move_player_x, move_player_y = 0, 0
move_ali_x, move_ali_y = 0, 0
class Block(pygame.sprite.Sprite):
def __init__(self, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
def update(self):
global move_ali_x
global move_ali_y
if block.rect.x < player_x:
move_ali_x =+ 0.05
elif block.rect.x > player_x:
move_ali_x =- 0.05
if block.rect.y < player_y:
move_ali_y =+ 0.05
elif block.rect.y > player_y:
move_ali_y =- 0.05
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20,20])
self.image.fill(red)
self.rect = self.image.get_rect()
def update(self):
pos = pygame.mouse.get_pos()
self.rect.x = player_x
self.rect.y = player_y
class Bullet(pygame.sprite.Sprite):
def __init__(self, mouse, player):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([4, 10])
self.image.fill(black)
self.mouse_x, self.mouse_y = mouse[0], mouse[1]
self.player = player
self.rect = self.image.get_rect()
def update(self):
speed = 10
range = 50000
distance = [self.mouse_x - self.player[0], self.mouse_y - self.player[1]]
norm = math.sqrt(distance[0] ** 2 + distance[1] ** 2)
direction = [distance[0] / norm, distance[1] / norm]
bullet_vector = [direction[0] * speed, direction[1] * speed]
self.rect.x += bullet_vector[0]
self.rect.y += bullet_vector[1]
pygame.init()
screen_width = 850
screen_height = 640
screen = pygame.display.set_mode([screen_width,screen_height])
all_sprites_list = pygame.sprite.Group()
block_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
for i in range(5):
block = Block(blue)
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(350)
block_list.add(block)
all_sprites_list.add(block)
player = Player()
all_sprites_list.add(player)
done = False
clock = pygame.time.Clock()
score = 0
player.rect.y = 370
# -------- Main Program Loop -----------
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Bullet(event.pos, [player.rect.x, player.rect.y])
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
all_sprites_list.add(bullet)
bullet_list.add(bullet)
if event.type== pygame.KEYDOWN:
if event.key==K_a:
move_player_x=-4
elif event.key==K_d:
move_player_x=+4
elif event.key==K_w:
move_player_y=-4
elif event.key==K_s:
move_player_y=+4
if event.type== pygame.KEYUP:
if event.key==K_a:
move_player_x=0
elif event.key==K_d:
move_player_x=0
elif event.key==K_w:
move_player_y=0
elif event.key==K_s:
move_player_y=0
# --- Game logic
all_sprites_list.update()
player_x += move_player_x
player_y += move_player_y
block.rect.y += move_ali_y
block.rect.x += move_ali_x
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print( score )
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
if player.rect.colliderect(block.rect):
health =- 35
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_x -= crosshair.get_width() / 2
mouse_y -= crosshair.get_height() / 2
screen.blit(background,(0,0))
all_sprites_list.draw(screen)
screen.blit(crosshair,(mouse_x, mouse_y))
pygame.display.flip()
pygame.display.update()
clock.tick(20)
pygame.quit()
So glitch number one:
only one of the boxers moves, I cant figure out why it only one of them is moving towards the player, all the boxers are meant to move towards the player as this is hoping to become a zombie shooter.
Glitch two:
At a random point the box that does move does stops moving in all directions but one, so lets say this happens when the box in in the center of the screen, if the player goes to the left of the box, nothing, but when the player moves to thr right of the player it moves right, but only right not up or down, and this seams to happen at soem point everytime.
Well thats it, hope you can help thanks heaps stackoverflow
Your code should looks like this:
import pygame
from pygame import *
import sys
import math
import random
import cmath
#----------------------------------------------------------------------
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
#imagers
IMAGE_GRASS = "grass_shit.png" #grass image
IMAGE_PLAYER = "shithead.png" #player name
IMAGE_ALI = "shit_head2.png" #alien image
IMAGE_DEAD_SCREEN = "dead_shit.png"
IMAGE_CROSSHAIR = "crosshair.png"
IMAGE_PLAYBUTTON = "playbutton.png"
#~ IMAGE_GRASS = "ball3.png" #grass image
#~ IMAGE_PLAYER = "ball2.png" #player name
#~ IMAGE_ALI = "ball3.png" #alien image
#~ IMAGE_DEAD_SCREEN = "ball3.png"
#~ IMAGE_CROSSHAIR = "ball1.png"
#~ IMAGE_PLAYBUTTON = "ball3.png"
#----------------------------------------------------------------------
class Block(pygame.sprite.Sprite):
def __init__(self, color, x, y, player = None):
pygame.sprite.Sprite.__init__(self)
self.player = player
self.image = pygame.Surface([20, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.move_x = self.move_y = 0
def update(self):
if self.player:
player_x, player_y = self.player.rect.center
if self.rect.x < player_x:
self.rect.x += 1
elif self.rect.x > player_x:
self.rect.x -= 1
if self.rect.y < player_y:
self.rect.y += 1
elif self.rect.y > player_y:
self.rect.y -= 1
#----------------------------------------------------------------------
class Player(pygame.sprite.Sprite):
def __init__(self, screen_rect, x=0, y=0):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20,20])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.min_x = screen_rect.left
self.min_y = screen_rect.top
self.max_x = screen_rect.right
self.max_y = screen_rect.bottom
self.move_x = self.move_y = 0
self.health = 100
def update(self):
pos = pygame.mouse.get_pos()
self.rect.x += self.move_x
self.rect.y += self.move_y
if self.rect.top < self.min_x:
self.rect.top = self.min_x
elif self.rect.bottom > self.max_y:
self.rect.bottom = self.max_y
if self.rect.left < self.min_x:
self.rect.left = self.min_x
elif self.rect.right > self.max_x:
self.rect.right = self.max_x
def event_handler(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
self.move_x = -4
elif event.key == pygame.K_d:
self.move_x = +4
elif event.key == pygame.K_w:
self.move_y = -4
elif event.key == pygame.K_s:
self.move_y = +4
if event.type == pygame.KEYUP:
if event.key in (pygame.K_a, pygame.K_d):
self.move_x = 0
elif event.key in (pygame.K_w, pygame.K_s):
self.move_y = 0
#----------------------------------------------------------------------
class Bullet(pygame.sprite.Sprite):
def __init__(self, start_pos, mouse_pos):
pygame.sprite.Sprite.__init__(self)
self.start_rect = start_pos.rect.copy()
self.mouse_x, self.mouse_y = mouse_pos # mouse[0], mouse[1]
self.image = pygame.Surface([5, 5])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
self.rect.centerx = self.start_rect.centerx
self.rect.centery = self.start_rect.centery
self.speed = 10
self.max_range = 50
self.current_range = 0
distance_x = self.mouse_x - self.start_rect.centerx
distance_y = self.mouse_y - self.start_rect.centery
norm = math.sqrt(distance_x ** 2 + distance_y ** 2)
direction_x = distance_x / norm
direction_y = distance_y / norm
self.bullet_vector_x = direction_x * self.speed
self.bullet_vector_y = direction_y * self.speed
def update(self):
self.current_range += 1
if self.current_range < self.max_range:
print self.start_rect.centerx + (self.bullet_vector_x*self.current_range),
print self.rect.centerx + self.bullet_vector_x,
#self.rect.centerx += self.bullet_vector_x
self.rect.centerx = self.start_rect.centerx + (self.bullet_vector_x*self.current_range)
print self.rect.centerx
#self.rect.centery += self.bullet_vector_y
self.rect.centery = self.start_rect.centery + (self.bullet_vector_y*self.current_range)
else:
self.kill()
#----------------------------------------------------------------------
class Crosshair(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(IMAGE_CROSSHAIR).convert_alpha()
self.rect = self.image.get_rect()
def update(self):
mouse_x, mouse_y = pygame.mouse.get_pos()
self.rect.centerx = mouse_x
self.rect.centery = mouse_y
def draw(self, screen):
screen.blit(self.image,self.rect.topleft)
#----------------------------------------------------------------------
class Background(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(IMAGE_GRASS).convert_alpha()
self.rect = self.image.get_rect()
def draw(self, screen):
screen.fill((128,128,128))
screen.blit(self.image,(0,0))
#----------------------------------------------------------------------
class Game():
def __init__(self):
pygame.init()
screen_width = 850
screen_height = 640
self.screen = pygame.display.set_mode( (screen_width,screen_height) )
pygame.mouse.set_visible(False)
#-----
self.all_sprites_list = pygame.sprite.Group()
self.block_list = pygame.sprite.Group()
self.bullet_list = pygame.sprite.Group()
# --- create sprites ---
self.background = Background()
self.player = Player(self.screen.get_rect(), 0, 370)
self.all_sprites_list.add(self.player)
for i in range(5):
block = Block(BLUE, random.randrange(100, screen_width), random.randrange(10, screen_height-10), self.player)
self.block_list.add(block)
self.all_sprites_list.add(block)
self.crosshair = Crosshair()
#-----
font = pygame.font.SysFont("", 72)
self.text_pause = font.render("PAUSE", -1, RED)
self.text_pause_rect = self.text_pause.get_rect(center=self.screen.get_rect().center) # center text
#-----
self.score = 0
def bullet_create(self, start_pos, mouse_pos):
bullet = Bullet(start_pos, mouse_pos)
self.all_sprites_list.add(bullet)
self.bullet_list.add(bullet)
def bullets_update(self):
for bullet in self.bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, self.block_list, True)
for block in block_hit_list:
self.bullet_list.remove(bullet)
self.all_sprites_list.remove(bullet)
self.score += 1
print self.score
if bullet.rect.y < -10:
self.bullet_list.remove(bullet)
self.all_sprites_list.remove(bullet)
if pygame.sprite.collide_rect(self.player, block):
self.player.health =- 35
# -------- Main Program Loop -----------
def run(self):
clock = pygame.time.Clock()
RUNNING = True
PAUSED = False
while RUNNING:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
RUNNING = false
elif event.key == pygame.K_SPACE:
PAUSED = not PAUSED
elif event.type == pygame.MOUSEBUTTONDOWN:
self.bullet_create(self.player, event.pos)
# send event to player
self.player.event_handler(event)
# send event to crosshair for mousebuttondown
#if not PAUSED:
# self.crosshair.event_handler(event)
# --- updates ---
if not PAUSED:
self.all_sprites_list.update()
self.bullets_update()
self.crosshair.update()
# --- draws ---
self.background.draw(self.screen)
self.all_sprites_list.draw(self.screen)
self.crosshair.draw(self.screen)
if PAUSED:
self.screen.blit(self.text_pause, self.text_pause_rect.topleft)
pygame.display.update() # use flip() OR update()
# --- FPS ---
clock.tick(20)
# --- quit ---
pygame.quit()
#----------------------------------------------------------------------
Game().run()
Changes:
player can move but can leave screen
press space to pause game - but you can still move cursor and fire :)
bullet has max range - than it is removed
i change bullet vetor calculations because value was rounded to integer every frame and bullet trajectory was incorrect
bullet never change trajectory when player is moving
all code is in classes except some constant values. I add class Crosshair and Background
you can see how class (Player) handle events in event_handle
I use pygame.Rect() (screen.get_rect(), image.get_rect()) to get rect.top, rect.left, rect.center, rect.centerx, rect.topleft, etc.
There is still many things to do.
ps. if someone needs images 'ball1.png', 'ball2.png', 'ball3.png' to run this example you can find it in answer to
Space invaders project
Pygame- window and sprite class - python

Categories