If Statement stops working after sometime and font not rendering - python

I am creating a game with pygame & python 3.10.0 using VSCode everything is working fine but this if statement stops working after sometime and the font doesn't draw but I can't pinpoint the problem for that , for the if statement it usually runs till 10 or 11 score but it can be quicker like 2:
if player.rect.colliderect(food):
pygame.sprite.Sprite.kill(food)
food.rect.x = random.randrange(20, 1700)
food.rect.y = random.randrange(20, 860)
all_sprites_list.add(food)
score += 1
print(score)
whole code:
import pygame
import sys
import time
import random
import ctypes
from ctypes import wintypes
from pygame import sprite
from pygame.draw import rect
from pygame.event import pump, wait
from pygame import font
pygame.font.init()
myappid = 'elementalstudios.snake.Alpha 0_1' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
#------ Initialize Variables------#
# Player
player_width = 20
player_height = 20
# Food
food_width = 10
food_height = 10
# Colours
seafoam_gr = (159, 226, 191)
black = (0, 0, 0)
blue = (0, 0, 255)
red = (255, 0, 0)
green = (0, 255, 0)
white = (255, 255, 255)
# Score
score = 0
#------ Score Font Initialize ------#
font = pygame.font.Font(None, 50)
text = font.render("Score:", False, white, None)
#------Sprites Class------#
class Sprite(pygame.sprite.Sprite):
def __init__(self, color, height, width):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(seafoam_gr)
self.image.set_colorkey(black)
pygame.draw.rect(self.image,
color,
pygame.Rect(0, 0, width, height))
self.rect = self.image.get_rect()
def moveRight(self, pixels):
self.rect.x += pixels
def moveLeft(self, pixels):
self.rect.x -= pixels
def moveUp(self, speed):
self.rect.y -= speed * speed / 5
def moveDown(self, speed):
self.rect.y += speed * speed / 5
#------ Font Class ------#
def create_font(t,s = 24, colour_font = white, bold = False, italic = False):
font = pygame.font.Font("prstart.ttf", s, bold, italic)
text = font.render(t, True, colour_font)
return text
#------ Initialize Pygame and Window------#
pygame.init()
icon = pygame.image.load('Icon.ico')
pygame.display.set_icon(icon)
gameDisplay = pygame.display.set_mode((1920,1080), pygame.FULLSCREEN)
#rect = pygame.Rect( * gameDisplay.get_rect().center, 0, 0).inflate(100, 100)
pygame.display.set_caption("Blocky")
gameDisplay.fill(black)
clock = pygame.time.Clock()
running = True
#------Initialize Sprites------#
all_sprites_list = pygame.sprite.Group()
player = Sprite(seafoam_gr, player_height, player_width)
food = Sprite(red, food_height, food_width)
player.rect.x = 960
player.rect.y = 540
food.rect.x = 20 #random.randrange(20, 1800)
food.rect.y = 30 #random.randrange(20, 1050)
#------Add Sprites to sprite list------#
all_sprites_list.add(player)
all_sprites_list.add(food)
clock = pygame.time.Clock()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
#------Eating------#
if player.rect.colliderect(food):
pygame.sprite.Sprite.kill(food)
food.rect.x = random.randrange(20, 1700)
food.rect.y = random.randrange(20, 860)
all_sprites_list.add(food)
score += 1
print(score)
#------ Score Draw ------#
text_rect = text.get_rect()
text_rect.center = (100, 150)
gameDisplay.blit(text, text_rect)
#------Key Movement------#
keys = pygame.key.get_pressed()
# Move Left
if keys[pygame.K_a]:
player.moveLeft(3)
if keys[pygame.K_LCTRL]:
player.moveLeft(5)
if keys[pygame.K_LEFT]:
player.moveLeft(3)
if keys[pygame.K_LCTRL]:
player.moveLeft(5)
# Move Right
if keys[pygame.K_d]:
player.moveRight(5)
if keys[pygame.K_LCTRL]:
player.moveRight(5)
if keys[pygame.K_RIGHT]:
player.moveRight(3)
if keys[pygame.K_LCTRL]:
player.moveRight(5)
# Move Down
if keys[pygame.K_s]:
player.moveDown(3)
if keys[pygame.K_LCTRL]:
player.moveDown(5)
if keys[pygame.K_DOWN]:
player.moveDown(3)
if keys[pygame.K_LCTRL]:
player.moveDown(5)
# Move Up
if keys[pygame.K_w]:
player.moveUp(3)
if keys[pygame.K_LCTRL]:
player.moveUp(5)
if keys[pygame.K_UP]:
player.moveUp(3)
if keys[pygame.K_LCTRL]:
player.moveUp(5)
all_sprites_list.update()
gameDisplay.fill(black)
all_sprites_list.draw(gameDisplay)
pygame.display.flip()
clock.tick(60)
pygame.quit()
quit()

