Sprite collision test in PyGame (tried: rect and group) - python

I know there are near 5 or 6 questions similar to this one I'm making, but none of the answers helped me (they probably would if I weren't a complete noob), so I'll try to show my specific case.
I'm trying to make a sprite collision test in PyGame (Python lib) but I can't get it to work, all I get is "False" when colliding, it simply won't work and I can't figure out why, been searching for 2 days straight.
In my game I have the object Player (which I called "dude") and the object Enemy (which I called... "enemy"). The __init__ on both is very similar, and both have rects generated.
This is the __init__ for the player:
class dude(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = self.load_image(SPRITE_PLAYER)
self.X = randint(34,ALTURA-10)
self.Y = randint(34,LARGURA-10)
self.rect = pygame.Rect(self.X,self.Y,25,25) #currently testing this
#self.rect = self.image.get_rect() already tried this
self.speed = SPEED_PLAYER
self.clock = pygame.time.Clock()
For the enemy, I have this:
class enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = self.load_image(SPRITE_ENEMY)
self.X = randint(34,ALTURA-10)
self.Y = randint(34,LARGURA-10)
self.rect = pygame.Rect(self.X,self.Y,25,25)
self.speed = SPEED_ENEMY
self.clock = pygame.time.Clock()
self.xis=1
self.yps=1
When testing collision, these are two methods that return something (wich is already awesome for me, cause there were other tries that only returned errors) but they always return 0's even when I collide then ingame.
The collision should occur when player (controlled by key input) touches the enemy sprite ou image (moved randomly though the stage).
These are the testing methods I have:
print pygame.sprite.collide_rect(player, enemy),pygame.sprite.collide_rect(player, enemy2)
print pygame.sprite.collide_rect(player, enemy),player.rect.colliderect(enemy2)
print player.rect.colliderect(enemy),player.rect.colliderect(enemy2)
(EDIT)
I've been oriented to show more code so the problem can be found, so there it is
(I'm also going to load the sprites globally, passing them as parameters to the objects, tanks for the tip).
Player Object:
# -*- coding: cp1252 -*-
import pygame, os
from pygame.locals import *
from Configs import *
from random import randint
class dude(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = self.load_image(SPRITE_PLAYER)
self.X = randint(34,ALTURA-10)
self.Y = randint(34,LARGURA-10)
self.rectBox()
self.speed = SPEED_PLAYER
self.clock = pygame.time.Clock()
def load_image(self,image_loaded):
try:
image = pygame.image.load(image_loaded)
except pygame.error, message:
print "Impossivel carregar imagem: " + image_loaded
raise SystemExit, message
return image.convert_alpha()
def rectBox(self):
self.rect = self.image.get_rect()
self.image_w, self.image_h = self.image.get_size()
self.rect.move(self.X, self.Y)
self.rect.topleft = (self.X, self.Y)
self.rect.bottomright = (self.X + self.image_w, self.Y + self.image_h)
def update(self,xis,yps,screen):
time_passed = self.clock.tick()
time_passed_seconds = time_passed/1000.0
distance_moved = time_passed_seconds * (self.speed +100) #Distancia = tempo * velocidade
self.X +=xis*distance_moved
self.Y +=yps*distance_moved
screen.blit(self.image, (self.X,self.Y))
if self.X>ALTURA-52:
self.X-=2
elif self.X<30:
self.X+=2
if self.Y>LARGURA-52:
self.Y-=2
elif self.Y<30:
self.Y+=2
Enemy Object:
# -*- coding: cp1252 -*-
import pygame, os
from pygame.locals import *
from Configs import *
from random import randint
class enemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = self.load_image(SPRITE_ENEMY)
self.X = randint(34,ALTURA-10)
self.Y = randint(34,LARGURA-10)
self.rectBox()
self.speed = SPEED_ENEMY
self.clock = pygame.time.Clock()
self.xis=1
self.yps=1
def load_image(self,image_loaded):
try:
image = pygame.image.load(image_loaded)
except pygame.error, message:
print "Impossivel carregar imagem: " + image_loaded
raise SystemExit, message
return image.convert_alpha()
def rectBox(self):
self.rect = self.image.get_rect()
self.image_w, self.image_h = self.image.get_size()
self.rect.move(self.X, self.Y)
self.rect.topleft = (self.X, self.Y)
self.rect.bottomright = (self.X + self.image_w, self.Y + self.image_h)
def update(self,screen):
time_passed = self.clock.tick()
time_passed_seconds = time_passed/1000.0
distance_moved = time_passed_seconds * (self.speed +100)
self.X +=self.xis*distance_moved
self.Y -=self.yps*distance_moved
screen.blit(self.image, (self.X,self.Y))
#Maquina de estados finitos para inteligência da movimentação
if self.X>ALTURA-50:
self.X-=2
self.xis=-1
elif self.X<30:
self.X+=2
self.xis=1
if self.Y>LARGURA-50:
self.Y-=2
self.yps=1
elif self.Y<30:
self.Y+=2
self.yps=-1
Main script:
# -*- coding: cp1252 -*-
import pygame, os
from pygame.locals import *
from Configs import *
from Enemy import enemy as e
from Player import dude
from sys import exit
pygame.init()
#Função para carregar imagens
def load_image(image_loaded):
try:
image = pygame.image.load(image_loaded)
except pygame.error, message:
print "Impossivel carregar imagem: " + image_loaded
raise SystemExit, message
return image.convert()
#Funções do Pause
def pause():
drawpause()
while 1:
p = pygame.event.wait()
if p.type in (pygame.QUIT, pygame.KEYDOWN):
return
def drawpause():
font = pygame.font.Font(None, 48)
text1 = font.render("PAUSE", 1, (10, 10, 10))
text1pos = text1.get_rect()
text1pos.centerx = screen.get_rect().centerx
text1pos.centery = screen.get_rect().centery
screen.blit(text1, text1pos)
font = pygame.font.Font(None, 36)
text2 = font.render("Pressione qualquer tecla para continuar", 1, (10, 10, 10))
text2pos = text2.get_rect()
text2pos.centerx = screen.get_rect().centerx
text2pos.centery = screen.get_rect().centery + 50
screen.blit(text2, text2pos)
pygame.display.flip()
#Inicializa tela principal
os.environ["SDL_VIDEO_CENTERED"] = "1"
screen = pygame.display.set_mode((ALTURA, LARGURA),0,32)
bg = load_image(BACKGROUND)
pygame.display.set_caption("Jogo teste para TCC - Top-Down")
#Inicialização do personagem
move_x, move_y = 0, 0
player = dude()
#Inicialização dos inimigos
enemy = e()
enemy2 = e()
#Atribuição de grupos de entidades
inimigos = pygame.sprite.Group()
inimigos.add(enemy)
inimigos.add(enemy2)
personagem = pygame.sprite.Group()
personagem.add(player)
#Objeto clock
clock = pygame.time.Clock()
#Loop principal sprite.get_height() sprite.get_width()
while True:
#Eventos do jogo
for event in pygame.event.get():
#SAIR DO JOGO
if event.type == QUIT:
pygame.quit()
exit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
exit()
elif event.key == K_p:
pause()
#TECLAS DE MOVIMENTAÇÃO PERSONAGEM
if event.type == KEYDOWN:
if event.key == K_LEFT:
move_x = -1
elif event.key == K_RIGHT:
move_x = +1
elif event.key == K_UP:
move_y = -1
elif event.key == K_DOWN:
move_y = +1
elif event.type == KEYUP:
if event.key == K_LEFT:
move_x = 0
elif event.key == K_RIGHT:
move_x = 0
elif event.key == K_UP:
move_y = 0
elif event.key == K_DOWN:
move_y = 0
#Posicionamento do background
screen.blit(bg, (0,0))
#Movimentação do personagem
player.update(move_x,move_y,screen)
#Movimentação inimigos
enemy.update(screen)
enemy2.update(screen)
#Teste colisão
#print pygame.sprite.collide_rect(player, enemy),pygame.sprite.collide_rect(player, enemy2)
#print pygame.sprite.collide_rect(player, enemy),player.rect.colliderect(enemy2)
#print player.rect.colliderect(enemy),player.rect.colliderect(enemy2)
#Refresh (por FPS)
pygame.display.update()
Thats it, thanks for the feedback on my post!

If you have a problem with collision detection, it often helps to print the rects of the sprites, e.g. print(player.rect). It'll show you that the rects are never updated and they stay at the same position, and because they're used for the collision detection your sprites won't collide.
To fix your problem you have to set the self.rect.center (or self.rect.topleft) of the sprites to the current self.X and self.Y coordinates in the update methods.
self.rect.center = self.X, self.Y
I recommend to take a look at the pygame.sprite.spritecollide method. Put your enemies into a separate sprite group and then get the collided sprites in this way:
collided_enemies = pg.sprite.spritecollide(player, enemies, False)
for collided_enemy in collided_enemies:
# Do something with the collided_enemy.
Here's a minimal, complete example:
import sys
import pygame as pg
class Player(pg.sprite.Sprite):
def __init__(self, pos, color):
super().__init__()
self.image = pg.Surface((50, 30))
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
player = Player((100, 100), pg.Color('dodgerblue2'))
enemy = Player((300, 100), pg.Color('sienna2'))
enemy2 = Player((200, 300), pg.Color('sienna2'))
all_sprites = pg.sprite.Group(player, enemy, enemy2)
enemies = pg.sprite.Group(enemy, enemy2)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEMOTION:
player.rect.center = event.pos
all_sprites.update()
collided_enemies = pg.sprite.spritecollide(player, enemies, False)
for collided_enemy in collided_enemies:
print(collided_enemy.rect)
screen.fill((50, 50, 50))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

Related

Pygame when moving into another object the player object should stop

I'm working on this prototype in pygame where you control a square and I'm currently working on collisions with other objects. When the player collides with the object, the player should not be able to move into the object.
import pygame,sys
pygame.init()
Clock = pygame.time.Clock()
FPS = 60
size = [1000,800]
bg = [0,0,0]
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Movement')
class Player:
def __init__(self,vel,x,y):
self.vel = vel
self.x = x
self.y = y
self.jump = False
def move(self):
k = pygame.key.get_pressed()
if k[pygame.K_a]:
self.x -= self.vel
if k[pygame.K_d]:
self.x += self.vel
if k[pygame.K_w]:
self.y -= self.vel
if k[pygame.K_s]:
self.y += self.vel
def draw(self):
pygame.draw.rect(screen,"red",(self.x,self.y,50,50))
def bordercollsion(self):
if self.x <=0:
self.x=0
if self.x >=950:
self.x=950
if self.y <=0:
self.y=0
if self.y >=750:
self.y=750
def do(self):
self.move()
self.draw()
self.bordercollsion()
player = Player(1,500,600)
while True:
screen.fill(bg)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))
if event.key == pygame.K_ESCAPE:
run = False
pygame.quit
sys.exit()
player.do()
en = pygame.draw.rect(screen,"blue",(10,20,50,50))
pygame.display.update()
See How do I detect collision in pygame?. Use pygame.Rect/colliderect to detect collisions of rectangles.
Store the position of the player
player_pos = player.x, player.y
Move the palyer:
player.do()
Set up a rectangle for the player and the obstacle:
player_rect = pygame.Rect(player.x, player.y, 50, 50)
obstacle_rect = pygame.Rect(10, 20, 50, 50)
Reset the position of the player, when the rectangles collide:
if player_rect.colliderect(obstacle_rect):
player.x, player.y = player_pos
Complete application loop
while True:
screen.fill(bg)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))
if event.key == pygame.K_ESCAPE:
run = False
pygame.quit
sys.exit()
player_pos = player.x, player.y
player.do()
player_rect = pygame.Rect(player.x, player.y, 50, 50)
obstacle_rect = pygame.Rect(10, 20, 50, 50)
if player_rect.colliderect(obstacle_rect):
player.x, player.y = player_pos
en = pygame.draw.rect(screen,"blue",(10,20,50,50))
pygame.display.update()
I see that someone has already posted a good answer to your question, but my answer has been shoe-horned into your original code. I've marked my changes with '###' to make them easier to find/identify.
There are a few ways to detect a collision in PyGame; this demonstrates PyGames colliderect() method. There is also a method using sprites and masking. The method you use depends on what you need.
import pygame,sys
pygame.init()
Clock = pygame.time.Clock()
FPS = 60
size = [1000,800]
bg = [0,0,0]
screen = pygame.display.set_mode(size)
pygame.display.set_caption('Movement')
class Buscemi(): ### new class for the blue square
'''Blue rectangle with which we must collide!''' ###
# def __init__(self):
# pass
# Normally you'd have an __init__() method, but this is one of
# those unusual cases were we don't actually need one. It still
# returns a Buscemi object
def draw(self): ###
'''Draws the rectangle and returns the Rect().''' ###
return pygame.draw.rect(screen,"blue",(10,20,50,50)) ###
class Player:
def __init__(self,vel,x,y,buscemi): ### added buscemi argument
self.vel = vel
self.buscemi=buscemi ### blue rectangle
self.x = x
self.y = y
self.xold = None ### for storing x before moving
self.yold = None ### for storing y before moving
self.jump = False
self.draw() ### because we need self.dude
def move(self):
if not self.dude.colliderect(self.buscemi.draw()): ### redraw blue and check collision
k = pygame.key.get_pressed()
self.xold = self.x ###
self.yold = self.y ###
if k[pygame.K_a]:
self.x -= self.vel
if k[pygame.K_d]:
self.x += self.vel
if k[pygame.K_w]:
self.y -= self.vel
if k[pygame.K_s]:
self.y += self.vel
else: ### restore x and y
self.x = self.xold ###
self.y = self.yold ###
def draw(self):
self.dude = pygame.draw.rect(screen,"red",(self.x,self.y,50,50)) ### named the rectangle
def bordercollsion(self):
if self.x <=0:
self.x=0
if self.x >=950:
self.x=950
if self.y <=0:
self.y=0
if self.y >=750:
self.y=750
def do(self):
self.move()
self.draw()
self.bordercollsion()
en = Buscemi() ### we need en as an object
player = Player(1,500,600,en) ### added one more argument
while True:
screen.fill(bg)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))
if event.key == pygame.K_ESCAPE:
run = False
pygame.quit
sys.exit()
# en = pygame.draw.rect(screen,"blue",(10,20,50,50)) ### made a class out of this
player.do()
pygame.display.update()

