Pygame tutorial - tank with collision detection - python

I'm new to Pygame and trying to learn about sprites and collision detection. I found some code and tried to add some functionality to it.
The basic idea is that I have a polygon which can be rotated in place. When the user presses SPACEBAR, the polygon fires a projectile which can bounce around the frame. If the projectile hits the polygon again, I would like to quite the program
First of all, here is the code
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
self.fire_from = (36, 20)
pygame.draw.polygon(self.image, pygame.Color('dodgerblue'), ((0, 0), (32, 16), (0, 32)))
self.org_image = self.image.copy()
self.angle = 0
self.direction = pygame.Vector2(1, 0)
self.rect = self.image.get_rect(center=(200, 200))
self.pos = pygame.Vector2(self.rect.center)
def update(self, events, dt):
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
bullet = Projectile(self.fire_from, self.direction.normalize())
self.groups()[0].add(bullet)
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]:
self.angle += 3
if pressed[pygame.K_RIGHT]:
self.angle -= 3
self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
self.image = pygame.transform.rotate(self.org_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
class Projectile(pygame.sprite.Sprite):
def __init__(self, pos, direction):
super().__init__()
self.image = pygame.Surface((8, 8))
self.image.fill((0, 0, 0))
self.image.set_colorkey((0, 0, 0))
pygame.draw.circle(self.image, pygame.Color('orange'), (4, 4), 4)
self.rect = self.image.get_rect(center=pos)
self.direction = direction
self.pos = pygame.Vector2(self.rect.center)
self.max_bounce = 10
def update(self, events, dt):
#print(self.pos)
# Bounding box of the screen
screen_r = pygame.display.get_surface().get_rect()
# where we would move next
next_pos = self.pos + self.direction * dt
# we hit a wall
if not screen_r.contains(self.rect):
# after 10 hits, destroy self
self.max_bounce -= 1
if self.max_bounce == 0:
return self.kill()
# horizontal reflection
if next_pos.x > screen_r.right or next_pos.x < screen_r.left:
self.direction.x *= -1
# vertical reflection
if next_pos.y > screen_r.bottom or next_pos.y < screen_r.top:
self.direction.y *= -1
# move after applying reflection
next_pos = self.pos + self.direction * dt
# set the new position
self.pos = next_pos
self.rect.center = self.pos
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
tank = Player()
sprites = pygame.sprite.Group()
sprites.add(tank)
#print(tank.groups()[0])
clock = pygame.time.Clock()
dt = 0
running = True
while running:
#print(tank.groups()[0])
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
sprites.update(events, dt)
screen.fill((30, 30, 30))
sprites.draw(screen)
pygame.display.update()
## ALWAYS DETECTS A COLLISION, WHY?
if len(tank.groups()[0])>1:
if pygame.sprite.spritecollideany(tank, tank.groups()[0]):
running = False
dt = clock.tick(60)
if __name__ == '__main__':
main()
I gather here that the Player class (i.e. my tank) itself has a sprite groups, to which it adds each projectile as its fired. Furthermore, the projectile originates from within the rectangle for the polygon (hence it always initially collides)
I would like to only add the projectile to the sprite group if it has made at least 1 bounce, is that possible? Or... is there a better way to do this?
Any help or pointers please?
Thanks
Null

The tank collides with itself, because it is a member of tank.groups()[0]:
if pygame.sprite.spritecollideany(tank, tank.groups()[0]):
Add a Group that contains just the bullets:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.bullets = pygame.sprite.Group()
def update(self, events, dt):
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
bullet = Projectile(self.fire_from, self.direction.normalize())
self.groups()[0].add(bullet)
self.bullets.add(bullet)
# [...]
Use this Group for the collision test:
def main():
# [...]
while running:
# [...]
pygame.display.update()
if pygame.sprite.spritecollideany(tank, tank.bullets):
running = False
dt = clock.tick(60)

Related

Bullets won't move accross the screen (Alien Invaders Python Crash Course project 1) [duplicate]

Heres my code
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
class player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class bullet:
def __init__(self):
self.radius = 10
self.speed = 20
def shoot(self):
x = p.x
y = p.y
self.shooting = True
while self.shooting:
d.fill((98, 98, 98))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
y -= self.speed
pygame.draw.circle(d, (255, 0, 0), (x, y), self.radius)
pygame.time.Clock().tick(100)
win.update()
if y <= 0:
self.shooting = False
b = bullet()
p = player(600, 500, 50, 30)
while True:
d.fill((98, 98, 98))
p.draw()
for event in pygame.event.get():
pass
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
b.shoot()
if event.key == pygame.K_LEFT:
p.move_left()
if event.key == pygame.K_RIGHT:
p.move_right()
win.update()
This is what i could come up with after a few Trial and errors but it is really uneffective. Firstly the player disappers when i press space bar. I guess this is obvious as i have a different loops for shooting and player but i dont know how to get around it and implement both shooting and player in the same loop.
The second probllem i am having is breaking the while self.shooting: loop. I tried breaking it when y reaches a certain point by doing this
if y <= 0:
self.shooting = False
but this dosent break. Instead, it restarts the loop all over again.
Another weird problem i am having is that everytime i move mouse(slightly fast) or press a bunch of buttons at once, it breaks the while self.shooting loop.
The general approach to firing bullets is to store the positions of the bullets in a list (bullet_list). When a bullet is fired, add the bullet's starting position ([start_x, start_y]) to the list. The starting position is the position of the object (player or enemy) that fires the bullet. Use a for-loop to iterate through all the bullets in the list. Move position of each individual bullet in the loop. Remove a bullet from the list that leaves the screen (bullet_list.remove(bullet_pos)). For this reason, a copy of the list (bullet_list[:]) must be run through (see How to remove items from a list while iterating?). Use another for-loop to blit the remaining bullets on the screen:
bullet_list = []
while run == True:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet_list.append([start_x, start_y])
for bullet_pos in bullet_list[:]:
bullet_pos[0] += move_bullet_x
bullet_pos[1] += move_bullet_y
if not screen.get_rect().colliderect(bullet_image.get_rect(center = bullet_pos))
bullet_list.remove(bullet_pos)
# [...]
for bullet_pos in bullet_list[:]
screen.blit(bullet_image, bullet_image.get_rect(center = bullet_pos))
# [...]
See also Shoot bullet.
Please note, that class names should normally use the CapWords convention.
(See Style Guide for Python Code - Class names)
That means it has to be Player and Bullet rather than player and bullet
You have an application loop, so use it. All the objects are continuously updated and drawn in the main application loop, in each frame.
The class Bullet do not need any loop. The constructor has to have parameters for the position (x, y). Further it needs on method which changes the position and one which draws the bullet:
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
Use a list of bullets. Create a new bullet when space is pressed. Move the bullets (update) in every frame an remove a bullet if it is out of the window. Draw the remaining bullets in every frame:
bullets = []
# [...]
while run:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# [...]
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# [...]
for b in bullets:
b.draw()
Furthermore use pygame.key.get_pressed() use to get the state of the keys in every frame and to update the position of the player:
while run:
# [...]
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
Complete example:
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
clock = pygame.time.Clock()
class Player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
bullets = []
p = Player(600, 500, 50, 30)
run = True
while run:
clock.tick(100)
# handel events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# update objects
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# clear display
d.fill((98, 98, 98))
# draw scene
for b in bullets:
b.draw()
p.draw()
# update display
win.update()

How do I make my bullets continuously move instead of stopping where I clicked? [duplicate]

Heres my code
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
class player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class bullet:
def __init__(self):
self.radius = 10
self.speed = 20
def shoot(self):
x = p.x
y = p.y
self.shooting = True
while self.shooting:
d.fill((98, 98, 98))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
y -= self.speed
pygame.draw.circle(d, (255, 0, 0), (x, y), self.radius)
pygame.time.Clock().tick(100)
win.update()
if y <= 0:
self.shooting = False
b = bullet()
p = player(600, 500, 50, 30)
while True:
d.fill((98, 98, 98))
p.draw()
for event in pygame.event.get():
pass
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
b.shoot()
if event.key == pygame.K_LEFT:
p.move_left()
if event.key == pygame.K_RIGHT:
p.move_right()
win.update()
This is what i could come up with after a few Trial and errors but it is really uneffective. Firstly the player disappers when i press space bar. I guess this is obvious as i have a different loops for shooting and player but i dont know how to get around it and implement both shooting and player in the same loop.
The second probllem i am having is breaking the while self.shooting: loop. I tried breaking it when y reaches a certain point by doing this
if y <= 0:
self.shooting = False
but this dosent break. Instead, it restarts the loop all over again.
Another weird problem i am having is that everytime i move mouse(slightly fast) or press a bunch of buttons at once, it breaks the while self.shooting loop.
The general approach to firing bullets is to store the positions of the bullets in a list (bullet_list). When a bullet is fired, add the bullet's starting position ([start_x, start_y]) to the list. The starting position is the position of the object (player or enemy) that fires the bullet. Use a for-loop to iterate through all the bullets in the list. Move position of each individual bullet in the loop. Remove a bullet from the list that leaves the screen (bullet_list.remove(bullet_pos)). For this reason, a copy of the list (bullet_list[:]) must be run through (see How to remove items from a list while iterating?). Use another for-loop to blit the remaining bullets on the screen:
bullet_list = []
while run == True:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet_list.append([start_x, start_y])
for bullet_pos in bullet_list[:]:
bullet_pos[0] += move_bullet_x
bullet_pos[1] += move_bullet_y
if not screen.get_rect().colliderect(bullet_image.get_rect(center = bullet_pos))
bullet_list.remove(bullet_pos)
# [...]
for bullet_pos in bullet_list[:]
screen.blit(bullet_image, bullet_image.get_rect(center = bullet_pos))
# [...]
See also Shoot bullet.
Please note, that class names should normally use the CapWords convention.
(See Style Guide for Python Code - Class names)
That means it has to be Player and Bullet rather than player and bullet
You have an application loop, so use it. All the objects are continuously updated and drawn in the main application loop, in each frame.
The class Bullet do not need any loop. The constructor has to have parameters for the position (x, y). Further it needs on method which changes the position and one which draws the bullet:
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
Use a list of bullets. Create a new bullet when space is pressed. Move the bullets (update) in every frame an remove a bullet if it is out of the window. Draw the remaining bullets in every frame:
bullets = []
# [...]
while run:
for event in pygame.event.get():
# [...]
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# [...]
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# [...]
for b in bullets:
b.draw()
Furthermore use pygame.key.get_pressed() use to get the state of the keys in every frame and to update the position of the player:
while run:
# [...]
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
Complete example:
import pygame, os
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
win = pygame.display
d = win.set_mode((1200, 600))
clock = pygame.time.Clock()
class Player:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.speed = 2
def draw(self):
pygame.draw.rect(d, (0, 0, 0), (self.x, self.y, self.width, self.height))
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
class Bullet:
def __init__(self, x, y):
self.radius = 10
self.speed = 10
self.x = x
self.y = y
def update(self):
self.y -= self.speed#
def draw(self):
pygame.draw.circle(d, (255, 0, 0), (self.x, self.y), self.radius)
bullets = []
p = Player(600, 500, 50, 30)
run = True
while run:
clock.tick(100)
# handel events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullets.append(Bullet(p.x+p.width//2, p.y))
# update objects
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
p.move_left()
if keys[pygame.K_RIGHT]:
p.move_right()
for b in bullets:
b.update()
if b.y < 0:
bullets.remove(b)
# clear display
d.fill((98, 98, 98))
# draw scene
for b in bullets:
b.draw()
p.draw()
# update display
win.update()

How to move a surface in pygame

I'm making a game in pygame and I'm trying to move the self.jetRect, which is the rectangular area of the Surface (jetSurface), but when I try updating the coordinates of the rectangle, I get a TypeError: 'tuple' object is not callable. I think this is related to the fact that a tuple is immutable, but I don't know how to fix this.
import pygame
pygame.init()
clock = pygame.time.Clock()
# Screen setup
screen_width = 600
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Jet Fighter Game")
# Colors
black = (0, 0, 0)
white = (255, 255, 255)
grey = (100, 100, 100)
# Classes
class PLAYER1:
def __init__(self):
self.jetSurface = pygame.image.load("player1 - blackJet.png")
self.x = 100
self.y = 100
self.speed = 5
self.jetRect = self.jetSurface.get_rect(center = (self.x, self.y))
self.angle = 0
self.rotatedJet = self.jetSurface
def rotateJet(self, surface, angle):
key = pygame.key.get_pressed()
if key[pygame.K_a]:
self.angle += 10
self.rotatedJet = pygame.transform.rotozoom(surface, angle, 1)
self.jetRect = self.rotatedJet.get_rect(center=(self.x, self.y))
if key[pygame.K_d]:
self.angle -= 10
self.rotatedJet = pygame.transform.rotozoom(surface, angle, 1)
self.jetRect = self.rotatedJet.get_rect(center=(self.x, self.y))
if self.angle >= 360:
self.angle = 0
screen.blit(self.rotatedJet, self.jetRect)
def moveJet(self):
key = pygame.key.get_pressed()
if key[pygame.K_w]:
self.y -= self.speed
self.jetRect.center(self.x, self.y)
class PLAYER2:
pass
class BULLET:
pass
class MAIN:
def __init__(self):
self.player1 = PLAYER1()
def rotateJets(self):
self.player1.rotateJet(self.player1.jetSurface, self.player1.angle)
def moveJets(self):
self.player1.moveJet()
main = MAIN()
# Main loop
run = True
while run:
# Checking for events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# Drawing on screen
screen.fill(grey)
# Movement and Collisions
main.rotateJets()
main.moveJets()
pygame.display.flip()
clock.tick(60)
pygame.quit()
Its a typo. self.jetRect.center is a tuple of (self.x, self.y), so when you do
self.jetRect.center(self.x, self.y), you are doing (self.x, self.y)(self.x, self.y), hence the error message of calling a tuple.
I think you meant to assign the tuple like self.jetRect.center = (self.x, self.y)

Updating a object already in a group

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()

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()) )

Categories

Resources