It is not necessary to kill the food object. It is sufficient to change the position of the food. pygame.sprite.Sprite.kill just removes the Sprite object from all Gorups. So there is no point in calling kill and then adding the object back to the Group.
The text does not show up, because you draw it before you clear the display. pygame.Surface.fill fills the Surface with a solid color. Everything that was previously drawn will be cleared. blit the text after fill.
You will have to render the text Surface again if the score has changed.
I recommend simplifying the code that moves the player. See How can I make a sprite move when key is held down.
class Sprite(pygame.sprite.Sprite):
def __init__(self, color, x, y, height, width):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.image.set_colorkey(black)
self.rect = self.image.get_rect(topleft = (x, y))
self.x, self.y = self.rect.x, self.rect.y
def move(self, move_x, move_y, speed_up):
if move_x or move_y:
direction = pygame.math.Vector2(move_x, move_y)
direction.scale_to_length(5 if speed_up else 3)
self.x += direction.x
self.y += direction.y
self.rect.x, self.rect.y = round(self.x), round(self.y)
player = Sprite(seafoam_gr, 400, 300, player_height, player_width)
food = Sprite(red, 20, 30, food_height, food_width)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if player.rect.colliderect(food):
food.rect.x = random.randrange(20, 780)
food.rect.y = random.randrange(20, 580)
score += 1
# render text
text = font.render("Score: " + str(score), False, white, None)
keys = pygame.key.get_pressed()
move_x = ((keys[pygame.K_d] or keys[pygame.K_RIGHT]) - (keys[pygame.K_a] or keys[pygame.K_LEFT]))
move_y = ((keys[pygame.K_s] or keys[pygame.K_DOWN]) - (keys[pygame.K_w] or keys[pygame.K_UP]))
player.move(move_x, move_y, keys[pygame.K_LCTRL])
all_sprites_list.update()
gameDisplay.fill(black)
# draw text
gameDisplay.blit(text, text.get_rect(center = (100, 150)))
all_sprites_list.draw(gameDisplay)
pygame.display.flip()
clock.tick(60)
pygame.quit()
quit()

Related

