i'm learning python by myself, and started with a simple game with pygame.
The game consists, so far, in a ball that's been chased by other balls, i have created a loop that avoid the chasing balls to overlap.
The way the loop works its by a nested loop that moves thru a list that includes all the chasing balls, then measures the distance between them, if the distance is less than the ball radius, it is moved away.
It seems to work most of the time, but sometimes a ball overlaps. I dont know why, if anyone can take a look at my code and give me a hint i would apreciate it, i think the error happens when there are more than 3 balls and at the moment when the player's ball (pelota) collides.
import pygame, random, math
pygame.init()
ancho , alto = 800 , 600
negro = (0,0,0)
blanco=(255,255,255)
FPS = 60
velocidad = 7
velocidadnalguis = velocidad - 2
contadordecolisiones = 1
perseguidores = []
pantalla=pygame.display.set_mode((ancho,alto))
pygame.display.set_caption("un jueguito")
reloj=pygame.time.Clock()
class Pelota(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("pelota.png").convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.rect.center = (ancho/2,alto/2)
self.speed = 0
def update(self):
self.speedx = 0
self.speedy = 0
tecla = pygame.key.get_pressed()
if tecla[pygame.K_LEFT]:
self.speedx = -velocidad
if tecla[pygame.K_RIGHT]:
self.speedx = velocidad
if tecla[pygame.K_UP]:
self.speedy = -velocidad
if tecla[pygame.K_DOWN]:
self.speedy = velocidad
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > ancho:
self.rect.right = ancho
if self.rect.bottom > alto:
self.rect.bottom = alto
if self.rect.left < 0:
self.rect.left = 0
if self.rect.top < 0:
self.rect.top = 0
pelota = Pelota()
class Perseguidor(pygame.sprite.Sprite):
def __init__(self,):
super().__init__()
self.image = pygame.image.load('nalguis.png').convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.velocidad = velocidadnalguis
self.radius = self.rect.width/2
self.center = self.rect.center
self.contadordecolisiones = 1
self.enerandom = random.randint(0,4)
if self.enerandom == 0:
self.rect.center = (random.randrange(-130,-30),random.randrange(-130,alto+130))
if self.enerandom == 1:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(-130,-30))
if self.enerandom == 2:
self.rect.center = (random.randrange(ancho+30,ancho+130),random.randrange(-130,alto+130))
if self.enerandom == 3:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(alto+30,alto+130))
def update(self):
self.pely = pelota.rect.y
self.pelx = pelota.rect.x
self.perx = self.rect.x
self.pery = self.rect.y
self.dist = math.sqrt(((self.pely-self.pery)**2) + ((self.pelx-self.perx)**2))/velocidadnalguis
self.angulo = math.atan2(self.pely-self.pery,self.pelx-self.perx)
self.speedx = 0
self.speedy = 0
self.speedx = math.cos(self.angulo)
self.speedy = math.sin(self.angulo)
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
all_sprites = pygame.sprite.Group()
perseguidor_grupo = pygame.sprite.Group()
all_sprites.add(pelota)
terminar=False
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if len(perseguidor_grupo)<1:
perseguidor=Perseguidor()
perseguidor_grupo.add(perseguidor)
all_sprites.add(perseguidor)
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
if len(perseguidor_grupo)<25:
perseguidor = Perseguidor()
perseguidor1 = Perseguidor()
all_sprites.add(perseguidor, perseguidor1)
perseguidor_grupo.add(perseguidor, perseguidor1)
perseguidores.append(perseguidor1)
perseguidores.append(perseguidor)
else :
perseguidor = Perseguidor()
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
dist=1
for i in range (len(perseguidores)):
for j in range(i+1,len(perseguidores)):
perseguidoresxy = [perseguidores[i].rect.centerx,perseguidores[i].rect.centery]
dist=math.hypot(perseguidores[i].rect.centerx - perseguidores[j].rect.centerx , perseguidores[i].rect.centery - perseguidores[j].rect.centery)
if dist <= perseguidor.radius*2:
if perseguidores[i].rect.centerx < perseguidores[j].rect.centerx:
perseguidores[i].rect.centerx -= 3
if perseguidores[i].rect.centery < perseguidores[j].rect.centery:
perseguidores[i].rect.centery -= 3
if perseguidores[i].rect.centerx > perseguidores[j].rect.centerx:
perseguidores[i].rect.centerx += 3
if perseguidores[i].rect.centery > perseguidores[j].rect.centery:
perseguidores[i].rect.centery -= 3
if len(perseguidores)> len(perseguidor_grupo):
del perseguidores [0]
all_sprites.update()
pantalla .fill(negro)
all_sprites.draw(pantalla)
pygame.display.flip()
pygame.quit()
quit()
[...] then measures the distance between them, if the distance is less than the ball radius, it is moved away. It seems to work most of the time, but sometimes a ball overlaps. [...]
Of course.You only consider 2 balls when moving a ball away.
This means that if you move a ball away from one ball, it can happen that you move it straight onto another ball.
You need to check that there is no other ball in the position where you are moving the ball.
Make sure the balls don't overlap as they spawn:
requested_balls = 1
while not terminar:
# [...]
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
requested_balls = min(25, requested_balls+1)
if len(perseguidor_grupo) < requested_balls:
perseguidor = Perseguidor()
if not pygame.sprite.spritecollide(perseguidor, perseguidor_grupo, True, pygame.sprite.collide_circle):
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
Only move a ball if the new position of the ball is not yet occupied by a ball:
class Perseguidor(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
old_rect = self.rect.copy()
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1: # at last 1, because the ball hits itself
if random.randrange(2) == 0:
self.rect.x = old_rect.x
else:
self.rect.y = old_rect.y
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1:
self.rect = old_rect
Complete example:
import pygame, random, math
pygame.init()
ancho , alto = 800 , 600
negro = (0,0,0)
blanco=(255,255,255)
FPS = 60
velocidad = 7
velocidadnalguis = velocidad - 2
contadordecolisiones = 1
perseguidores = []
pantalla=pygame.display.set_mode((ancho,alto))
pygame.display.set_caption("un jueguito")
reloj=pygame.time.Clock()
class Pelota(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("pelota.png").convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.rect.center = (ancho/2,alto/2)
self.speed = 0
def update(self):
self.speedx = 0
self.speedy = 0
tecla = pygame.key.get_pressed()
if tecla[pygame.K_LEFT]:
self.speedx = -velocidad
if tecla[pygame.K_RIGHT]:
self.speedx = velocidad
if tecla[pygame.K_UP]:
self.speedy = -velocidad
if tecla[pygame.K_DOWN]:
self.speedy = velocidad
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > ancho:
self.rect.right = ancho
if self.rect.bottom > alto:
self.rect.bottom = alto
if self.rect.left < 0:
self.rect.left = 0
if self.rect.top < 0:
self.rect.top = 0
pelota = Pelota()
class Perseguidor(pygame.sprite.Sprite):
def __init__(self,):
super().__init__()
self.image = pygame.image.load('nalguis.png').convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.velocidad = velocidadnalguis
self.radius = self.rect.width/2
self.center = self.rect.center
self.contadordecolisiones = 1
self.enerandom = random.randint(0,4)
if self.enerandom == 0:
self.rect.center = (random.randrange(-130,-30),random.randrange(-130,alto+130))
if self.enerandom == 1:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(-130,-30))
if self.enerandom == 2:
self.rect.center = (random.randrange(ancho+30,ancho+130),random.randrange(-130,alto+130))
if self.enerandom == 3:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(alto+30,alto+130))
def update(self):
self.pely = pelota.rect.y
self.pelx = pelota.rect.x
self.perx = self.rect.x
self.pery = self.rect.y
self.dist = math.sqrt(((self.pely-self.pery)**2) + ((self.pelx-self.perx)**2))/velocidadnalguis
self.angulo = math.atan2(self.pely-self.pery,self.pelx-self.perx)
self.speedx = 0
self.speedy = 0
self.speedx = math.cos(self.angulo)
self.speedy = math.sin(self.angulo)
old_rect = self.rect.copy()
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1: # at last 1, because the ball hits itself
if random.randrange(2) == 0:
self.rect.x = old_rect.x
else:
self.rect.y = old_rect.y
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1:
self.rect = old_rect
all_sprites = pygame.sprite.Group()
perseguidor_grupo = pygame.sprite.Group()
all_sprites.add(pelota)
terminar=False
requested_balls = 1
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if len(perseguidor_grupo)<1:
perseguidor=Perseguidor()
perseguidor_grupo.add(perseguidor)
all_sprites.add(perseguidor)
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
requested_balls = min(25, requested_balls+1)
if len(perseguidor_grupo) < requested_balls:
perseguidor = Perseguidor()
if not pygame.sprite.spritecollide(perseguidor, perseguidor_grupo, True, pygame.sprite.collide_circle):
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
dist=1
if len(perseguidores)> len(perseguidor_grupo):
del perseguidores [0]
all_sprites.update()
pantalla .fill(negro)
all_sprites.draw(pantalla)
pygame.display.flip()
pygame.quit()
quit()
Related
This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
How to draw a moving circle in Pygame with a small angle at a low speed and blinking?
(1 answer)
Closed 2 years ago.
I am making an Astroid clone where you only move with the keyboard. The player is able to rotate and needs to move forward to were it is facing.
For example:
When I rotate the player by 30 degrees and push W to go forward I want the player to go forward in the direction the player is facing. I can't just use self.rect.x += self.vel
I also tried to use this function:
def calculate_new_xy(speed, degrees):
add_x = (speed*(math.sin((degrees)*math.pi/180)))
add_y = (speed*(math.cos((degrees)*math.pi/180)))
return add_x, add_y
In theory, it should work because I am taking the sin and cos of this
Triangle.
But when I added that to my code the movement of the player wasn't as smooth as I hoped it to be. And sometimes the player moved indirectly forward.
I also noticed that the player moves faster when facing to the top left than to the bottom right.
Due to that the player doesn't move in a circle when turning always right. Instead the player moves elliptical to the top left.
Here is the code:
import pygame
import time
import random
import math
from os import path
#Colors:
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
#Settings:
pygame.init()
pygame.mixer.init() #for sound
WIDTH = 700
HEIGHT = 500
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Astroids")
clock = pygame.time.Clock()
FPS = 60
#Images/Sounds:
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, "img")
player = pygame.image.load(path.join(img_folder, 'Player.png'))
stone = pygame.image.load(path.join(img_folder, 'Stein.png'))
#Game Classes
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.original_image = player
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed_x = 0
self.speed_y = 0
self.vel = 3
self.degrees = 0
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
def movement(self):
'''Player Movement'''
keystate = pygame.key.get_pressed()
if keystate[pygame.K_q]:
self.degrees += 3
if keystate[pygame.K_e]:
self.degrees -= 3
if keystate[pygame.K_w]:
self.rect.centerx += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.rect.centery += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.rect.centery += -self.vel * math.sin(math.radians(self.degrees - 90))
self.rect.centerx += self.vel * math.cos(math.radians(self.degrees - 90))
def rotate(self):
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.degrees)
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#self.rect.y += self.speed_y
class Astroid(pygame.sprite.Sprite):
def __init__(self, life):
pygame.sprite.Sprite.__init__(self)
self.original_image = stone
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed = 2
self.life = life
self.speed_x = random.randrange(-3, 3)
self.speed_y = random.randrange(-3, 3)
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-3, 3)
self.last_rotation = pygame.time.get_ticks() # keeps track of time in milliseconds
self.rotation_deegre = random.randint(0, 360)
self.rotation_speed = 5
def rotate(self):
current_time = pygame.time.get_ticks()
if current_time - self.last_rotation > 50:
self.last_rotation = current_time
self.rotation_deegre += self.rotation_speed
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.rotation_deegre)
self.rect = self.image.get_rect()
self.rect.center = old_center
'''def new_astroids(self):
for i in range(2):
m = Astroid()
all_sprites.add(m)
all_astroids.add(m)''' #to do
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
def movement(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#Game Funktions
def calculate_new_xy(speed, degrees):
add_x = (speed*(math.sin((degrees)*math.pi/180)))
add_y = (speed*(math.cos((degrees)*math.pi/180)))
return add_x, add_y
#Game Sprites
all_astroids = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range (1):
m = Astroid(3)
all_astroids.add(m)
all_sprites.add(m)
#Main Game Loop
running = True
while running:
#Keep the game runnung at 60 FPS
clock.tick(FPS)
#Check for events:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#Update for Sprites
all_sprites.update()
#Check to see if any Astroids hit the player
astroid_colision = pygame.sprite.spritecollide(player, all_astroids, False)
if astroid_colision:
running = False
#Draw/Render
screen.fill(BLACK)
all_sprites.draw(screen)
#Update the display
pygame.display.update()
pygame.quit()
Any help is welcomed.
But when I added that to my code the movement of the player wasn't as smove as i hoped
The issue is caused by the fact, that the attributes of a pygame.Rect are integral values. Every time the position of the rectangle is changed, the fraction of the floating point motion vector is lost.
You have to compute the position with floating point accuracy and to synchronize the integral rectangle location by rounding the floating point position (round).
Add the attributes self.x and self.y:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.x, self.y = self.rect.center
Update self.x and self.y when the rectangle goes out of bounds:
class Player(pygame.sprite.Sprite):
# [...]
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
self.x = self.rect.centerx
if self.rect.bottom < 0:
self.rect.top = HEIGHT
self.y = self.rect.centery
if self.rect.right < 0:
self.rect.left = WIDTH
self.x = self.rect.centerx
if self.rect.top > HEIGHT:
self.rect.bottom = 0
self.y = self.rect.centery
Change the the attributes self.x and self.y when the player moves and update self.rect.center by self.x and self.y:
class Player(pygame.sprite.Sprite):
# [...]
def movement(self):
# [...]
if keystate[pygame.K_w]:
self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
self.x += self.vel * math.cos(math.radians(self.degrees - 90))
self.rect.center = round(self.x), round(self.y)
Class Player:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.original_image = player
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed_x = 0
self.speed_y = 0
self.vel = 3
self.degrees = 0
self.x, self.y = self.rect.center
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
self.x = self.rect.centerx
if self.rect.bottom < 0:
self.rect.top = HEIGHT
self.y = self.rect.centery
if self.rect.right < 0:
self.rect.left = WIDTH
self.x = self.rect.centerx
if self.rect.top > HEIGHT:
self.rect.bottom = 0
self.y = self.rect.centery
def movement(self):
'''Player Movement'''
keystate = pygame.key.get_pressed()
if keystate[pygame.K_q]:
self.degrees += 3
if keystate[pygame.K_e]:
self.degrees -= 3
if keystate[pygame.K_w]:
self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
self.x += self.vel * math.cos(math.radians(self.degrees - 90))
self.rect.center = round(self.x), round(self.y)
def rotate(self):
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.degrees)
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#self.rect.y += self.speed_y
i'm learning python by myself, and started with a simple game with pygame.
The game consists, so far, in a ball that's been chased by other balls, i have created a loop that avoid the chasing balls to overlap.
The way the loop works its by a nested loop that moves thru a list that includes all the chasing balls, then measures the distance between them, if the distance is less than the ball radius, it is moved away.
It seems to work most of the time, but sometimes a ball overlaps. I dont know why, if anyone can take a look at my code and give me a hint i would apreciate it, i think the error happens when there are more than 3 balls and at the moment when the player's ball (pelota) collides.
import pygame, random, math
pygame.init()
ancho , alto = 800 , 600
negro = (0,0,0)
blanco=(255,255,255)
FPS = 60
velocidad = 7
velocidadnalguis = velocidad - 2
contadordecolisiones = 1
perseguidores = []
pantalla=pygame.display.set_mode((ancho,alto))
pygame.display.set_caption("un jueguito")
reloj=pygame.time.Clock()
class Pelota(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("pelota.png").convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.rect.center = (ancho/2,alto/2)
self.speed = 0
def update(self):
self.speedx = 0
self.speedy = 0
tecla = pygame.key.get_pressed()
if tecla[pygame.K_LEFT]:
self.speedx = -velocidad
if tecla[pygame.K_RIGHT]:
self.speedx = velocidad
if tecla[pygame.K_UP]:
self.speedy = -velocidad
if tecla[pygame.K_DOWN]:
self.speedy = velocidad
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > ancho:
self.rect.right = ancho
if self.rect.bottom > alto:
self.rect.bottom = alto
if self.rect.left < 0:
self.rect.left = 0
if self.rect.top < 0:
self.rect.top = 0
pelota = Pelota()
class Perseguidor(pygame.sprite.Sprite):
def __init__(self,):
super().__init__()
self.image = pygame.image.load('nalguis.png').convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.velocidad = velocidadnalguis
self.radius = self.rect.width/2
self.center = self.rect.center
self.contadordecolisiones = 1
self.enerandom = random.randint(0,4)
if self.enerandom == 0:
self.rect.center = (random.randrange(-130,-30),random.randrange(-130,alto+130))
if self.enerandom == 1:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(-130,-30))
if self.enerandom == 2:
self.rect.center = (random.randrange(ancho+30,ancho+130),random.randrange(-130,alto+130))
if self.enerandom == 3:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(alto+30,alto+130))
def update(self):
self.pely = pelota.rect.y
self.pelx = pelota.rect.x
self.perx = self.rect.x
self.pery = self.rect.y
self.dist = math.sqrt(((self.pely-self.pery)**2) + ((self.pelx-self.perx)**2))/velocidadnalguis
self.angulo = math.atan2(self.pely-self.pery,self.pelx-self.perx)
self.speedx = 0
self.speedy = 0
self.speedx = math.cos(self.angulo)
self.speedy = math.sin(self.angulo)
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
all_sprites = pygame.sprite.Group()
perseguidor_grupo = pygame.sprite.Group()
all_sprites.add(pelota)
terminar=False
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if len(perseguidor_grupo)<1:
perseguidor=Perseguidor()
perseguidor_grupo.add(perseguidor)
all_sprites.add(perseguidor)
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
if len(perseguidor_grupo)<25:
perseguidor = Perseguidor()
perseguidor1 = Perseguidor()
all_sprites.add(perseguidor, perseguidor1)
perseguidor_grupo.add(perseguidor, perseguidor1)
perseguidores.append(perseguidor1)
perseguidores.append(perseguidor)
else :
perseguidor = Perseguidor()
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
dist=1
for i in range (len(perseguidores)):
for j in range(i+1,len(perseguidores)):
perseguidoresxy = [perseguidores[i].rect.centerx,perseguidores[i].rect.centery]
dist=math.hypot(perseguidores[i].rect.centerx - perseguidores[j].rect.centerx , perseguidores[i].rect.centery - perseguidores[j].rect.centery)
if dist <= perseguidor.radius*2:
if perseguidores[i].rect.centerx < perseguidores[j].rect.centerx:
perseguidores[i].rect.centerx -= 3
if perseguidores[i].rect.centery < perseguidores[j].rect.centery:
perseguidores[i].rect.centery -= 3
if perseguidores[i].rect.centerx > perseguidores[j].rect.centerx:
perseguidores[i].rect.centerx += 3
if perseguidores[i].rect.centery > perseguidores[j].rect.centery:
perseguidores[i].rect.centery -= 3
if len(perseguidores)> len(perseguidor_grupo):
del perseguidores [0]
all_sprites.update()
pantalla .fill(negro)
all_sprites.draw(pantalla)
pygame.display.flip()
pygame.quit()
quit()
[...] then measures the distance between them, if the distance is less than the ball radius, it is moved away. It seems to work most of the time, but sometimes a ball overlaps. [...]
Of course.You only consider 2 balls when moving a ball away.
This means that if you move a ball away from one ball, it can happen that you move it straight onto another ball.
You need to check that there is no other ball in the position where you are moving the ball.
Make sure the balls don't overlap as they spawn:
requested_balls = 1
while not terminar:
# [...]
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
requested_balls = min(25, requested_balls+1)
if len(perseguidor_grupo) < requested_balls:
perseguidor = Perseguidor()
if not pygame.sprite.spritecollide(perseguidor, perseguidor_grupo, True, pygame.sprite.collide_circle):
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
Only move a ball if the new position of the ball is not yet occupied by a ball:
class Perseguidor(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
old_rect = self.rect.copy()
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1: # at last 1, because the ball hits itself
if random.randrange(2) == 0:
self.rect.x = old_rect.x
else:
self.rect.y = old_rect.y
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1:
self.rect = old_rect
Complete example:
import pygame, random, math
pygame.init()
ancho , alto = 800 , 600
negro = (0,0,0)
blanco=(255,255,255)
FPS = 60
velocidad = 7
velocidadnalguis = velocidad - 2
contadordecolisiones = 1
perseguidores = []
pantalla=pygame.display.set_mode((ancho,alto))
pygame.display.set_caption("un jueguito")
reloj=pygame.time.Clock()
class Pelota(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("pelota.png").convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.rect.center = (ancho/2,alto/2)
self.speed = 0
def update(self):
self.speedx = 0
self.speedy = 0
tecla = pygame.key.get_pressed()
if tecla[pygame.K_LEFT]:
self.speedx = -velocidad
if tecla[pygame.K_RIGHT]:
self.speedx = velocidad
if tecla[pygame.K_UP]:
self.speedy = -velocidad
if tecla[pygame.K_DOWN]:
self.speedy = velocidad
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > ancho:
self.rect.right = ancho
if self.rect.bottom > alto:
self.rect.bottom = alto
if self.rect.left < 0:
self.rect.left = 0
if self.rect.top < 0:
self.rect.top = 0
pelota = Pelota()
class Perseguidor(pygame.sprite.Sprite):
def __init__(self,):
super().__init__()
self.image = pygame.image.load('nalguis.png').convert()
self.image = pygame.transform.scale(self.image, (30, 30))
self.image.set_colorkey(negro)
self.rect = self.image.get_rect()
self.velocidad = velocidadnalguis
self.radius = self.rect.width/2
self.center = self.rect.center
self.contadordecolisiones = 1
self.enerandom = random.randint(0,4)
if self.enerandom == 0:
self.rect.center = (random.randrange(-130,-30),random.randrange(-130,alto+130))
if self.enerandom == 1:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(-130,-30))
if self.enerandom == 2:
self.rect.center = (random.randrange(ancho+30,ancho+130),random.randrange(-130,alto+130))
if self.enerandom == 3:
self.rect.center = (random.randrange(-130,ancho+130),random.randrange(alto+30,alto+130))
def update(self):
self.pely = pelota.rect.y
self.pelx = pelota.rect.x
self.perx = self.rect.x
self.pery = self.rect.y
self.dist = math.sqrt(((self.pely-self.pery)**2) + ((self.pelx-self.perx)**2))/velocidadnalguis
self.angulo = math.atan2(self.pely-self.pery,self.pelx-self.perx)
self.speedx = 0
self.speedy = 0
self.speedx = math.cos(self.angulo)
self.speedy = math.sin(self.angulo)
old_rect = self.rect.copy()
if self.dist>=1:
self.rect.x += velocidadnalguis * self.speedx
self.rect.y += velocidadnalguis * self.speedy
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1: # at last 1, because the ball hits itself
if random.randrange(2) == 0:
self.rect.x = old_rect.x
else:
self.rect.y = old_rect.y
hit = pygame.sprite.spritecollide(self, perseguidor_grupo, False, pygame.sprite.collide_circle)
if len(hit) > 1:
self.rect = old_rect
all_sprites = pygame.sprite.Group()
perseguidor_grupo = pygame.sprite.Group()
all_sprites.add(pelota)
terminar=False
requested_balls = 1
while not terminar:
reloj.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminar=True
if len(perseguidor_grupo)<1:
perseguidor=Perseguidor()
perseguidor_grupo.add(perseguidor)
all_sprites.add(perseguidor)
hit = pygame.sprite.spritecollide(pelota, perseguidor_grupo, True, pygame.sprite.collide_circle)
if hit:
requested_balls = min(25, requested_balls+1)
if len(perseguidor_grupo) < requested_balls:
perseguidor = Perseguidor()
if not pygame.sprite.spritecollide(perseguidor, perseguidor_grupo, True, pygame.sprite.collide_circle):
all_sprites.add(perseguidor)
perseguidor_grupo.add(perseguidor)
perseguidores.append(perseguidor)
dist=1
if len(perseguidores)> len(perseguidor_grupo):
del perseguidores [0]
all_sprites.update()
pantalla .fill(negro)
all_sprites.draw(pantalla)
pygame.display.flip()
pygame.quit()
quit()
This question already has an answer here:
How do I make my player rotate towards mouse position?
(1 answer)
Closed 2 years ago.
I've been working on some game code for school and was working through some tutorials and figured out how to make the player sprite tracks the mouse but now I'm having trouble figuring out how to spawn enemies from all sides of the screen instead of just the top. (To clarify code is in two parts/files, also completely unrelated). I was also wondering, how I could maybe have the mobs/NPCs chase and rotate towards the player?
import random
import math
import pygame as py
from PlayerSprite import *
WIDTH = 800
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# initialize pygame and create window
py.init()
py.mixer.init()
screen = py.display.set_mode((WIDTH, HEIGHT))
py.display.set_caption("Dimensional Drifter")
clock = py.time.Clock()
all_sprites = py.sprite.Group()
NPCs = py.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range(8):
n = NPC()
all_sprites.add(n)
NPC.add(n)
# Game loop
running = True
while running:
# keep loop running at the right speed
clock.tick(FPS)
for event in py.event.get():
# check for closing window
if event.type == py.QUIT:
running = False
# Update
all_sprites.update()
#
mouse_x, mouse_y = py.mouse.get_pos()
player.rotate(mouse_x, mouse_y)
# render
screen.fill(BLACK)
all_sprites.draw(screen)
# flip the display
py.display.flip()
py.quit()
import pygame as py
import math
import random
WIDTH = 800
HEIGHT = 600
FPS = 60
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
class Player(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((50, 50), py.SRCALPHA)
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT / 2
self.Yspeed = 0
self.rotatableimage = self.image
def update(self):
self.Xspeed = 0
self.Yspeed = 0
# line below allow for key press to equate to move of sprite
keypreesed = py.key.get_pressed()
if keypreesed[py.K_a]:
self.Xspeed = - 11
if keypreesed[py.K_d]:
self.Xspeed = 11
if keypreesed[py.K_w]:
self.Yspeed = - 11
if keypreesed[py.K_s]:
self.Yspeed = 11
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
# line below allow the sprite to wrap around the screen
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.top = 0
if self.rect.bottom < 0:
self.rect.bottom = HEIGHT
def rotate(self, mouse_x, mouse_y):
rel_x = mouse_x - self.rect.x
rel_y = mouse_y - self.rect.y
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
self.image = py.transform.rotate(self.rotatableimage, int(angle))
self.rect = self.image.get_rect(center=(self.rect.centerx, self.rect.centery))
return
class NPC(py.sprite.Sprite):
def __init__(self):
py.sprite.Sprite.__init__(self)
self.image = py.Surface((30, 30))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Yspeed = random.randrange(1, 8)
def update(self):
self.rect.y += self.Yspeed
if self.rect.top > HEIGHT + 10:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Yspeed = random.randrange(1, 8)
Thanks in advance.
You have to add a random direction and a separate speed (self.Xspeed, self.Yspeed) for the x and y direction:
self.direction = random.randrange(4)
and to set self.rect.x, self.rect.y, self.Xspeed and self.Yspeed dependent on the random direction:
if self.direction == 0:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Xspeed = 0
self.Yspeed = random.randrange(1, 8)
elif self.direction == 1:
# [...]
elif self.direction == 2:
# [...]
elif self.direction == 3:
# [...]
Create a method spawn, which spawns a enemy with a random direction and a random speed and call it in the constructor of NPC:
class NPC(py.sprite.Sprite):
def __init__(self):
# [...]
self.spawn()
def spawn(self):
self.direction = random.randrange(4)
if self.direction == 0:
# [...]
In update the speed has to be and to the x and y position separately:
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
Furthermore, if the object is out of the window has to be evaluated dependent on the direction. If the object is out of the window, the call spawn() again, thus the object gets a new random direction, position and speed:
if self.direction == 0:
if self.rect.top > HEIGHT + 10:
self.spawn()
elif self.direction == 1:
if self.rect.bottom < -10:
self.spawn()
elif self.direction == 2:
if self.rect.left > WIDTH + 10:
self.spawn()
elif self.direction == 3:
if self.rect.right < -10:
self.spawn()
To rotate the image you have to create surface with per pixel alpha (convert_alpha()) and to keep the original surface:
self.image = py.Surface((30, 30)).convert_alpha()
self.image.fill(RED)
self.originalimage = self.image
Compute the angle from the enemy to the player and rotate the image by pygame.transform.rotate():
(See also How to rotate a triangle to a certain angle in pygame?)
dir_x, dir_y = player.rect.x - self.rect.x, player.rect.y - self.rect.y
self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y)
self.image = py.transform.rotate(self.originalimage, self.rot)
Class NPC:
class NPC(py.sprite.Sprite):
def __init__(self, player):
py.sprite.Sprite.__init__(self)
self.player = player
self.image = py.Surface((30, 30)).convert_alpha()
self.image.fill(RED)
self.originalimage = self.image
self.rect = self.image.get_rect()
self.spawn()
def spawn(self):
self.direction = random.randrange(4)
if self.direction == 0:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.Xspeed = 0
self.Yspeed = random.randrange(1, 8)
elif self.direction == 1:
self.rect.x = random.randrange(WIDTH - self.rect.width)
self.rect.y = random.randrange(HEIGHT, HEIGHT+60)
self.Xspeed = 0
self.Yspeed = -random.randrange(1, 8)
elif self.direction == 2:
self.rect.x = random.randrange(-100, -40)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = random.randrange(1, 8)
self.Yspeed = 0
elif self.direction == 3:
self.rect.x = random.randrange(WIDTH, WIDTH+60)
self.rect.y = random.randrange(HEIGHT - self.rect.height)
self.Xspeed = -random.randrange(1, 8)
self.Yspeed = 0
def update(self):
self.rect.x += self.Xspeed
self.rect.y += self.Yspeed
dir_x, dir_y = self.player.rect.x - self.rect.x, self.player.rect.y - self.rect.y
self.rot = (180 / math.pi) * math.atan2(-dir_x, -dir_y)
self.image = py.transform.rotate(self.originalimage, self.rot)
if self.direction == 0:
if self.rect.top > HEIGHT + 10:
self.spawn()
elif self.direction == 1:
if self.rect.bottom < -10:
self.spawn()
elif self.direction == 2:
if self.rect.left > WIDTH + 10:
self.spawn()
elif self.direction == 3:
if self.rect.right < -10:
self.spawn()
player = Player()
all_sprites.add(player)
for i in range(8):
n = NPC(player)
all_sprites.add(n)
NPC.add(n)
When i shot i can control bullet in a fly. How can i avoid it. I want to bullet moved and i can't control it. Maybe i should to fix my code. But i can't solved it. For example bullet fly to right but when i pressed keys K_LEFT the bullet changes route.
import pygame
pygame.init()
#Frames
FPS = 60
#Colors
WHITE = (255, 255, 255)
GREY = (105, 105, 105)
BLACK = (0, 0, 0)
GREEN = (0, 225, 0)
#Screen size
W = 1280
H = 680
#Player_img
p_up = pygame.image.load('Assets/tanks/green/up1.png')
p_d = pygame.image.load('Assets/tanks/green/down1.png')
p_r = pygame.image.load('Assets/tanks/green/right1.png')
p_l = pygame.image.load('Assets/tanks/green/left1.png')
#Scale_img
player_up = pygame.transform.scale(p_up, (40, 40))
player_d = pygame.transform.scale(p_d, (40, 40))
player_r = pygame.transform.scale(p_r, (40, 40))
player_l = pygame.transform.scale(p_l, (40, 40))
#Screen
sf = pygame.display.set_mode((W, H))
pygame.display.set_caption('TanksX')
sf.fill(WHITE)
#Clock
clock = pygame.time.Clock()
#For Game
Run = True
last_move = 0
#Sprites init
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = player_up
self.rect = self.image.get_rect()
self.rect.centerx = W // 2
self.rect.y = H // 2
self.speedy = 2
self.speedx = 2
self.shoot_delay = 1000
self.last_shot = pygame.time.get_ticks()
def update(self):
global last_move
if keys[pygame.K_UP]:
self.image = pygame.transform.rotate(player_up, 360)
self.rect.y -= self.speedy
last_move = 1
elif keys[pygame.K_DOWN]:
self.image = pygame.transform.rotate(player_up, 180)
self.rect.y += self.speedy
last_move = 2
elif keys[pygame.K_RIGHT]:
self.image = pygame.transform.rotate(player_up, -90)
self.rect.x += self.speedx
last_move = 3
elif keys[pygame.K_LEFT]:
self.image = pygame.transform.rotate(player_up, 90)
self.rect.x -= self.speedx
last_move = 4
if keys[pygame.K_SPACE]:
self.shoot()
def shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
self.last_shot = now
bullet1 = Bullet(self.rect.centerx, self.rect.centery)
all_sprites.add(bullet1)
bullets.add(bullet1)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
a = 10
b = 10
self.image = pygame.Surface((a, b))
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.speedy = 10
self.speedx = 10
def update(self):
global last_move
if self.rect.x > W or self.rect.x < 0 or self.rect.y > H or self.rect.y < 0:
self.kill()
if last_move == 1:
self.rect.y -= self.speedy
if last_move == 2:
self.rect.y += self.speedy
if last_move == 3:
self.rect.x += self.speedx
if last_move == 4:
self.rect.x -= self.speedx
#Groups
all_sprites = pygame.sprite.Group()
players = pygame.sprite.Group()
bullets = pygame.sprite.Group()
#Sprites
player1 = Player()
#Add Sprites to Groups
all_sprites.add(player1)
players.add(player1)
#Game
while Run:
keys = pygame.key.get_pressed()
for i in pygame.event.get():
if i.type == pygame.QUIT:
exit()
all_sprites.draw(sf)
all_sprites.update()
pygame.display.flip()
sf.fill(WHITE)
clock.tick(FPS)
Please help me i will be very gratefull.
I'm new to a python and pygame. Thankyou
The movement of the bullet is not affected by the current movement of the player. Add the arguments speedx and speedy to the class Bullet:
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, speedx, speedy):
pygame.sprite.Sprite.__init__(self)
a = 10
b = 10
self.image = pygame.Surface((a, b))
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
self.speedx = speedx
self.speedy = speedy
def update(self):
if self.rect.x > W or self.rect.x < 0 or self.rect.y > H or self.rect.y < 0:
self.kill()
self.rect.x += self.speedx
self.rect.y += self.speedy
The movement of bullet depends on the direction of the player when the bullet is shot. Determine the movement of the bullet (speedx, speedy) in Player.shoot, dependent on last_move and pass the movement to the constructor of Bullet:
class Player(pygame.sprite.Sprite):
# [...]
def shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
speedx, speedy = 10, 0
if last_move == 1:
speedx, speedy = 0, -10
if last_move == 2:
speedx, speedy = 0, 10
if last_move == 3:
speedx, speedy = 10, 0
if last_move == 4:
speedx, speedy = -10, 0
self.last_shot = now
bullet1 = Bullet(self.rect.centerx, self.rect.centery, speedx, speedy)
all_sprites.add(bullet1)
bullets.add(bullet1)
I'm creating a tennis game with Pygame and Python (obviously). I am trying to implement a scoreboard so that when the ball stops, assuming the other player cannot reach it, the score goes up.
I've tried using an if statement checking if the ball's speed has stopped, but that just pretty much created a running clock. I also don't need the score to go up while the ball is stopped in its starting position (haven't added serving mechanics yet).
import pygame
import time
pygame.init()
# Define some colors
BLACK = (0, 0, 0)
OUT = (193, 58, 34)
COURT = (69, 150, 81)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
SKIN = (232, 214, 162)
ballspeed = 2
# Create the screen
windowsize = (700, 500)
screen = pygame.display.set_mode(windowsize)
pygame.display.set_caption('Tennis')
# Player Sprites
class Robert(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Robert_tennis.png")
self.rect = self.image.get_rect()
self.rect.center = (360, 480)
self.speedx = 0
self.speedy = 0
def update(self):
self.speedx = 0
self.speedy = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -4
if keystate[pygame.K_RIGHT]:
self.speedx = 4
self.rect.x += self.speedx
if self.rect.right > 700:
self.rect.right = 700
if self.rect.right < 0:
self.rect.left = 0
if keystate[pygame.K_UP]:
self.speedy = -5
if keystate[pygame.K_DOWN]:
self.speedy = 3
self.rect.y += self.speedy
if self.rect.y < 235:
self.rect.y = 235
if self.rect.y < 0:
self.rect.y = 0
class Camden(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("camden_tennis_front.png")
self.rect = self.image.get_rect()
self.rect.center = (360, 100)
self.speedx = 0
self.speedy = 0
def update(self):
self.speedx = 0
self.speedy = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_a]:
self.speedx = -6
if keystate[pygame.K_d]:
self.speedx = 6
self.rect.x += self.speedx
if self.rect.right > 700:
self.rect.right = 700
if self.rect.right < 0:
self.rect.left = 0
if keystate[pygame.K_w]:
self.speedy = -7
if keystate[pygame.K_s]:
self.speedy = 5
self.rect.y += self.speedy
if self.rect.y > 180:
self.rect.y = 180
if self.rect.y < 0:
self.rect.y = 0
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("tennisball.png")
self.rect = self.image.get_rect()
self.rect.center = (360, 325)
self.speedx = 0
self.speedy = 0
def update(self):
#Robert's forehand
if tennisball.rect.colliderect(robert) and tennisball.rect.x > robert.rect.x + 10:
robert.image = pygame.image.load("Robert_tennis2 (1).png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
robert.rect.y -5
self.speedy = -8
self.speedx = 4
#Robert's forehand volley
if tennisball.rect.colliderect(robert) and tennisball.rect.x > robert.rect.x + 10 and robert.rect.y < 300:
robert.image = pygame.image.load("Robert_tennis.png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
robert.rect.y -5
self.speedy = -3
self.speedx = -3
#Robert's backhand
if tennisball.rect.colliderect(robert) and tennisball.rect.x < robert.rect.x - 10:
robert.image = pygame.image.load("Robert_tennis2_backhand.png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
robert.rect.y -5
self.speedy = -7
self.speedx = -3
#Robert's backhand volley
if tennisball.rect.colliderect(robert) and tennisball.rect.x < robert.rect.x - 10 and robert.rect.y < 300:
robert.image = pygame.image.load("Robert_tennis_backhand_volley.png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
robert.rect.y -5
self.speedy = -3
self.speedx = -3
#Camden's forehand
if tennisball.rect.colliderect(camden) and tennisball.rect.x < camden.rect.x -10:
camden.image = pygame.image.load("camden_front_forehand.png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
camden.rect.y -5
self.speedy = 9
self.speedx = 2
self.speedy = self.speedy * .98
self.speedx = self.speedx * .96
self.rect = self.rect.move(self.speedx, self.speedy)
#Add people
all_sprites = pygame.sprite.Group()
robert = Robert()
camden = Camden()
tennisball = Ball()
all_sprites.add(robert)
all_sprites.add(tennisball)
all_sprites.add(camden)
carryOn = True
clock = pygame.time.Clock()
global score
score = 0
screen.fill(OUT)
while carryOn:
font = pygame.font.Font('freesansbold.ttf', 32)
scorebox = font.render(str(score), True, WHITE, BLACK)
scoreRect = scorebox.get_rect()
scoreRect.center = (625, 50)
screen.blit(scorebox, scoreRect)
if tennisball.speedx == 0 and tennisball.speedy == 0 and tennisball.rect.x != 360 and tennisball.rect.y != 325:
score = score + 1
scorebox = font.render(str(score), True, WHITE, BLACK)
scoreRect.center = (625, 50)
screen.blit(scorebox, scoreRect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
carryOn = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
carryOn = False
all_sprites.update()
# Fill the screen with a sky color
# Draw the court
pygame.draw.rect(screen, COURT, [175, 0, 350, 500])
pygame.draw.line(screen, WHITE, (170,500), (170,0), 10)
pygame.draw.line(screen, WHITE, (520,500), (520,0), 10)
pygame.draw.line(screen, WHITE, (170,130), (520,130), 10)
pygame.draw.line(screen, WHITE, (170,370), (520,370), 10)
#center line
pygame.draw.line(screen, WHITE, (345,130), (345,370), 10)
pygame.draw.line(screen, BLACK, (170,250), (520,250), 10)
# Update
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
I expect the score to update when the ball stops. I can worry about resetting the ball when I implement serving mechanics later.
Since the the position attributes of the tennis ball are floating point values, the never get exactly 0.0, but the get a value very close to 0.0.
You have to do a Epsilon Comparison:
Test if tennisball.speedx and tennisball.speedy are close to 0.0by checking if the absolut value (abs()) is less than a certain threshold (epsilon):
abs(tennisball.speedx) < espi and abs(tennisball.speedy) < espi:
Change the term tennisball.speedx == 0 and tennisball.speedy == 0 in yuor code:
espi = 0.1
if abs(tennisball.speedx) < espi and abs(tennisball.speedy) < espi:
score = score + 1
Note, you've to "choose" the value of epsi for your needs.
[...] but that just pretty much created a running clock.
Add a state allow_score which indicates if it is allowed to score and initialize it by False:
allow_score = False
Immediately when the ball starts to moving, then state has to be signaled (allow_score = True). From now on it is possible to score.
When the ball has stopped and allow_score == True, then it is possible to score, but it is only possible to score once, so the state has to be be reset (allow_score = False).
This cause that the score is not continuously increases, because to score again, the ball has to move first:
while carryOn:
# [...]
moving = abs(tennisball.speedx) > espi or abs(tennisball.speedy) > espi
if moving:
allow_score = True
elif allow_score:
allow_score = False
score = score + 1
Note, since allow_score is initialized False, the score is not incremented at the start.