Python Pygame how to put the rectangle on the center of the screen

I am really newer to study the python and doing the exercrise
I want to create an rectange or any other graph such as i usepygame.Rect(x,y,w,h)
and set the screen_center by
self.screen_rect=setting.screen.get_rect()
self.screent_center=self.screen_rect.center
but the rectange's center is not in the screen's center
also i want use self.b=self.bullet.get_rect()but it show error
how can i fix it?
here's the code:
#! /usr/bin/python
import pygame as p
import sys
class Setting():
def __init__(self,width,height):
self.w=width
self.h=height
self.flag=p.RESIZABLE
self.color=(255,255,255)
self.speed=1
self.screen=p.display.set_mode((self.w,self.h),self.flag)
p.display.set_caption("Bullet")
self.bullet_s=1
self.bullet_w=100
self.bullet_h=300
self.bullet_c=(0,0,0)
class Bullet(p.sprite.Sprite):
def __init__(self,setting):
super().__init__()
self.screen_rect=setting.screen.get_rect()
self.screent_center=self.screen_rect.center
self.bullet=p.Rect((self.screen_center),(setting.bullet_w,setting.bullet_h)) **<-- not in the center**
self.b=self.bullet.get_rect() **<-- AttributeError: 'pygame.Rect' object has no attribute 'get_rect'**
self.color=setting.bullet_c
self.speed=setting.bullet_s
# self.centery=float(self.bullet.centery)
def bullet_move(self):
self.y -= self.speed
self.bullet.y=self.y
def draw_bullet(self,setting):
self.rect=p.draw.rect(setting.screen,self.color,self.bullet)
def game():
p.init()
setting=Setting(1200,800)
bullet=Bullet(setting)
while True:
for event in p.event.get():
if event.type == p.QUIT:
sys.exit()
setting.screen.fill((255,0,0))
bullet.draw_bullet(setting)
p.display.flip()
game()
This code center rectangle on screen using
self.screen_rect = setting.screen.get_rect()
self.rect.center = self.screen_rect.center
It also moves rectangle when you press UP or DOWN.
It uses KEYDOWN, KEYUP to change speed and it runs move() in every loop and this function uses speed to change position (without checking keys).
It also compare rect.top with screen.top and rect.bottom with screen.bottom to stop rectangle when it touchs border of the screen.
BTW: I also add spaces and empty lines in code to make it more readable.
See: PEP 8 -- Style Guide for Python Code
import pygame as p
class Setting():
def __init__(self, width, height):
self.w = width
self.h = height
self.flag = p.RESIZABLE
self.color = (255, 255, 255)
self.speed = 1
self.screen = p.display.set_mode((self.w, self.h), self.flag)
p.display.set_caption("Bullet")
self.bullet_s = 1
self.bullet_w = 100
self.bullet_h = 300
self.bullet_c = (0, 0, 0)
class Bullet(p.sprite.Sprite):
def __init__(self, setting):
super().__init__()
self.setting = setting
self.screen_rect = setting.screen.get_rect()
self.rect = p.Rect(0, 0, setting.bullet_w, setting.bullet_h)
self.rect.center = self.screen_rect.center
self.color = setting.bullet_c
self.speed = 0 #setting.bullet_s
def move(self):
self.rect.y -= self.speed
if self.rect.top < 0:
self.rect.top = 0
elif self.rect.bottom > self.screen_rect.bottom:
self.rect.bottom = self.screen_rect.bottom
def draw(self):
p.draw.rect(self.setting.screen, self.color, self.rect)
def handle_event(self, event):
if event.type == p.KEYDOWN:
if event.key == p.K_UP:
self.speed = self.setting.bullet_s
elif event.key == p.K_DOWN:
self.speed = -self.setting.bullet_s
elif event.type == p.KEYUP:
if event.key == p.K_UP:
self.speed = 0
elif event.key == p.K_DOWN:
self.speed = 0
def game():
p.init()
setting = Setting(1200,800)
bullet = Bullet(setting)
running = True
while running:
for event in p.event.get():
if event.type == p.QUIT:
running = False
bullet.handle_event(event)
bullet.move()
setting.screen.fill((255, 0, 0))
bullet.draw()
p.display.flip()
p.quit()
game()
This will calculate the position from the top-left corner of the sprite
screen = pygame.display.set_mode((1080, 720))
def getcenter(sprite):
screen_size = screen.get_size()
sprite_size = sprite.image.get_size()
center_x = screen_size[0]/2 + sprite_size[0]/2
center_y = screen_size[1]/2 + sprite_size[1]/2
return (center_x, center_y)
getcenter(spritehere)