It gives me an error that my object has no attribute even though I assigned it [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 7 months ago.
I'm working in pygame & python and trying to make a zombie shooting game. It all worked smoothly until I tried to add the collision system. If you also know how to add a collision system for this game please leave a comment with a full code.
The error:
type object 'bullet' has no attribute 'rect'
File "C:\Users\*\Desktop\Zombie!\main.py", line 172, in game
collide = pygame.sprite.spritecollide(bullet, enemiesList, False)
File "C:\Users\*\Desktop\Zombie!\main.py", line 44, in menu
game()
File "C:\Users\*\Desktop\Zombie!\main.py", line 211, in <module>
menu()
The code:
import pygame, sys, math, random, time
pygame.init()
#var
screen = pygame.display.set_mode([800, 500])
font = pygame.font.SysFont(None, 20)
playerX = 200
playerY = 200
player = pygame.Rect((playerX, playerY), (10,10))
bullets = pygame.sprite.Group()
clock = pygame.time.Clock()
previous_time = pygame.time.get_ticks()
waveCount = 1
#Remaining enemy count
enemiesList = pygame.sprite.Group()
normalEnemy = 0
speedyEnemy = 0
tankEnemy = 0
def draw_text(text, font, color, surface, x, y):
textobj = font.render(text, 1, color)
textrect = textobj.get_rect()
textrect.topleft = (x, y)
surface.blit(textobj, textrect)
click = False
def menu():
while True:
global click
screen.fill((0,0,0))
draw_text('main menu', font, (255, 255, 255), screen, 20, 20)
mx, my = pygame.mouse.get_pos()
button_1 = pygame.Rect(50, 100, 200, 50)
button_2 = pygame.Rect(50, 200, 200, 50)
if button_1.collidepoint((mx, my)):
if click:
game()
if button_2.collidepoint((mx, my)):
if click:
options()
pygame.draw.rect(screen, (255, 0, 0), button_1)
pygame.draw.rect(screen, (255, 0, 0), button_2)
click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
click = True
pygame.display.update()
clock.tick(60)
def game():
run = True
class bullet(pygame.sprite.Sprite):
def __init__(self, x, y, mx, my):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.mx = mx
self.my = my
self.speed = 10
self.angle = math.atan2(my-self.y, mx-self.x)
self.x_vel = math.cos(self.angle) * self.speed
self.y_vel = math.sin(self.angle) * self.speed
self.radius = 4
self.mask = pygame.mask.Mask((self.radius, self.radius), True)
self.rect = pygame.Rect(self.x, self.y, self.radius, self.radius)
def update(self):
self.x += int(self.x_vel)
self.y += int(self.y_vel)
pygame.draw.circle(screen, (0, 255, 255), (self.x + 5, self.y + 5), self.radius)
if self.x > 800 or self.x < 0 or self.y > 500 or self.y < 0:
#Remove Bullet Class from list(bullets)
self.kill()
class enemy(pygame.sprite.Sprite):
def __init__(self, enemyType, x, y):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
if enemyType == "normal":
self.speed = 1
self.hp = 1
self.color = (255, 28, 28)
self.radius = 10
if enemyType == "speedy":
self.speed = 3
self.hp = 1
if enemyType == "tank":
self.speed = 3
self.hp = 3
self.mask = pygame.mask.Mask((self.radius, self.radius), True)
self.rect = pygame.Rect(self.x, self.y, self.radius, self.radius)
def update(self):
global playerX, playerY, enemiesList, bullets
# Find direction vector (dx, dy) between enemy and player.
dx, dy = playerX - self.x, playerY - self.y
dist = math.hypot(dx, dy)
dx, dy = dx / dist, dy / dist # Normalize.
# Move along this normalized vector towards the player at current speed.
self.x += dx * self.speed
self.y += dy * self.speed
pygame.draw.circle(screen, self.color, (self.x, self.y), self.radius)
#Wave system
def waves(waveNumber):
global normalEnemy,speedyEnemy, tankEnemy, enemiesList, waveCount
if waveNumber == 1:
normalEnemy = 5
speedyEnemy = 0
tankEnemy = 0
for i in range(5):
enemiesList.add(enemy("normal", random.randint(0, 800), random.randint(0, 600)))
if waveNumber == 2:
normalEnemy = 7
speedyEnemy = 0
tankEnemy = 0
while run:
clock.tick(60)
screen.fill([255, 255, 255])
mx, my= pygame.mouse.get_pos()
#movements
def movement():
global playerX, playerY, previous_time
key = pygame.key.get_pressed()
mouse = pygame.mouse.get_pressed()
if key[pygame.K_w]:
playerY -= 5
if key[pygame.K_s]:
playerY += 5
if key[pygame.K_d]:
playerX += 5
if key[pygame.K_a]:
playerX -= 5
if mouse[0]:
current_time = pygame.time.get_ticks()
if current_time - previous_time > 500:
previous_time = current_time
bullets.add(bullet(playerX, playerY, mx, my))
for bullets_ in bullets:
bullets_.update()
for enemies_ in enemiesList:
enemies_.update()
if normalEnemy == 0 and speedyEnemy == 0 and tankEnemy == 0:
waves(waveCount)
collide = pygame.sprite.spritecollide(bullet, enemiesList, False)
def draw():
global player, waveCount
player = pygame.Rect((playerX, playerY), (10,10))
pygame.draw.rect(screen, (0, 255, 0), player)
draw_text('wave:' + str(waveCount), font, (0, 0, 0), screen, 20, 20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
movement()
draw()
pygame.display.flip()
def options():
running = True
while running:
screen.fill((0,0,0))
draw_text('options', font, (255, 255, 255), screen, 20, 20)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
pygame.display.update()
clock.tick(60)
menu()
I am pretty sure I assigned self.rect at the bullet class, I checked the indents and the spelling but I can't to seem to find the error.
You have a Group bullets and a Group enemiesList. Use pygame.sprite.groupcollide() to detect collisions between Sprites in Groups:
collide = pygame.sprite.groupcollide(bullets, enemiesList, False, False)
Look at the error: type error bullet ... . Somewhere, you're using the bullet class name instead of using an instance of that class.
pygame.sprite.spritecollide - assumes that first parameter is an instance of a class which has 'rect' attribute. But you put the class itself there.
I believe the code must looks like this:
for bullets_ in bullets:
collide = pygame.sprite.spritecollide(bullets_, enemiesList, False)

How to create and move multiple shapes in python

I am creating a space type game for a school competition. Im going to use stars (white dots) moving outwards to give an effect of moving in space. Next I would check to see if a star's x cords are less than the centre or the screen (move the star to the left) and if the x coordinate is larger (Move to the right). However, I can't seem to make more than one instance of the stars class.Here is an image of the kind of thing I want . Help is very appreciated.
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((1200, 800))
caption = pygame.display.set_caption("sapce game")
screen.fill((56, 56, 56))
white = (255, 255, 255)
class Stars:
def __init__(self):
self.x = random.randint(0, 600)
self.y = random.randint(0, 400)
self.pos = (self.x, self.y)
def move(self):
pygame.draw.circle(screen, white, (self.x, self.y), 4)
self.y -= 1
self.x -= 2
screen.fill((56, 56, 56))
pygame.draw.circle(screen, white, (self.x, self.y), 4)
pygame.display.update()
s = Stars()
run = True
while run:
s.move()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
First of all you have to put scrre.fill and pygame.display.update() in the main application loop. Remove this calls from Stars.move:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
screen.fill((56, 56, 56))
s.move()
pygame.display.update()
Note, you want to clear the display once, then draw all the stars and finally update the display.
Create a list of stars:
star_list = []
for i in range(10):
star_list.append(Stars())
Move and draw the stars in a loop:
run = True
while run:
# [...]
screen.fill((56, 56, 56))
for s in star_list:
s.move()
pygame.display.update()
Example code:
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((1200, 800))
caption = pygame.display.set_caption("sapce game")
screen.fill((56, 56, 56))
white = (255, 255, 255)
class Stars:
def __init__(self):
self.x = random.randint(0, 600)
self.y = random.randint(0, 400)
self.dx, self.dy = 0, 0
while self.dx == 0:
self.dx = random.randint(-2, 2)
while self.dy == 0:
self.dy = random.randint(-2, 2)
self.pos = (self.x, self.y)
def move(self):
self.x += self.dx
self.y += self.dy
if self.x <= 0 or self.x >= 1200:
self.dx = -self.dx
if self.y <= 0 or self.y >= 800:
self.dy = -self.dy
pygame.draw.circle(screen, white, (self.x, self.y), 4)
star_list = []
for i in range(10):
star_list.append(Stars())
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
quit()
screen.fill((56, 56, 56))
for s in star_list:
s.move()
pygame.display.update()

Objects being drawn from list not staying on the screen (pygame)

I'm starting to work with classes more and I was trying to translate my old classless code to this new format. Before, I defined my lists as global variables and could draw multiple enemies to the screen with no problem. This time, however, something about working with classes causes the enemies to be drawn and instantly disappear from their position.
Some help or workaround would be greatly appreciated. Here's the code:
import pygame
import sys
import random
import math
pygame.init()
class enemies(object):
enemies = []
def __init__(self, enemy_num, diff, enemy_size):
self.enemy_num = enemy_num
self.diff = diff
self.enemy_s = enemy_size
def add_enemies(self):
counter = 0
if len(self.enemies) < self.enemy_num:
for enemy in range(0, self.enemy_num):
if len(self.enemies) < 5:
self.enemies.append([counter * (self.enemy_s * 0.5), 200, (50, 168, 82)])
elif len(self.enemies) > 5 and len(self.enemies) <= 9:
self.enemies.append([counter * (self.enemy_s * 0.5), 250, (168, 160, 50)])
else:
self.enemies.append([counter * (self.enemy_s * 0.5), 300, (50, 52, 168)])
counter += 1
def move(self, surface):
for enemy in self.enemies:
surface.fill((0, 0, 0))
pygame.draw.rect(surface, enemy[2], (enemy[0], enemy[1], self.enemy_s, self.enemy_s))
pygame.display.update()
class ship(object):
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
def draw_all(self, surface, ship_size):
surface.fill((0, 0, 0))
pygame.draw.rect(surface, self.color, (self.x, self.y, ship_size, ship_size))
pygame.display.update()
def move(self):
keys = pygame.key.get_pressed()
for key in keys:
if keys[pygame.K_LEFT]:
self.x -= 0.2
if keys[pygame.K_RIGHT]:
self.x += 0.2
if keys[pygame.K_UP]:
self.y -= 0.2
if keys[pygame.K_DOWN]:
self.y += 0.2
def main():
width = 800
height = 1000
ship_size = 35
difficulty = 0
enemy_number = 12
enemy_size = 35
player = ship(width / 2, height - 100, (255, 0, 0))
aliens = enemies(enemy_number, difficulty, enemy_size)
screen = pygame.display.set_mode((width, height))
enemy_list = 12
run = True
clock = pygame.time.Clock()
aliens.add_enemies()
while run:
player.draw_all(screen, ship_size)
player.move()
aliens.move(screen)
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.quit()
main()
You've to do 1 clear display and 1 display update in the main loop of the application.
Remove .fill() respectively .pygame.display.update from the .move() and .draw_all method. Clear the display once, draw all the objects and then update the display:
while run:
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.quit()
screen.fill((0, 0, 0))
player.draw_all(screen, ship_size)
player.move()
aliens.move(screen)
pygame.display.update()
Note, you didn't see the enemies, because the display is clear before drawing a single enemy in enemies.move() and it is clear again when drawing the ship in ship.draw_all()

Detecting the point of collision with Pygame

I am making a game with Pygame. For this game I need to be able to detect not only that two rectangles have collided, but also the point of collision between them. I looked at the documentation but couldn't seem to find any answers.
Is something like this possible?
You can use Rect.clip:
crops a rectangle inside another
clip(Rect) -> Rect
Returns a new rectangle that is cropped to be completely inside the argument Rect. If the two rectangles do not overlap to begin with, a Rect with 0 size is returned.
Here's an example:
import pygame
import random
class Stuff(pygame.sprite.Sprite):
def __init__(self, pos, color, *args):
super().__init__(*args)
self.image = pygame.Surface((30, 30))
self.image.fill(color)
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
def update(self):
self.rect.center = self.pos
def main():
pygame.init()
screen = pygame.display.set_mode((500, 500))
screen_rect = screen.get_rect()
font = pygame.font.SysFont(None, 26)
clock = pygame.time.Clock()
sprites = pygame.sprite.Group()
blocks = pygame.sprite.Group()
movement = {
pygame.K_UP: ( 0, -1),
pygame.K_DOWN: ( 0, 1),
pygame.K_LEFT: (-1, 0),
pygame.K_RIGHT: ( 1, 0)
}
for _ in range(15):
x, y = random.randint(0, 500), random.randint(0, 500)
color = random.choice(['green', 'yellow'])
Stuff((x, y), pygame.Color(color), sprites, blocks)
player = Stuff(screen_rect.center, pygame.Color('dodgerblue'))
sprites.add(player)
dt = 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
pressed = pygame.key.get_pressed()
move = pygame.Vector2()
for dir in (movement[key] for key in movement if pressed[key]):
move += dir
if move.length() > 0: move.normalize_ip()
player.pos += move * dt/5
sprites.update()
screen.fill(pygame.Color('black'))
sprites.draw(screen)
for block in pygame.sprite.spritecollide(player, blocks, False):
clip = player.rect.clip(block.rect)
pygame.draw.rect(screen, pygame.Color('red'), clip)
hits = [edge for edge in ['bottom', 'top', 'left', 'right'] if getattr(clip, edge) == getattr(player.rect, edge)]
text = font.render(f'Collision at {", ".join(hits)}', True, pygame.Color('white'))
screen.blit(text, (20, 20))
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()

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