This question already has answers here:
Pygame collision with masks
(1 answer)
Collision between masks in pygame
(1 answer)
Pygame mask collision
(1 answer)
How can I made a collision mask?
(1 answer)
Closed 2 years ago.
I am trying to have a pixel perfect collision with my pong object and my rotated paddle object. However, I realized that the pong ball is still hitting the rectangle of the rotated paddle object. As you can see, the ball is hitting the rect of the paddle, instead of the actual paddle
Here is my pong class with defined collision detection. Please check the checkCollision() function because I use the pygame.sprite.colliderect() function.
class Pong(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = screensize[0] // 2
self.centery = screensize[1] // 2
self.radius = 25
self.rect = pygame.Rect(self.centerx-self.radius,
self.centery-self.radius,
self.radius*2, self.radius*2)
self.image = pygame.image.load("pokeball.png").convert()
self.colorkey = self.image.get_at((0,0))
self.image = pygame.transform.scale(self.image, (self.radius, self.radius))
#Create the mask
self.mask = pygame.mask.from_surface(self.image)
self.color = (100,100,255)
self.direction = [-2,1]
self.speedx = 2
self.speedy = 3
self.hit_edge_left = False
self.hit_edge_right = False
def checkCollision(self, player_paddle, ai_paddle):
col = pygame.sprite.collide_rect(self, player_paddle)
return col
def collisionFormula(self, player_paddle, ai_paddle):
if self.checkCollision(self, player_paddle):
relative_IntersectionY = player_paddle.centery - self.rect.centery
normal_IntersectionY = relative_IntersectionY / (player_paddle.height /2)
bounce_angle = normal_IntersectionY * (5*math.pi/ 12 )
self.direction[0] = math.cos(bounce_angle)
self.direction[1] = -1 * math.sin(bounce_angle)
def update_ball_position(self):
#update the position of the ball
self.centerx += self.direction[0]*self.speedx
self.centery += self.direction[1]*self.speedy
self.rect.center = (self.centerx, self.centery)
def reset_ball(self):
if self.rect.right >= self.screensize[0]-1:
self.hit_edge_right = True
self.__init__(self.screensize)
elif self.rect.left <= 0:
self.hit_edge_left = True
self.__init__(self.screensize)
def collision_checks(self, player_paddle, ai_paddle):
#if the ball hits the top or bottom of the screen, change the y direction
if self.rect.top <= 0 or self.rect.bottom >= self.screensize[1] - 1:
self.direction[1] *= -1
#if the pong hits the paddles, change how the pong ball moves
if self.rect.colliderect(player_paddle.rect) or self.rect.colliderect(ai_paddle.rect):
self.collisionFormula(player_paddle, ai_paddle)
def update(self, player_paddle, ai_paddle):
self.update_ball_position()
self.reset_ball()
self.collision_checks(player_paddle, ai_paddle)
def render(self, screen):
screen.blit(self.image, (self.centerx, self.centery))<!-- Paste the part of the code that shows the problem.
Here is my PlayerPaddle class with defined rotate functions.
class PlayerPaddle(pygame.sprite.Sprite):
def __init__(self, screensize):
pygame.sprite.Sprite.__init__(self)
self.screensize = screensize
self.centerx = 50
self.centery = screensize[1]//2
self.height = 100
self.width = 20
self.imageMaster = pygame.image.load("naruto.png").convert()
self.colorkey = self.imageMaster.get_at((0,0))
self.imageMaster = pygame.transform.scale(self.imageMaster, (100, 200))
self.image = self.imageMaster
#mask
#self.maskMaster = pygame.mask.from_surface(self.image)
#self.mask = self.maskMaster
self.rect = self.image.get_rect()
self.rect.center = (screensize[0]//2, screensize[1]//2)
self.dir = 0
self.color = (100,255,100)
self.speed = 3
self.direction = 0
self.update()
def update(self):
#Rotate functions
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMaster, self.dir).convert()
self.image.set_colorkey(self.colorkey)
self.rect = self.image.get_rect()
#self.rect.center = oldCenter
self.mask = pygame.mask.from_surface(self.image)
self.centery += self.direction*self.speed
self.rect.center = (self.centerx, self.centery)
if self.rect.top < 0:
self.rect.top = 0
if self.rect.bottom > self.screensize[1]-1:
self.rect.bottom = self.screensize[1]-1
def render(self, screen):
screen.blit(self.image, (self.rect.x, self.rect.y))
def turnLeft(self):
self.dir += 45
if self.dir > 360:
self.dir = 45
def turnRight(self):
self.dir -= 45
if self.dir < 0:
self.dir = 315
And I write this within the while loop of my main().
if pygame.sprite.spritecollide(pong_sprite, paddleGroup, False, pygame.sprite.collide_mask):
print("Collided")
I'm not sure why this is occurring but I'd like to know how to get a pixel perfect collision so I can avoid the issue from the image.
EDIT:
I changed the update function of my PlayerPaddle class.
def update(self):
#Rotate functions
oldCenter = self.rect.center
self.image = pygame.transform.rotate(self.imageMaster, self.dir)
self.rect = self.image.get_rect()
self.rect.center = oldCenter
self.mask = pygame.mask.from_surface(self.image)
self.centery += self.direction*self.speed
self.rect.center = (self.centerx, self.centery)
if self.rect.top < 0:
self.rect.top = 0
if self.rect.bottom > self.screensize[1]-1:
self.rect.bottom = self.screensize[1]-1
And here is my main():
def main():
pygame.init()
screensize = (640,700)
screen = pygame.display.set_mode(screensize)
background = pygame.Surface(screen.get_size())
background.fill((0, 255, 0))
clock = pygame.time.Clock()
pong_sprite = Pong(screensize)
player_paddle = PlayerPaddle(screensize)
ai_paddle = AIPaddle(screensize)
paddleGroup = pygame.sprite.Group()
paddleGroup.add(player_paddle)
pongGroup = pygame.sprite.Group()
pongGroup.add(pong_sprite)
running = True
keymap = {pygame.K_u: False}
while running:
#fps limiting/reporting phase
clock.tick(64)
screen.fill((100,100,100))
#event handling phase
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == KEYDOWN:
if event.key in keymap:
keymap[event.key] = True
if event.key == K_UP:
player_paddle.direction = -1
elif event.key == K_DOWN:
player_paddle.direction = 1
elif event.key == K_LEFT:
player_paddle.turnLeft()
elif event.key == K_RIGHT:
player_paddle.turnRight()
if event.type == KEYUP:
if event.key in keymap:
keymap[event.key] = False
if event.key == K_UP and player_paddle.direction == -1:
player_paddle.direction = 0
elif event.key == K_DOWN and player_paddle.direction == 1:
player_paddle.direction = 0
#object updating phase
ai_paddle.update(pong_sprite, player_paddle)
#player_paddle.update()
#pong.update(player_paddle, ai_paddle)
if pygame.sprite.spritecollide(pong_sprite, paddleGroup, False, pygame.sprite.collide_mask):
print("Collided")
if pong_sprite.hit_edge_left:
print("You won")
#running = False
elif pong_sprite.hit_edge_right:
print("You Lose")
#running = False
#rendering phase
ai_paddle.render(screen)
player_paddle.render(screen)
#pong.render(screen)
paddleGroup.clear(screen, background)
paddleGroup.update()
paddleGroup.draw(screen)
pongGroup.clear(screen,background)
pongGroup.update(player_paddle, ai_paddle)
pongGroup.draw(screen)
pygame.display.flip()
pygame.quit()
Here is my imageenter image description here:
This is the naruto.jpg image
And here is the pokeball.jpg
enter image description here
Related
I am stuck on how to write my collision function in my player class. Also should I put my collision check function in my player class or should it be in my bullet class? I honestly don't know. I mean common sense says my bullet should have the collision check function in it, just because I want the top of my bullet checking if it hits a falling cement block sprite, but I don't know.... I would appreciate an answer on how to code my collision function. This would really help me out. My collision function is right below my shoot function in my player class.
My code:
import pygame
pygame.init()
#screen settings
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255, 255, 255))
#fps
FPS = 120
clock = pygame.time.Clock()
#load images
bg = pygame.image.load('background/street.png').convert_alpha() # background
bullets = pygame.image.load('car/bullet.png').convert_alpha()
debris_img = pygame.image.load('debris/cement.png')
#define game variables
shoot = False
#player class
class Player(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.bullet_list = []
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image, (self.rect.centerx, self.rect.centery))
#move car
def move(self):
#reset the movement variables
dx = 0
dy = 0
#moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
#update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot
def shoot(self):
bullet = Bullet(self.rect.centerx + 18, self.rect.y + 30, self.direction)
bullet_group.add(bullet)
#def collision(self):
#write code here
#bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 5
self.image = bullets
self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.direction = direction
def update(self):
self.rect.centery -= self.speed
#check if bullet has gone off screen
if self.rect.top < 1:
self.kill()
#debris class
class Debris(pygame.sprite.Sprite):
def __init__(self, x, y, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.scale = scale
self.x = x
self.y = y
self.speed = speed
self.vy = 0
self.on_ground = True
self.move = True
self.health = 4
self.max_health = self.health
self.alive = True
#load debris
self.image = debris_img
self.rect = self.image.get_rect()
self.rect.center = (x,y)
######################CAR/DEBRIS##########################
player = Player(1,5)
debris = Debris(300,15,1,5)
##########################################################
#groups
bullet_group = pygame.sprite.Group()
debris_group = pygame.sprite.Group()
debris_group.add(debris)
#game runs here
run = True
while run:
#draw street
screen.blit(bg, [0, 0])
#update groups
bullet_group.update()
bullet_group.draw(screen)
debris_group.update()
debris_group.draw(screen)
#draw car
player.draw()
player.move()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
player.movingLeft = True
if event.key == pygame.K_d:
player.movingRight = True
if event.key == pygame.K_SPACE:
player.shoot()
shoot = True
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movingLeft = False
if event.key == pygame.K_d:
player.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
I suggest reading How do I detect collision in pygame?. Use pygame.sprite.spritecollide() for the collision detection. Set the dokill argument True. So the bullets gat automatically destroyed.
The following code is base on one of your previous questions: Check collision of bullet sprite hitting cement block sprite
class Car(pygame.sprite.Sprite):
# [...]
def collision(self, debris_group):
for debris in debris_group:
if pygame.sprite.spritecollide(debris, bullet_group, True):
debris.health -= 1
if debris.health <= 0:
debris.kill()
This question already exists:
I want to do an explosion animation. I have 4 png files number 0 - 3. In my debris class I loaded them into an image list using a for loop [duplicate]
Closed 1 year ago.
I want to do an explosion animation. I have 4 png files number 0 - 3. In my debris class I loaded them into an image list using a for loop. Then I made an explosion function but when I load my pygame windows and start shooting my cement block sprites, my cement block sprites disappear which is fine but they don't do the exploding animation I want. How can I fix this problem. Would appreciate the help.
My code:
import random
import pygame
import pygame.freetype
pygame.init()
#screen settings
WIDTH = 1000
HEIGHT = 400
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("AutoPilot")
screen.fill((255, 255, 255))
#fps
FPS = 120
clock = pygame.time.Clock()
#load images
bg = pygame.image.load('background/street.png').convert_alpha() # background
bullets = pygame.image.load('car/bullet.png').convert_alpha()
debris_img = pygame.image.load('debris/cement.png')
#define game variables
shoot = False
#player class
class Player(pygame.sprite.Sprite):
def __init__(self, scale, speed):
pygame.sprite.Sprite.__init__(self)
self.bullet = pygame.image.load('car/bullet.png').convert_alpha()
self.bullet_list = []
self.speed = speed
#self.x = x
#self.y = y
self.moving = True
self.frame = 0
self.flip = False
self.direction = 0
self.score = 0
#load car
self.images = []
img = pygame.image.load('car/car.png').convert_alpha()
img = pygame.transform.scale(img, (int(img.get_width()) * scale, (int(img.get_height()) * scale)))
self.images.append(img)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.update_time = pygame.time.get_ticks()
self.movingLeft = False
self.movingRight = False
self.rect.x = 465
self.rect.y = 325
#draw car to screen
def draw(self):
screen.blit(self.image, (self.rect.centerx, self.rect.centery))
#move car
def move(self):
#reset the movement variables
dx = 0
dy = 0
#moving variables
if self.movingLeft and self.rect.x > 33:
dx -= self.speed
self.flip = True
self.direction = -1
if self.movingRight and self.rect.x < 900:
dx += self.speed
self.flip = False
self.direction = 1
#update rectangle position
self.rect.x += dx
self.rect.y += dy
#shoot
def shoot(self):
bullet = Bullet(self.rect.centerx + 18, self.rect.y + 30, self.direction)
bullet_group.add(bullet)
#check collision
def collision(self, debris_group):
for debris in debris_group:
if pygame.sprite.spritecollide(debris, bullet_group, True):
debris.health -= 1
if debris.health <= 0:
self.score += 1
debris.kill()
#player stats
def stats(self):
myfont = pygame.font.SysFont('comicsans', 30)
scoretext = myfont.render("Score: " + str(self.score), 1, (0,0,0))
screen.blit(scoretext, (100,10))
#bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, direction):
pygame.sprite.Sprite.__init__(self)
self.speed = 5
self.image = bullets
self.rect = self.image.get_rect()
self.rect.center = (x,y)
self.direction = direction
def update(self):
self.rect.centery -= self.speed
#check if bullet has gone off screen
if self.rect.centery < 1:
self.kill()
#debris class
class Debris(pygame.sprite.Sprite):
def __init__(self,scale,speed):
pygame.sprite.Sprite.__init__(self)
self.scale = scale
self.x = random.randrange(100,800)
self.speed_y = 10
self.y = 15
self.speed = speed
self.vy = 0
self.on_ground = True
self.move = True
self.health = 4
self.max_health = self.health
self.alive = True
self.velocity = random.randrange(1,2)
self.speed_x = random.randrange(-3,3)
self.moving_down = True
#load debris
self.image = debris_img
self.rect = self.image.get_rect()
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.rect.center = (self.x,self.y)
#load explosion
self.images = []
for i in range(4):
self.images.append(pygame.image.load(f'explosion/{i}.png'))
self.index = 0
self.img = self.images[self.index]
#spawn new debris
def spawn_new_debris(self):
self.rect.x = random.randrange(100, 800)
self.rect.y = random.randrange(-150, -100)
self.velocity = random.randrange(1, 2)
self.speed_x = random.randrange(-3, 3)
#respawn debris when they go of the screen
def boundaries(self):
if self.rect.left > WIDTH + 10 or self.rect.right < -10 or self.rect.top > HEIGHT + 10:
self.spawn_new_debris()
#update image
def update(self):
self.rect.y += self.velocity
self.rect.x += self.speed_x
self.boundaries()
#make debris fall down
def falldown(self):
self.rect.centery += self.velocity
if self.moving_down and self.rect.y > 350:
self.kill()
#explosion
def explode(self):
if self.max_health <= 0:
self.index += 1
if self.index >= len(self.images):
self.index = 0
self.img = self.images[self.index]
######################CAR/DEBRIS##########################
player = Player(1,5)
##########################################################
#groups
bullet_group = pygame.sprite.Group()
debris_group = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
for i in range(50):
d = Debris(1,5)
debris_group.add(d)
all_sprites.add(d)
#game runs here
run = True
while run:
#draw street
screen.blit(bg, [0, 0])
#update groups
bullet_group.update()
bullet_group.draw(screen)
debris_group.update()
debris_group.draw(screen)
#draw car
player.draw()
player.move()
player.collision(debris_group)
player.stats()
#update all sprites
all_sprites.update()
all_sprites.draw(screen)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#check if key is down
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
if event.key == pygame.K_a:
player.movingLeft = True
if event.key == pygame.K_d:
player.movingRight = True
if event.key == pygame.K_SPACE:
player.shoot()
shoot = True
#check if key is up
if event.type == pygame.KEYUP:
if event.key == pygame.K_a:
player.movingLeft = False
if event.key == pygame.K_d:
player.movingRight = False
#update the display
pygame.display.update()
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
My game has a sprite which can move left and right and shoot bullets. However, if I shoot out too many bullets (about 50 or so), my game starts lagging a lot. My sprite doesn't move fluently anymore and the game doesn't work well. So I think the problem to this is that all the bullets created are continuing to be run outside my screen. That's why I would like to know how to set a range limit to my bullet. Once past that limit it disappears and out of the program so my game won't lag. However, I am open to other suggestions like if there is a way to make my game no lag without setting a range limit. I have separated my game into 2 .py files a main.py and a Sprite1.py which I import to my main.
Here's my Sprite1.py file:
import pygame
import sys
import os
import time
from pygame import mixer
from pygame.locals import *
def showStartScreen(surface):
show = True
while (show == True):
background = pygame.image.load(os.path.join('images', 'Starting_scr.png'))
# rect = surface.get_rect()
surface.blit(background, (0,0))
pygame.display.flip()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
show = False
class Player(pygame.sprite.Sprite):
def __init__(self, all_sprites):
pygame.sprite.Sprite.__init__(self)
self.movex = 0
self.movey = 0
self.frame = 0
self.images = []
self.imagesleft = []
self.imagesright = []
self.direction = "right"
# self.rect = self.image.get_rect(center=pos)
self.alpha = (0,0,0)
self.ani = 4 # animation cycles
self.all_sprites = all_sprites
self.add(self.all_sprites)
self.bullet_timer = .1
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesright.append(img)
self.image = self.imagesright[0]
self.rect = self.image.get_rect()
for i in range(1,5):
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
img = pygame.transform.flip(img, True, False)
img.convert_alpha()
img.set_colorkey(self.alpha)
self.imagesleft.append(img)
self.image = self.imagesleft[0]
self.rect = self.image.get_rect()
def control(self,x,y):
'''
control player movement
'''
self.movex += x
self.movey -= y
def update(self, dt):
'''
Update sprite position
'''
self.rect.x = self.rect.x + self.movex
self.rect.y = self.rect.y + self.movey
# moving left
if self.movex < 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesleft[self.frame//self.ani]
self.direction = "left"
# moving right
if self.movex > 0:
self.frame += 1
if self.frame > 3*self.ani:
self.frame = 0
self.image = self.imagesright[self.frame//self.ani]
self.direction = "right"
# self.rect.center = pygame.mouse.get_pos()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.bullet_timer -= dt # Subtract the time since the last tick.
if self.bullet_timer <= 0:
self.bullet_timer = 0 # Bullet ready.
if keys: # Left mouse button.
# Create a new bullet instance and add it to the groups.
if self.direction == "right":
Bullet([self.rect.x + self.image.get_width(), self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
else:
Bullet([self.rect.x, self.rect.y + self.image.get_height()/2], self.direction, self.all_sprites)
self.bullet_timer = .1 # Reset the timer.
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.sound = pygame.mixer.music.play()
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(750, 0)
self.direction = direction
def update(self, dt):
# Add the velocity to the position vector to move the sprite
if self.direction == "right":
#self.vel = pygame.math.Vector2(750, 0)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.pos += self.vel * dt
else:
#self.vel = pygame.math.Vector2(-750, 0)
BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png'))
self.image = pygame.transform.flip(BULLET_IMG, True, False)
self.rect = self.image.get_rect()
self.pos -= self.vel * dt
#print(self.pos)
self.rect.center = self.pos # Update the rect pos.
if self.rect.bottom <= 0:
self.kill()
And this is my main.py file:
import pygame
import os
import sys
import time
from pygame import mixer
import Sprite1
'''
Setup
'''
pygame.init()
width = 960
height = 720
fps = 40 # frame rate
#ani = 4 # animation cycles
clock = pygame.time.Clock()
pygame.display.set_caption('B.S.G.!!!')
surface = pygame.display.set_mode((width, height))
#pygame.mixer.music.load('.\\sounds\\Fairy.mp3')
#pygame.mixer.music.play(-1, 0.0)
pygame.mixer.music.load('.\\sounds\\Fireball.wav')
#direction = "right"
all_sprites = pygame.sprite.Group()
player = Sprite1.Player(all_sprites)
player.rect.x = 50
player.rect.y = 500
steps = 10 # how fast to move
Sprite1.showStartScreen(surface)
'''
Main loop
'''
main = True
while main == True:
background = pygame.image.load(os.path.join('images', 'Bg.png'))
surface.blit(background, (0,0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
main = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(-steps,0)
#direction = "left"
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(steps,0)
#direction = "right"
if event.key == pygame.K_UP or event.key == ord('w'):
player.rect.y -= 100
#player.rect.y -= 10
#player.movey == 10
#player.movey == -10
#time.sleep(1)
#player.control(0,-steps)
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == ord('a'):
player.control(steps,0)
#direction = "left"
if event.key == pygame.K_RIGHT or event.key == ord('d'):
player.control(-steps,0)
#direction = "right"
if event.key == pygame.K_UP or event.key == ord('w'):
player.rect.y += 100
#player.movey == -10
if event.key == ord('q'):
pygame.quit()
sys.exit()
main = False
# dt = time since last tick in milliseconds.
dt = clock.tick(60) / 1000
all_sprites.update(dt)
player.update(dt)
all_sprites.draw(surface) #refresh player position
pygame.display.flip()
P.S.: don't mind how my sprite jumps, I just wrote it that way for now. I will be making him actually jump later on with gravity. Thanks beforehand.
You already check if the bullets leave the screen at the top:
if self.rect.bottom <= 0:
self.kill()
You could change it to
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.kill()
to kill the sprite if it is not at the screen at all.
But your problem is actually this:
def update(self, dt):
# Add the velocity to the position vector to move the sprite
if self.direction == "right":
#self.vel = pygame.math.Vector2(750, 0)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.pos += self.vel * dt
else:
#self.vel = pygame.math.Vector2(-750, 0)
BULLET_IMG = pygame.image.load(os.path.join('images','fireball.png'))
self.image = pygame.transform.flip(BULLET_IMG, True, False)
self.rect = self.image.get_rect()
self.pos -= self.vel * dt
...
Here you're loading the fireball.png image once for every instance of the Bullet class
every frame. You aim for 60 fps, so when there are 50 Bullet instances you try to load the file 300 times per second from your disk.
Instead, you should load the image once at startup.
Here's how it could look like:
class Bullet(pygame.sprite.Sprite):
IMAGE = None
FLIPPED_IMAGE = None
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
# cache images
if not Bullet.IMAGE:
Bullet.IMAGE = pygame.image.load(os.path.join('images','fireball.png'))
Bullet.FLIPPED_IMAGE = pygame.transform.flip(Bullet.IMAGE, True, False)
if direction == "right":
self.vel = pygame.math.Vector2(750, 0)
self.image = Bullet.IMAGE
else:
self.vel = pygame.math.Vector2(-750, 0)
self.image = Bullet.FLIPPED_IMAGE
# suspicious... Should use the Sound class instead
# self.sound = pygame.mixer.music.play()
self.pos = pygame.math.Vector2(pos)
self.rect = self.image.get_rect(center=pos)
def update(self, dt):
# Add the velocity to the position vector to move the sprite
self.pos += self.vel * dt
self.rect.center = self.pos # Update the rect pos.
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.kill()
Also, you should only use pygame.mixer.music.play() for playing background music (since you can only play one file at once with music.play()). For sound effects, better use the Sound class.
You can check if the bullet is in the screen and remove it if it isn't.
for bullet in bullets[:]:
if not surface.get_rect().collidepoint(bullet.pos):
#remove the bullet
Alternatively, you can remove it after a set amount of time. Under your __init__ method of the bullet class, you can add start = time.time() to record when it spawns.
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, direction, *sprite_groups):
super().__init__(*sprite_groups)
self.image = pygame.image.load(os.path.join('images','fireball.png'))
self.rect = self.image.get_rect()
self.sound = pygame.mixer.music.play()
self.pos = pygame.math.Vector2(pos)
self.vel = pygame.math.Vector2(750, 0)
self.direction = direction
self.start = time.time()
Then in your main loop make a variable now = time.time() to keep track of the current time. You can determine how long you bullet has been around by taking away spawn time, which is self.start from now
def kill(self, now, lifetime):
if now - self.start > lifetime:
#remove bullet
I have a problem, I made a Pong game but I have a problem printing the score to the pygame window.
I get the error 'TypeError: argument 1 must be pygame.Surface, not str
I blit the text but get an error
I know the code is a mess, I'll fix it later
import pygame
from pygame.locals import *
from random import randint
import random
import time
pygame.display.set_caption("PONG!")
myimage = pygame.image.load("lose.png")
middle = pygame.image.load("middle.png")
pygame.init()
screensize = (640,480)
screen = pygame.display.set_mode(screensize)
clock = pygame.time.Clock()
PlayerProp = 0
AiProp = 0
Score = str(PlayerProp)
AiScore = str(AiProp)
game = True
while game == True:
class pong_:
def __init__(self, screensize):
self.screensize = screensize
self.centerx = int(screensize[0]*0.5)
self.centery = int(screensize[1]*0.5)
self.radius = 8
self.rect = pygame.Rect(self.centerx-self.radius,
self.centery-self.radius,
self.radius*2, self.radius*2)
self.color = (255,255,255)
self.direction = [1,1]
self.speedx = 3
self.speedy = 4
#CODE TASK: change speed as game progresses to make it harder mwahaha
self.hit_edge_left = False
self.hit_edge_right = False
def update(self, player_paddle, ai_paddle):
self.centerx += self.direction[0]*self.speedx
self.centery += self.direction[1]*self.speedy
self.rect.center = (self.centerx, self.centery)
#Make new random for each -1
if self.rect.top <= 0:
self.direction[1] = 1
elif self.rect.bottom >= self.screensize[1]-1:
self.direction[1] = -1
if self.rect.right >= self.screensize[0]-1:
self.hit_edge_right = True
elif self.rect.left <= 0:
self.hit_edge_left = True
if self.rect.colliderect(player_paddle.rect):
self.direction[0] = random.randrange(-2, -1)
if self.rect.colliderect(ai_paddle.rect):
self.direction[0] = random.randrange(2, 3)
def render(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.radius, 0)
pygame.draw.circle(screen, (0,0,0), self.rect.center, self.radius, 1)
class AIPaddle(object):
def __init__(self, screensize):
self.screensize = screensize
self.centerx = 4
self.centery = int(screensize[1]*0.5)
self.height = 100
self.width = 10
self.rect = pygame.Rect(0, self.centery-int(self.height*0.5), self.width, self.height)
self.color = (255,255,255)
#CODE TASK: Adjust size of AI paddle as match progresses to make it more difficult
self.speed = random.randrange(3, 4)
def update(self, pong):
if pong.rect.top < self.rect.top:
self.centery -= self.speed
elif pong.rect.bottom > self.rect.bottom:
self.centery += self.speed
self.rect.center = (self.centerx, self.centery)
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect, 0)
pygame.draw.rect(screen, (0,0,0), self.rect, 1)
class PlayerPaddle(object):
def __init__(self, screensize):
self.screensize = screensize
self.centerx = screensize[0]-5
self.centery = int(screensize[1]*0.5)
self.height = 100
self.width = 10
self.rect = pygame.Rect(0, self.centery-int(self.height*0.5), self.width, self.height)
self.color = (255,255,255)
#CODE TASK: Adjust size of player paddle as match progresses to make it more difficult
self.speed = 3
self.direction = 0
def update(self):
self.centery += self.direction*self.speed
self.rect.center = (self.centerx, self.centery)
if self.rect.top < 0:
self.rect.top = 0
if self.rect.bottom > self.screensize[1]-1:
self.rect.bottom = self.screensize[1]-1
def render(self, screen):
pygame.draw.rect(screen, self.color, self.rect, 0)
pygame.draw.rect(screen, (0,0,0), self.rect, 1)
def main():
pygame.init()
pong = pong_(screensize)
ai_paddle = AIPaddle(screensize)
player_paddle = PlayerPaddle(screensize)
running = True
while running:
#fps limit
clock.tick(64)
#big boy event handling
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == KEYDOWN:
if event.key == K_UP:
player_paddle.direction = -1
elif event.key == K_DOWN:
player_paddle.direction = 1
if event.type == KEYUP:
if event.key == K_UP and player_paddle.direction == -1:
player_paddle.direction = 0
if event.key == K_DOWN and player_paddle.direction == 1:
player_paddle.direction = 0
#object updates
ai_paddle.update(pong)
player_paddle.update()
pong.update(player_paddle, ai_paddle)
screen.fill((0,0,0))
screen.blit(middle, (0,0))
screen.blit(AiScore, (0,0))
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
basicFont = pygame.font.SysFont(None, 48)
#CODE TASK: Make some text on the screen over everything
#code task 2: Make game restart?
if pong.hit_edge_left:
print('You Won!')
running = False
elif pong.hit_edge_right:
print('You lose')
running = False
#rendering phase
ai_paddle.render(screen)
player_paddle.render(screen)
pong.render(screen)
pygame.display.flip()
main()
Use basicFont.render(...) to create surface with text. Then blit that surface.
https://www.pygame.org/docs/ref/font.html#pygame.font.Font.render
btw: don't define classes and main() inside while game:. You don't have to. Put it outside while game:
Hello I am currently making a survival shooter but cannot find any way on how to do my collisions! I am trying to make the player collide with the floors but with no success. Here is an image of the map (I want to collide with moon blocks):
http://i.stack.imgur.com/R2apz.jpg
Here is an image of moon blocks with no Background:
http://i.stack.imgur.com/W9H2m.png
And finally here is my code:
import pygame
import random
gravity = .5
jump_time = 2000
x = 0
y = 0
# Player
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, gravity):
# Player dimensions and position
self.gravity = gravity
# Player image and animation
self.images = []
self.images.append(pygame.image.load('images/player.png'))
self.images.append(pygame.image.load('images/player2.png'))
#~ self.images.append(pygame.image.load('ball1.png'))
#~ self.images.append(pygame.image.load('ball2.png'))
self.maxImage = len(self.images)
self.currentImage = 0
#~ self.rect = pygame.Rect(x, y, 80, 80)
self.rect = self.images[0].get_rect()
self.rect.x = x
self.rect.y = y
self.timeTarget = 10
self.timeNum = 1
self.velX = 0
self.velY = 0
# Jump and gravity
self.vSpeed = 3
self.jumpForce = 15
self.maxVspeed = 3
self.isJumping = False
# Jump inputs
def handle_events(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
if not self.isJumping:
self.isJumping = True
elif event.key == pygame.K_a:
self.velX = -5
elif event.key == pygame.K_d:
self.velX = +5
elif event.type == pygame.KEYUP:
if event.key in (pygame.K_a, pygame.K_d):
self.velX = 0
# PLayer updates
def update(self, ground):
keys = pygame.key.get_pressed()
# Jumping
self.vSpeed += gravity
if self.vSpeed > self.maxVspeed:
self.vSpeed = self.maxVspeed
self.rect.y += self.vSpeed
if self.rect.y >= ground.y:
self.vSpeed = 0
self.rect.y = ground.y
self.isJumping = False
if keys[pygame.K_SPACE]:
if not self.isJumping:
self.isJumping = True
if self.isJumping:
if pygame.time.get_ticks() < jump_time:
self.isJumping == True
else:
self.isJumping = False
self.vSpeed -= self.jumpForce
#print "isJumping:", self.isJumping
# Animations
if self.timeNum == self.timeTarget:
self.currentImage += 1
if self.currentImage >= self.maxImage:
self.currentImage = 0
self.timeNum = 0
self.rect.centerx += self.velX
self.rect.centery += self.velY
# Screen wrap
if self.rect.right > 1280:
self.rect.left = 0
elif self.rect.left < 0:
self.rect.right = 1280
# Player rendering
def render(self, surface):
surface.blit(self.images[self.currentImage], self.rect)
#----------------------------------------------------------------------
class Zombie():
def __init__(self, x, y):
self.image = pygame.image.load('images/zombie.png')
#~ self.image = pygame.image.load('ball2.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.direction_left = True
def update(self, surface_rect):
if self.direction_left:
self.rect.x -= 1
if self.rect.left <= surface_rect.left:
self.direction_left = not self.direction_left
else:
self.rect.x += 1
if self.rect.right >= surface_rect.right:
self.direction_left = not self.direction_left
def render(self, surface):
surface.blit(self.image, self.rect)
#----------------------------------------------------------------------
class Background():
def __init__(self):
self.image = pygame.image.load('images/arena2.jpg')
#~ self.image = pygame.image.load('background.jpg')
self.rect = self.image.get_rect()
def render(self, surface):
surface.blit(self.image, self.rect)
#----------------------------------------------------------------------
class Game():
def __init__(self):
pygame.init()
# A few variables
self.gravity = .50
self.ground = pygame.Rect(0, 640, 1280, 80)
# Screen
size = (1280, 720)
self.screen = pygame.display.set_mode(size)
pygame.display.set_caption('Moon Survival!')
# Moon / Background
self.moon = Background()
# Zombies
self.zombies = []
for i in range(10):
self.zombies.append( Zombie(random.randint(0,1280), random.randint(0,720)) )
# Player
self.player = Player(25, 320, self.gravity)
# Font for text
self.font = pygame.font.SysFont(None, 72)
# Pause - center on screen
self.pause_text = self.font.render("PAUSE", -1, (255,0,0))
self.pause_rect = self.pause_text.get_rect(center = self.screen.get_rect().center)
def run(self):
clock = pygame.time.Clock()
# "state machine"
RUNNING = True
PAUSED = False
GAME_OVER = False
# Game loop
while RUNNING:
# (all) 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_p:
PAUSED = not PAUSED
# Player/Zomies events
if not PAUSED and not GAME_OVER:
self.player.handle_events(event)
# (all) Movements / Updates
if not PAUSED and not GAME_OVER:
self.player.update(self.ground)
for z in self.zombies:
z.update(self.screen.get_rect())
# (all) Display updating
self.moon.render(self.screen)
for z in self.zombies:
z.render(self.screen)
self.player.render(self.screen)
if PAUSED:
self.screen.blit(self.pause_text, self.pause_rect)
pygame.display.update()
# FTP
clock.tick(100)
# --- the end ---
pygame.quit()
#---------------------------------------------------------------------
Game().run()
Any help will be greatly appreciated, thank you.
I made a very similar game for PyWeek #17 called "Miner".
Here's a screenshot: http://media.pyweek.org/dl/17/powrtoch/miner_ss.png (you can see the similarity!)
You can find the relevant code on GitHub here: https://github.com/marcusmoller/pyweek17-miner/blob/master/miner/engine.py#L202-L220:
def checkCollision(self, sprite, xVel, yVel):
for x in range(len(level.levelStructure)):
for y in range(len(level.levelStructure[x])):
block = level.levelStructure[x][y]
if block is not None:
if pygame.sprite.collide_rect(sprite, block):
if xVel < 0:
sprite.rect.x = block.rect.x + block.rect.w
if xVel > 0:
sprite.rect.x = block.rect.x - sprite.rect.w
if yVel < 0:
sprite.rect.y = block.rect.y + block.rect.h
if yVel > 0 and not sprite.onGround:
sprite.onGround = True
sprite.rect.y = block.rect.y - sprite.rect.h
You could do something like this using collide_rect:
# See if the Sprite block collides with anything in the Ground block_list
for block in groundBlocks:
if(collide_rect(player, block)):
# do not fall
else:
# fall
where all Block you can walk on are in the groundBlocks list.