How do I make an enemy follow the player in Pygame?

So, I'm a beginner to Python and Pygame and would like some help. I'm trying to make a game similar to Asteroids and I've made most of this program by looking around at other examples on the internet. However, I'm stuck on this problem: how do I get an enemy sprite to follow the player sprite? I've googled how to do so and tried implementing the same thing on my code but the sprite just stays in one place and doesn't follow the player. I've used the vector stuff to create the player sprite and I still barely understand how that works. I'm sort of on a tight schedule so I don't have time to thoroughly understand this stuff but I intend to later. Sorry if I haven't explained my code properly but I'm still trying to understand how Pygame works and most of this code is basically just copied.
import random, math, pygame, time, sys
from pygame.locals import *
from pygame.math import Vector2
######Setting up Variables
class settings:
fps = 30
windowwidth = 590
windowheight = 332
class ship:
HEALTH = 3
SPEED = 4
SIZE = 25
class colours:
black = (0, 0, 0)
white = (255, 255, 255)
###########################
######################################################################################
class Player(pygame.sprite.Sprite):
def __init__(self,image_file , pos=(0,0)):
super(Player, self).__init__()
self.image = pygame.image.load(image_file)
self.image = pygame.transform.scale(self.image, (25,25))
self.original_image = self.image
self.rect = self.image.get_rect(center=pos)
self.position = Vector2(pos)
self.direction = Vector2(1, 0)
self.speed = 0
self.angle_speed = 0
self.angle = 0
def update(self):
if self.angle_speed != 0:
self.direction.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pygame.transform.rotate(self.original_image, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.position += self.direction * self.speed
self.rect.center = self.position
class Enemy(pygame.sprite.Sprite):
def __init__(self, image_file, pos=(0,0)):
super(Enemy, self).__init__()
self.image = pygame.image.load(image_file)
self.image = pygame.transform.scale(self.image, (25, 25))
self.original_image = self.image
self.rect = self.image.get_rect(center=pos)
self.speed = 1
def move_towards_player(self, Player):
dx, dy = self.rect.x - Player.rect.x, self.rect.y - Player.rect.y
dist = math.hypot(dx, dy)
dx, dy = dx/dist, dy/dist
self.rect.x += dx * self.speed
self.rect.y += dy * self.speed
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def main():
pygame.init()
screen = pygame.display.set_mode((settings.windowwidth, settings.windowheight))
pygame.display.set_caption('Final Game')
background = Background('space.jpg',[0,0])
global player, enemy
player = Player('SpaceShip.png',[200,100])
playersprite = pygame.sprite.RenderPlain((player))
enemy = Enemy('Enemy Hexagon.png',[300,150])
enemysprite = pygame.sprite.RenderPlain((enemy))
fpsClock = pygame.time.Clock()
intro = True
while intro == True:
myFont = pygame.font.SysFont('freesansbold.ttf', 75)
otherFont = pygame.font.SysFont('freesansbold.ttf', 30)
SurfaceFont = myFont.render("Space Destroyer", True, (colours.white))
SurfaceFont2 = otherFont.render("Press Space to Start", True, (colours.white))
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_SPACE:
intro= False
screen.blit(SurfaceFont,(50,50))
screen.blit(SurfaceFont2,(125,125))
pygame.display.flip()
screen.blit(background.image, background.rect)
while intro == False:
fpsClock.tick(settings.fps)
for event in pygame.event.get():
enemy.rect.x
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_UP:
player.speed += 4
if event.key == K_LEFT:
player.angle_speed = -4
if event.key == K_RIGHT:
player.angle_speed = 4
if player.position.y < 0:
player.position = (player.position.x ,332)
elif player.position.y > settings.windowheight:
player.position = (player.position.x, 0)
elif player.position.x < 0:
player.position = (590, player.position.y)
elif player.position.x > settings.windowwidth:
player.position = (0, player.position.y)
elif event.type == KEYUP:
if event.key == K_LEFT:
player.angle_speed = 0
if event.key == K_RIGHT:
player.angle_speed = 0
if event.key == K_UP:
player.speed = 0
screen.fill(colours.white)
screen.blit(background.image, background.rect)
enemysprite.draw(screen)
enemysprite.update()
playersprite.draw(screen)
playersprite.update()
pygame.display.update()
playersprite.update()
pygame.display.flip()
if __name__ == '__main__': main()
You never called move_towards_player. Try adding this:
...
enemysprite.draw(screen)
enemysprite.update()
playersprite.draw(screen)
playersprite.update()
enemy.move_towards_player(player)
...

Screen Modes not Running in Proper Order

I am having trouble displaying my proper screens in the right order. I first want the Title screen, which is red. Then, if I click the Play Button, I want to switch to the Difficulty Screen, which has Easy and Hard Buttons. If I click the easy button, I want my game to go to main game, which I think I have done by passing in pong4.main() into my action parameter for the button.
My problem is that I'm running the main game first instead of the Title Screen. I never do hit the title screen. Then, once the game of pong is finished, I'm also getting a font
import pygame
import os
import pongtry4
WHITE = (255, 255, 255)
GREY = (200, 200, 200)
BLACK = (0, 0, 0)
screen = pygame.display.set_mode((800, 400))
pygame.init()
###############################
class Button():
def __init__(self, txt, location, action, bg=WHITE, fg=BLACK, size=(80, 30), font_name="Segoe Print", font_size=16):
self.color = bg # the static (normal) color
self.bg = bg # actual background color, can change on mouseover
self.fg = fg # text color
self.size = size
self.font = pygame.font.SysFont(font_name, font_size)
self.txt = txt
self.txt_surf = self.font.render(self.txt, 1, self.fg)
self.txt_rect = self.txt_surf.get_rect(center=[s//2 for s in self.size])
self.surface = pygame.surface.Surface(size)
self.rect = self.surface.get_rect(center=location)
self.call_back_ = action
def draw(self):
self.mouseover()
self.surface.fill(self.bg)
self.surface.blit(self.txt_surf, self.txt_rect)
screen.blit(self.surface, self.rect)
def mouseover(self):
self.bg = self.color
pos = pygame.mouse.get_pos()
if self.rect.collidepoint(pos):
self.bg = GREY # mouseover color
def call_back(self):
self.call_back_()
def my_great_function():
print("Great! " * 5)
def my_fantastic_function():
print("Fantastic! " * 4)
def mousebuttondown(button):
pos = pygame.mouse.get_pos()
if button.rect.collidepoint(pos):
button.call_back()
#########################
class SceneBase:
def __init__(self):
self.next = self
def ProcessInput(self, events, pressed_keys):
print("uh-oh, you didn't override this in the child class")
def Update(self):
print("uh-oh, you didn't override this in the child class")
def Render(self, screen):
print("uh-oh, you didn't override this in the child class")
def SwitchToScene(self, next_scene):
self.next = next_scene
def Terminate(self):
self.SwitchToScene(None)
def run_game(width, height, fps, starting_scene):
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
active_scene = starting_scene
while active_scene != None:
pressed_keys = pygame.key.get_pressed()
# Event filtering
filtered_events = []
for event in pygame.event.get():
quit_attempt = False
if event.type == pygame.QUIT:
quit_attempt = True
elif event.type == pygame.KEYDOWN:
alt_pressed = pressed_keys[pygame.K_LALT] or \
pressed_keys[pygame.K_RALT]
if event.key == pygame.K_ESCAPE:
quit_attempt = True
elif event.key == pygame.K_F4 and alt_pressed:
quit_attempt = True
if quit_attempt:
active_scene.Terminate()
else:
filtered_events.append(event)
active_scene.ProcessInput(filtered_events, pressed_keys)
active_scene.Update()
active_scene.Render(screen)
active_scene = active_scene.next
pygame.display.flip()
clock.tick(fps)
# The rest is code where you implement your game using the Scenes model
class TitleScene(SceneBase):
def __init__(self):
SceneBase.__init__(self)
#Create buttons and font instances here
self.play_button = Button("Play", (60, 30), self.SwitchToScene(DifficultyScene()))
self.my_font = pygame.font.SysFont("Moyko", 50)
def ProcessInput(self, events, pressed_keys):
for event in events:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
# Move to the next scene when the user pressed Enter
self.SwitchToScene(DifficultyScene())
if event.type == pygame.KEYUP:
print("You are hitting up!")
print(self.next)
if event.type == pygame.MOUSEBUTTONDOWN:
mousebuttondown(self.play_button)
def Update(self):
pass
def Render(self, screen):
# For the sake of brevity, the title scene is a blank red screen
screen.fill((255, 0, 0))
#Just Draw the Text Here
#myfont = pygame.font.SysFont(("Moyko"), 50)
textImage = self.my_font.render("Anime Pong", True, (0, 255, 0))
screen.blit(textImage, (100,100))
#Just draw the button here
self.play_button.draw()
def my_great_function():
print("Great! " * 5)
def my_fantastic_function():
print("Fantastic! " * 4)
class DifficultyScene(SceneBase):
def __init__(self):
SceneBase.__init__(self)
self.easy_button = Button("Easy", (60, 30), pongtry4.main())
self.hard_button = Button("Hard", (120, 60), my_fantastic_function)
def ProcessInput(self, events, pressed_keys):
for event in events:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
# Move to the next scene when the user pressed Enter
self.SwitchToScene(GameScene())
if event.type == pygame.KEYUP:
print("You are hitting up!")
print(self.next)
if event.type == pygame.MOUSEBUTTONDOWN:
mousebuttondown(self.easy_button)
mousebuttondown(self.hard_button)
def Update(self):
pass
def Render(self, screen):
# The game scene is just a blank blue screen
screen.fill((255, 0, 255))
self.easy_button.draw()
self.hard_button.draw()
class GameScene(SceneBase):
def __init__(self):
SceneBase.__init__(self)
def ProcessInput(self, events, pressed_keys):
for event in events:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
# Move to the next scene when the user pressed Enter
self.SwitchToScene(DifficultyScene())
if event.type == pygame.KEYUP:
print("You are hitting up!")
print(self.next)
if event.type == pygame.MOUSEBUTTONDOWN:
mousebuttondown(self.play_button)
def Update(self):
pass
def Render(self, screen):
# The game scene is just a blank blue screen
screen.fill((0, 0, 255))
run_game(800, 400, 60, TitleScene())
Below is also my pongTry4 Main Game Model
import pygame
from pygame.locals import *
import math
import random
########Colors######
RED = (255,0,0)
WHITE = (255, 255, 255)
GREEN = (0,255,0)
BLACK = (0, 0 , 0)
####################
game_mode = "Easy"
class Pong(object):
def __init__(self, screen_size):
#screenSize is a tuple (XLen, YLen)
self.screen_size = screen_size
self.XCenter = screen_size[0] // 2
self.YCenter = screen_size[1] // 2
self.rad = 10
#Create the surface for the Pong. #Drawn from top left corner
self.rect = pygame.Rect(self.XCenter-self.rad,
self.YCenter-self.rad,
self.rad*2,
self.rad*2)
self.color = GREEN
#direction and speed
self.direction = [-1, -1]
self.speedX = 4
self.speedY = 2
#Pong Hitting left edge results in a loss
#Pong Hitting right edge results in a win
self.hit_left_edge = False
self.hit_right_edge = False
def update(self, player_paddle, ai_paddle):
self.XCenter += self.speedX * self.direction[0]
self.YCenter += self.speedY * self.direction[1]
#update the center of the rectangle
self.rect.center = (self.XCenter, self.YCenter)
#Make sure the ball does not go past the bottom/top of screen
if self.rect.top <= 0:
self.direction[1] = 1
elif self.rect.bottom >= self.screen_size[1] - 1:
self.direction[1] = -1
#Tells us if the right or left edge has been hit
#This will tell us if someone has scored or not
if self.rect.left <= 0:
self.hit_left_edge = True
elif self.rect.right >= self.screen_size[0] - 1:
self.hit_right_edge = True
#Change the direction of pong based on where it hits player paddle
if self.rect.colliderect(player_paddle.rect):
relative_IntersectionY = player_paddle.YCenter - self.YCenter
normal_IntersectionY = relative_IntersectionY // (player_paddle.height //2)
bounce_angle = normal_IntersectionY * (math.pi * 5 // 12)
#constrains the speed of the ball
if self.speedX >= 10 or self.speedY >= 10:
self.speedX -= random.randint(4, 7)
self.speedY -= random.randint(4, 7)
self.speedX += random.randint(1, 3)
self.speedY += random.randint(1, 3)
print(self.speedX, self.speedY)
self.direction[0] = math.cos(bounce_angle)
self.direction[1] = -1*math.sin(bounce_angle)
#Change the direction of pong baesd on where it hits AI Paddle
if self.rect.colliderect(ai_paddle.rect):
relative_IntersectionY = ai_paddle.YCenter - self.YCenter
normal_IntersectionY = relative_IntersectionY // (ai_paddle.height // 2)
bounce_angle = normal_IntersectionY * (math.pi * 5 //12)
if self.speedX >= 10 or self.speedY >= 10:
self.speedX -= random.randint(4, 7)
self.speedY -= random.randint(4, 7)
self.speedX += random.randint(1,2)
self.speedY += random.randint(1,2)
print(self.speedX, self.speedY)
self.direction[0] = -1 * math.cos(bounce_angle)
self.direction[1] = -1 * math.sin(bounce_angle)
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.rect.center, self.rad, 0)
pygame.draw.circle(screen, BLACK, self.rect.center, self.rad, 1)
class Paddle(object):
def __init__(self, screen_size, XCenter, YCenter, height, width, color):
self.screen_size = screen_size
self.XCenter = XCenter
self.YCenter = YCenter
self.height = height
self.width = width
self.color = color
#Create the paddle surface on the sides of the screen
self.rect = pygame.Rect(0, self.YCenter - self.height//2, self.width, self.height)
def draw(self, screen):
pygame.draw.rect(screen, self.color, self.rect, 0)
pygame.draw.rect(screen, BLACK, self.rect, 1)
class PlayerPaddle(Paddle):
def __init__(self, screen_size, XCenter, YCenter, height, width, color):
super().__init__(screen_size, XCenter, YCenter, height, width, color)
self.speed = 5
self.direction = 0
def draw(self, screen):
super().draw(screen)
def update(self):
self.YCenter += self.direction * self.speed
self.rect.center = (self.XCenter, self.YCenter)
#ensures the paddle doesn't go off the screen
if self.rect.top <= 0:
self.rect.top = 0
if self.rect.bottom > self.screen_size[1]:
self.rect.bottom = self.screen_size[1]
class AIPaddle(Paddle):
def __init__(self, screen_size, XCenter, YCenter, height, width, color):
super().__init__(screen_size, XCenter, YCenter, height, width, color)
self.speed = 4
def draw(self, screen):
super().draw(screen)
def update(self, pong):
#If the pong is above the paddle, move the paddle towards it
#If the pong is below the paddle, move the paddle towards it
if pong.rect.top < self.rect.top:
self.YCenter -= self.speed
elif pong.rect.bottom > self.rect.bottom:
self.YCenter += self.speed
#update the AI Paddle's center coordinates
self.rect.center = (self.XCenter, self.YCenter)
################################################
def main():
pygame.init()
screen_size = (1200, 800)
screen = pygame.display.set_mode(screen_size)
clock = pygame.time.Clock()
pong = Pong(screen_size)
ai_paddle = AIPaddle(screen_size, screen_size[0] - 5, screen_size[1]//2, 100, 10, WHITE)
player_paddle = PlayerPaddle(screen_size, 5, screen_size[1]//2, 100, 10, WHITE)
running = True
while running:
#fps limiting/reporting phase
clock.tick(64)
#event handling phase
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
elif event.key == K_DOWN and player_paddle.direction == 1:
player_paddle.direction = 0
#object updating phase
ai_paddle.update(pong)
player_paddle.update()
pong.update(player_paddle, ai_paddle)
#CODE TASK: make some text on the screen over everything else saying you lost/won, and then exit on keypress
#CODE BONUS: allow restarting of the game (hint: you can recreate the Pong/Paddle objects the same way we made them initially)
if pong.hit_left_edge:
print("You Won")
running = False
elif pong.hit_right_edge:
print("you lose")
running = False
#rendering phase
screen.fill((100,100,100))
ai_paddle.draw(screen)
player_paddle.draw(screen)
pong.draw(screen)
pygame.display.flip()
pygame.quit()
is there a problem when I'm running main(), or running run_game(), or is it more of a problem of passing in main() into one of the parameters of my button if it is clicked? Thanks
Buttons in most frameworks expect callback - it means function name without () and parameters - so you need pong4.main instead of pong4.main()
If you use pong4.main() then you get something like
result = pong4.main()
Button(..., result)
so it executes pong4.main() as first.
EDIT: You have the same problem in
Button("Play", (60, 30), self.SwitchToScene(DifficultyScene()))
You have to create function and use it button
def function():
self.SwitchToScene(DifficultyScene())
Button("Play", (60, 30), function)
or you can use lambda to create noname function
Button("Play", (60, 30), lambda:self.SwitchToScene(DifficultyScene()) )

Determining a collision of an arching projectile with another sprite in Pygame

In pygame I have a projectile being shot from one character sprite to another which I would like to determine whether there is a collision or not. That is a collision between a shot projectile and another character I will call TRUMP. I have found an equation in a tutorial that is the best example of an arching trajectory that I can accomplish. If that equation could be helped it would be awesome.
def fireshell(self, elapsedTime):
fire = True
FPS = 60 # frames per second setting
fpsClock = pg.time.Clock()
print("fire", self.pos.x, self.pos.y)
fireX = int(self.pos.x)
fireY = int(self.pos.y)
print("fireX", fireX, "fireY", fireY, "elapsed time", elapsedTime)
power = elapsedTime*.0005
x = int(self.pos.x)
y = int(self.pos.y) - 100
while fire:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
quit()
pg.draw.circle(self.screen, RED, (x, y), 5)
x -= int(-(elapsedTime*6))
y += int((((x - fireX)*0.015)**2) - ((elapsedTime*2)/(12 - elapsedTime )))
print("X:", x,"Y:", y)
if y > HEIGHT or x > WIDTH:
fire = False
pg.display.update()
self.clock.tick(20)
My character sprite who I would like to check for collisions with the projectile is here:
class TRUMP(pg.sprite.Sprite):
def __init__(self, spritesheet, all_sprites, mudballGroup, jetsGroup):
pg.sprite.Sprite.__init__(self)
self.spritesheet = spritesheet
self.all_sprites = all_sprites
self.mudballGroup = mudballGroup
self.jetsGroup = jetsGroup
self.current_frame2 = 0
self.last_update2 = 0
self.load_images()
self.image = self.TRUMP_fingers_l
self.rect = self.image.get_rect()
self.rect.center = (WIDTH *3/4), (589)
self.rect.centerx = (WIDTH *3/4)
self.rect.centery = 589
self.rect.centerx = (WIDTH*5/6)
self.rect.centery = 589
self.pos = vec((WIDTH/2), (HEIGHT/2))
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.dir = 0
To get a ballistic trajectory, you can just add a GRAVITY constant to the y-value of the velocity vector each frame.
For the collision detection you can use pygame.sprite.spritecollide again (you already know how that works).
Here's a complete example:
import sys
import pygame as pg
GRAVITY = 3
class Player(pg.sprite.Sprite):
def __init__(self, pos, color):
super().__init__()
self.image = pg.Surface((50, 30))
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
self.pos = pg.math.Vector2(pos)
self.vel = pg.math.Vector2()
def update(self):
self.pos += self.vel
self.rect.center = self.pos
class Projectile(pg.sprite.Sprite):
def __init__(self, pos, color, target):
super().__init__()
self.image = pg.Surface((7, 5))
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
self.pos = pg.math.Vector2(pos)
direction = target - self.pos # Vector to the target.
# Normalize, then scale direction to adjust the speed.
self.vel = direction.normalize() * 33
def update(self):
self.pos += self.vel
self.vel.y += GRAVITY
self.rect.center = self.pos
if self.rect.y > 580:
self.kill()
class Game:
def __init__(self):
self.fps = 30
self.screen = pg.display.set_mode((800, 600))
pg.display.set_caption('Ballistic trajectory')
self.clock = pg.time.Clock()
self.bg_color = pg.Color(90, 120, 100)
self.green = pg.Color('aquamarine2')
self.blue = pg.Color(30, 90, 150)
self.font = pg.font.Font(None, 30)
self.player = Player((100, 300), self.green)
self.player2 = Player((400, 300), self.blue)
self.all_sprites = pg.sprite.Group(self.player, self.player2)
self.projectiles = pg.sprite.Group()
self.collisions = 0
self.done = False
def run(self):
while not self.done:
self.handle_events()
self.run_logic()
self.draw()
self.clock.tick(self.fps)
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
proj = Projectile(
self.player.rect.center, pg.Color('sienna2'), event.pos)
self.projectiles.add(proj)
self.all_sprites.add(proj)
if event.type == pg.KEYDOWN:
if event.key == pg.K_a:
self.player.vel.x = -3
if event.key == pg.K_d:
self.player.vel.x = 3
if event.key == pg.K_w:
self.player.vel.y = -3
if event.key == pg.K_s:
self.player.vel.y = 3
if event.type == pg.KEYUP:
if event.key == pg.K_a and self.player.vel.x == -3:
self.player.vel.x = 0
if event.key == pg.K_d and self.player.vel.x == 3:
self.player.vel.x = 0
if event.key == pg.K_w and self.player.vel.y == -3:
self.player.vel.y = 0
if event.key == pg.K_s and self.player.vel.y == 3:
self.player.vel.y = 0
def run_logic(self):
self.all_sprites.update()
hits = pg.sprite.spritecollide(self.player2, self.projectiles, True)
for collided_sprite in hits:
self.collisions += 1
def draw(self):
self.screen.fill(self.bg_color)
self.all_sprites.draw(self.screen)
txt = self.font.render('Collisions {}'.format(self.collisions), True, self.green)
self.screen.blit(txt, (20, 20))
pg.display.flip()
if __name__ == '__main__':
pg.init()
game = Game()
game.run()
pg.quit()
sys.exit()

Categories

Resources