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()) )
Related
The following code attempts; unsuccessfully, to move a small graphic together with its mask. The graphic (car.png) moves as intended when the keyboard input indicates forward and backward. However, when the keyboard input indicates a rotation, the car graphic disappears and the place-holder triangle appears. The place-holder triangle subsequently behaves as the car graphic (with its mask) is supposed to behave.
import math
import pygame as pg
from pygame.math import Vector2
class Player(pg.sprite.Sprite):
def __init__(self, pos=(420, 420)):
super(Player, self).__init__()
self.image = pg.Surface((70, 50), pg.SRCALPHA)
pg.draw.polygon(self.image, (50, 120, 180), ((0, 0), (0, 50), (70, 25)))
self.original_image = self.image
self.rect = self.image.get_rect(center=pos)
# Instead we could load a picture of a car
self.image = pg.image.load("car.png").convert_alpha()
self.mask = pg.mask.from_surface(self.image)
self.position = Vector2(pos)
self.direction = Vector2(1, 0) # A unit vector pointing rightward.
self.speed = 2
self.angle_speed = 0
self.angle = 0
def update(self):
if self.angle_speed != 0:
# Rotate the direction vector and then the image.
self.direction.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
self.image = pg.transform.rotate(self.original_image, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
# Update the position vector and the rect.
self.position += self.direction * self.speed
self.rect.center = self.position
def main():
pg.init()
screen = pg.display.set_mode((1280, 720))
player = Player((420, 420))
playersprite = pg.sprite.RenderPlain((player))
clock = pg.time.Clock()
done = False
while not done:
clock.tick(60)
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
player.speed += 1
elif event.key == pg.K_DOWN:
player.speed -= 1
elif event.key == pg.K_LEFT:
player.angle_speed = -4
elif event.key == pg.K_RIGHT:
player.angle_speed = 4
elif event.type == pg.KEYUP:
if event.key == pg.K_LEFT:
player.angle_speed = 0
elif event.key == pg.K_RIGHT:
player.angle_speed = 0
playersprite.update()
screen.fill((30, 30, 30))
playersprite.draw(screen)
pg.display.flip()
if __name__ == '__main__':
main()
pg.quit()
See How do I rotate an image around its center using PyGame?. You don't need the triangle at all. original_image needs to be the car:
class Player(pg.sprite.Sprite):
def __init__(self, pos=(420, 420)):
super(Player, self).__init__()
self.original_image = pg.image.load("car.png").convert_alpha()
self.image = self.original_image
self.rect = self.image.get_rect(center=pos)
self.mask = pg.mask.from_surface(self.image)
self.position = Vector2(pos)
self.direction = Vector2(1, 0) # A unit vector pointing rightward.
self.speed = 2
self.angle_speed = 0
self.angle = 0
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)
...
I am attempting to create a bullet for my 2d shooter game.
I have created the bullet and it is displayed on the screen when the user presses the left mouse button, however I must now make it move.
Each bullet is added to the bullet group.
What would be the easiest way to update the position of the bullet?
This is the while loop. GameClass.Game.getevent is called, which states that if lmb is pressed, then the code below it will be run.
while play:
display.fill(WHITE)
GameClass.Game.getevent(player1)
platforms.draw(display)
sprites.draw(display)
bullets.draw(display)
pygame.display.update()
The following is part of a different file. The shoot function is part of the 'player' class, which has been cut off due to irrelevance.
def shoot(self):
bullet=Bullet(5,self.bullets,self.x,self.y)
self.bullets.add(bullet)
class Bullet(pygame.sprite.Sprite):
def __init__(self,speed,bullets,x,y):
super().__init__()
self.speed=speed
self.image=pygame.Surface((10,10))
self.rect=self.image.get_rect()
self.bullets=bullets
self.x=x
self.y=y
Bullet.movebullet(self)
def movebullet(self):
self.rect.center=((self.x+self.speed),self.y)
What would be the best way of updating the position of each individual bullet in the group?
Here's a complete solution. The player has a direction attribute which I change to 'left' or 'right' when the player presses one of the corresponding keys. When a bullet is created, I pass the player.direction to the bullet instance and then set its image and velocity depending on the direction.
In order to move the sprites, I add the velocity to the position (which are lists in this example) and finally I update the rect because it's needed for the blitting and collision detection. Actually, I'd use pygame.math.Vector2s for the pos and velocity, but I'm not sure if you are familiar with vectors.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
screen_rect = screen.get_rect()
PLAYER_IMG = pg.Surface((30, 50))
PLAYER_IMG.fill(pg.Color('dodgerblue1'))
BULLET_IMG = pg.Surface((16, 8))
pg.draw.polygon(BULLET_IMG, pg.Color('sienna1'), [(0, 0), (16, 4), (0, 8)])
BULLET_IMG_LEFT = pg.transform.flip(BULLET_IMG, True, False)
class Player(pg.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = PLAYER_IMG
self.rect = self.image.get_rect(center=pos)
self.velocity = [0, 0]
self.pos = [pos[0], pos[1]]
self.direction = 'right'
def update(self):
self.pos[0] += self.velocity[0]
self.pos[1] += self.velocity[1]
self.rect.center = self.pos
class Bullet(pg.sprite.Sprite):
def __init__(self, pos, direction):
super().__init__()
if direction == 'right':
self.image = BULLET_IMG
self.velocity = [9, 0]
else:
self.image = BULLET_IMG_LEFT
self.velocity = [-9, 0]
self.rect = self.image.get_rect(center=pos)
self.pos = [pos[0], pos[1]]
def update(self):
# Add the velocity to the pos to move the sprite.
self.pos[0] += self.velocity[0]
self.pos[1] += self.velocity[1]
self.rect.center = self.pos # Update the rect as well.
# Remove bullets if they're outside of the screen area.
if not screen_rect.contains(self.rect):
self.kill()
def main():
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
bullets = pg.sprite.Group()
player = Player((100, 300))
all_sprites.add(player)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
bullet = Bullet(player.rect.center, player.direction)
bullets.add(bullet)
all_sprites.add(bullet)
elif event.type == pg.KEYDOWN:
if event.key == pg.K_d:
player.velocity[0] = 5
player.direction = 'right'
elif event.key == pg.K_a:
player.velocity[0] = -5
player.direction = 'left'
elif event.type == pg.KEYUP:
if event.key == pg.K_d and player.velocity[0] > 0:
player.velocity[0] = 0
elif event.key == pg.K_a and player.velocity[0] < 0:
player.velocity[0] = 0
all_sprites.update()
screen.fill((30, 30, 30))
all_sprites.draw(screen)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
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()
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: