Pygame not updating dialogue within a sprite group - python

I have an issue with my dialogue index updating but not updating the text drawn to the screen. If the player is within distance of the npc the space button should update the dialogue. Currently it does draw the first index of dialogue if the player collides with the npc but moving away doesn't change anything as well as entering space. npc_list should be updating the self.image and then redrawing them.
self.npcs = [ [[450,500,[]]],]
self.npc_dialogue = [['Hi','Bye']]
self.npc_dialogue_sizes = [(25,50),(25,50)]
self.npc_list = pygame.sprite.Group()
#x,y,size,dialog,path_list
for i,npc in enumerate(self.npcs[self.level]):
new_npc = Npc((npc[0],npc[1]),self.npc_dialogue_sizes,self.npc_dialogue[0],npc[2])
self.npc_list.add(new_npc)
def display_stages(self,screen):
clock = pygame.time.Clock()
running = True
#Break if exit goes into a cutscene maybe use -1 and then update the level.
dialog = False
npc = None
while running:
if self.stages.level>=len(self.stages.exits) or self.state!=0:
break
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
if dialog and npc!=None:
#Update a single npc list dialogue
npc.update()
print('Update')
screen.fill(white)
dialog, npc = self.npc_dialog()
if dialog and npc!=None:
npc.draw_dialog(screen)
self.stages.draw(screen)
pygame.display.flip()
clock.tick(FPS)
def npc_dialog(self):
for npc in self.stages.npc_list:
gets_hit = npc.rect.colliderect(self.stages.player.rect)
if gets_hit:
return True, npc
return False, None
def draw(self,screen):
if self.level < len(self.boundaries):
screen.blit(self.image,self.rect)
self.npc_list.draw(screen)
class Npc(pygame.sprite.Sprite):
def __init__(self,position,size,dialog,path_list):
pygame.sprite.Sprite.__init__(self)
print(size,position,dialog)
self.sizes = size
self.image = pygame.Surface(self.sizes[0])
self.rect = self.image.get_rect()
self.rect.topleft = position
self.path_list = path_list
# rework size and position to n*elements with dialog size
positions = []
for i in range(len(size)):
positions.append(position)
self.dialog = Dialog(positions,size,dialog)
def update(self):
#self.dialog.update()
print(self.dialog.index,len(self.dialog.size))
if self.dialog.index < len(self.dialog.size)-1:
self.dialog.update()
def draw_dialog(self,screen):
print(self.dialog.dialog[self.dialog.index])
if self.dialog.index < len(self.dialog.size)-1:
print('Updating drawing')
text = self.dialog.font.render(self.dialog.dialog[self.dialog.index], True, white)
self.image.blit(text, (self.dialog.dialog_text_offset,self.dialog.dialog_text_offset))
class Dialog():
def __init__(self,position,size,dialog):
self.index = 0
self.dialog = dialog
self.sizes = size
self.size = self.sizes[self.index]
self.positions = position
self.pos = position[self.index]
self.image = pygame.Surface(self.size, pygame.SRCALPHA)
self.rect = pygame.Rect((0,0), self.size)
self.font = pygame.font.Font(None,32)
self.rect.topleft = self.pos
self.dialog_background = (0, 0, 255, 127)
self.dialog_text_offset = 5
def update(self):
self.size = self.sizes[self.index]
self.pos = self.positions[self.index]
self.index += 1

The list of size tuples is self.dialog.sizes, not self.dialog.size:
if self.dialog.index < len(self.dialog.size)-1:
if self.dialog.index < len(self.dialog.sizes)-1:

Related

Colliderect firing even when 2 items aren't touching

I am making a system where the items fall from the sky and you collect them however the collision is being registered even when the character doesn't touch the items. The collision happens when the character collides with the item on the x axis but it can collide at any point on the y axis.
Here is an example from the image below:
Example Image
I have tried printing the positions of both the character and item to see if for some reason the position wasn't up to date with where it was on screen but for both the character and item the positions were fine.
Here is my code:
import pygame
import sys
import os
import time
from pygame.locals import *
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
SCREENWIDTH = 580
SCREENHEIGHT = 375
SCREENSIZE = [SCREENWIDTH, SCREENHEIGHT]
SCREEN = pygame.display.set_mode(SCREENSIZE)
CLOCK = pygame.time.Clock()
FPS = 60
jumping = False
backgroundImage = pygame.image.load('Images/backgroundImage.jpg')
backgroundScaled = pygame.transform.scale(backgroundImage,(580,375))
pygame.display.set_caption("Christmas Game")
santaWidth, santaHeight = 93,64
santaImage = pygame.image.load("Images/santa.png")
santaScaled = pygame.transform.scale(santaImage,(santaWidth,santaHeight))
santa = pygame.Rect(100,100,santaWidth,santaHeight)
font = pygame.font.SysFont("myriadProFont",50)
timeText = font.render("", 1,(255,255,255))
startTime = time.time()
itemsGroup = pygame.sprite.Group()
#IDs 0 = coin,
class Item(pygame.sprite.Sprite):
def __init__(self,x,y,ID,imageName):
super().__init__()
self.ID = ID
self.image = pygame.image.load(imageName)
self.scaledImage = pygame.transform.scale(self.image,(50,50))
self.x = x
self.y = y
self.rect = self.image.get_rect()
self.rect.topleft = (self.x,self.y)
def render(self, screen):
screen.blit(self.scaledImage,(self.x,self.y))
def collect(self, santa):
hit = self.rect.colliderect(santa)
if hit:
if self.ID == 0:
print("Coin")
self.kill()
def itemGravity(self):
if self.y < 370:
self.y += 1
self.rect = self.image.get_rect()
self.rect.topleft = (self.x,self.y)
else:
self.kill()
print("killed")
coin = Item(200,0,0,"Images/coin.png")
itemsGroup.add(coin)
coin2 = Item(500,0,0,"Images/coin.png")
itemsGroup.add(coin2)
class Floor(pygame.sprite.Sprite):
def __init__(self, width, height, x, y, name):
super().__init__()
self.image = pygame.image.load(name)
self.image = pygame.transform.scale(self.image, (width, height))
self.rect = self.image.get_rect()
self.size = width, height
self.pos = (x, y)
self.rect.topleft = self.pos
self.isTouching = False
def render(self, display):
display.blit(self.image, self.pos)
def collision(self,santa):
global jumping
hit = self.rect.colliderect(santa)
if hit and self.isTouching == False:
self.isTouching = True
elif not hit:
self.isTouching = False
floorGroup = pygame.sprite.Group()
floor = Floor(291,70,-50,305,"Images/floor.webp")
floorGroup.add(floor)
floor2 = Floor(291,70,330,305,"Images/floor.webp")
floorGroup.add(floor2)
def adjustTime():
global timeText
global jumping
newTime = time.time()
difference = newTime - startTime
if difference > 1:
timeText = font.render(time.strftime('%M:%S', time.gmtime(difference)), 1,(255,255,255))
def playerMovement(keyPressed,santa):
global jumping
if (keyPressed[pygame.K_UP] or keyPressed[pygame.K_w] or keyPressed[pygame.K_SPACE]) and santa.y > 0 and jumping == False:
jumping = True
santa.y = santa.y - 50
elif(keyPressed[pygame.K_LEFT] or keyPressed[pygame.K_a]) and santa.x > -20:
santa.x = santa.x - 5
elif(keyPressed[pygame.K_RIGHT] or keyPressed[pygame.K_d]) and santa.x < 525:
santa.x = santa.x + 5
def updateScreen(santa):
SCREEN.blit(backgroundScaled, (0, 0))
for floor in floorGroup:
floor.render(SCREEN)
for item in itemsGroup:
item.render(SCREEN)
SCREEN.blit(santaScaled,(santa.x,santa.y))
SCREEN.blit(timeText,(250,0))
pygame.display.update()
def gravity(santa):
global jumping
if santa.y < 320:
santa.y += 3
elif santa.y == 320:
jumping = False
else:
santa.y = 320
while True:
timeDelta = CLOCK.tick(FPS)/1000.0
adjustTime()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
user_input = pygame.key.get_pressed()
playerMovement(user_input,santa)
gravity(santa)
for item in itemsGroup:
item.collect(santa)
item.itemGravity()
for floor in floorGroup:
floor.collision(santa)
updateScreen(santa)
CLOCK.tick(FPS)
`
You have to get the bounding rectangle of the scaled image instead of the original image:
self.rect = self.image.get_rect()
self.rect = self.scaledImage.get_rect()
Note that the pygame.sprite.Sprite object does not need a render or draw method if you use pygame.sprite.Group. See How can I add objects to a "pygame.sprite.Group()"? and What does pygame.sprite.Group() do

i want to change the location of my sprite when i click.... who to do that?

from numpy import place
import pygame, sys ,random as ran
start = True
ref_x = ran.randint(18,387)
ref_y = ran.randint(18,387)
class Player(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.attack_animation = False
self.sprites_1 = []
self.sprites_1.append(pygame.image.load('.\crossHair.png'))
self.sprites_1.append(pygame.image.load('.\crossHair_2.png'))
self.sprites_1.append(pygame.image.load('.\crossHair_3.png'))
self.sprites_1.append(pygame.image.load('.\crossHair_4.png'))
self.sprites_1.append(pygame.image.load('.\crossHair_5.png'))
self.sprites_1.append(pygame.image.load('.\FIRE.png'))
self.current_sprite = 0
self.image = self.sprites_1[self.current_sprite]
self.image.set_colorkey('white')
for items in self.sprites_1:
items.set_colorkey('white')
self.rect = self.image.get_rect()
self.rect.topleft = [pos_x,pos_y]
def attack(self):
self.attack_animation = True
self.image.set_colorkey('white')
if mouse[0]+24 >= ref_x and ref_x+4 >= mouse[0] and mouse[1]+24 >= ref_y and ref_y+13 >= mouse[1]:
get_shot()
else :
print(ref_x,'here')
def update(self,speed):
self.image.set_colorkey('white')
mouse = pygame.mouse.get_pos()
if self.attack_animation == True:
self.current_sprite += speed
if int(self.current_sprite) >= len(self.sprites_1):
self.current_sprite = 0
self.attack_animation = False
print(mouse)
print('shot')
self.image = self.sprites_1[int(self.current_sprite)]
# self.image = self.sprites_1[int(self.current_sprite)]
self.rect = mouse
class enemy(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.image = pygame.image.load('sp_1.png')
self.rect = self.image.get_rect()
self.rect.center = [pos_x, pos_y]
self.image.set_colorkey((255,255,255))
# General setup
pygame.init()
pygame.mouse.set_visible(0)
clock = pygame.time.Clock()
# Game Screen
screen_width = 400
screen_height = 400
mouse = pygame.mouse.get_pos()
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption("Sprite Animation")
# Creating the sprites and groups
moving_sprites = pygame.sprite.Group()
crosshair = Player(mouse[0],mouse[1])
enemy_x = ref_x
enemy_y = ref_y
print(enemy_x,enemy_y)
enemy_ = enemy(enemy_x,enemy_y)
moving_sprites.add(enemy_,crosshair)
def get_shot():
moving_sprites.remove(enemy_)
moving_sprites.add(enemy_)
moving_sprites.remove(crosshair)
moving_sprites.add(crosshair)
pygame.display.flip()
while True:
# Player.set_pos(*pygame.mouse.get_pos())
globals()['mouse'] = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if pygame.mouse.get_pressed()[0]:
crosshair.attack()
# enemy.checkCollision(enemy,crosshair,enemy_)
# enemy.attack()
# pygame.sprite.spritecollide(Player,enemy,True)
screen.fill((120,220,150))
#this is causing the problem
# get_hit = pygame.sprite.spritecollide(Player,enemy,True)
# Drawing
screen.set_colorkey('white')
moving_sprites.draw(screen)
moving_sprites.update(0.06)
pygame.display.flip()
clock.tick(120)
on function get_shot i want to create my sp_1 at a new place i already tried to change the rect but it didn't work so what can I do to make it work and tell me it get_shot is the best method or not or do i have to create another sprite to make it work also i am a beggener to pygame so tr y to make it easy as possible ...
kill the enemy and spawn a new enemy in a new random position:
enemy_ = enemy(ref_x, ref_y)
moving_sprites.add(enemy_, crosshair)
def get_shot():
global enemy_, ref_x, ref_y
enemy_.kill()
ref_x = ran.randint(18,387)
ref_y = ran.randint(18,387)
enemy_ = enemy(ref_x, ref_y)
moving_sprites.add(enemy_)

Creating a Asteroid type game, problem is when player waits to press play button many asteroids spawn instead of controlled amount

About 500 lines of code, so to make it easier to read I believe the bug comes from _update_asteroids(self) function. Simply put, when the user is in the paused game state and must press play, if the player decides to wait lets say a minute. A minutes worth of asteroids will spawn at the exact same time. I believe this is because the pygame.time.get_ticks() function continues to gain ticks while game is paused. Is there any way to reset the ticks or make it so that 100s of asteroids do not spawn when the user decides to wait before pressing play button?
import pygame
import sys
from pygame.sprite import Sprite
import random
from time import sleep
import pygame.font
class Game(Sprite):
""" a class the creates a window with a blue screen """
def __init__(self):
pygame.init()
super().__init__()
#--------------------------------------------------------------------------------------------
#screen size, color, caption
self.screen = pygame.display.set_mode((1200,800)) #create attribute to hold display settings
self.bg_color = (0,0,255) #create attribute to hold RGB color (blue)
pygame.display.set_caption("Blue Screen")
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#tank drawing
self.screen_rect = self.screen.get_rect() #get the screen rect dim
self.image = pygame.image.load('images/tank.gif') #load the image from directory
self.rect = self.image.get_rect() #get the image rect dim
self.rect.center = self.screen_rect.center #store the screens center x/y coord
self.x = float(self.rect.x)
self.y = float(self.rect.y)
#tank movement
self.tank_moving_left = False
self.tank_moving_right = False
self.tank_moving_up = False
self.tank_moving_down = False
self.tank_speed = 0.5 #tank pixels
self.direction_right = self.image #holds right image
self.direction_left = pygame.transform.flip(self.image, True, False) #holds left image
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#UI
self.health = 100
self.visable = True
self.sb = Scoreboard(self)
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#bullet
self.bullets = pygame.sprite.Group()
self.current_direction = self.direction_right
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#asteroids
self.asteroids = pygame.sprite.Group()
self.next_object_time = 0
self.time_interval = 250
#self._create_asteroids()
#--------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------
#button
self.game_active = False
self.play_button = Button(self, "Play")
#--------------------------------------------------------------------------------------------
def _create_asteroids(self):
""" create the asteroid shower """
#create a asteroid
number_of_aliens = 1
for asteroid_num in range(number_of_aliens):
self._create_asteroid()
def _create_asteroid(self):
asteroid = Asteroid(self)
if asteroid.x <= 0:
asteroid.direction = 1
elif asteroid.x >= 1160:
asteroid.direction = -1
self.asteroids.add(asteroid)
def move(self):
""" move tnak tank_speed based on direction of movement (key pressed)
also detect collision """
if self.tank_moving_right and self.rect.right < self.screen_rect.right:
self.x += self.tank_speed
self.rect.x = self.x
if self.tank_moving_left and self.rect.left > self.screen_rect.left:
self.x -= self.tank_speed
self.rect.x = self.x
if self.tank_moving_down and self.rect.bottom < self.screen_rect.bottom:
self.y += self.tank_speed
self.rect.y = self.y
if self.tank_moving_up and self.rect.top > self.screen_rect.top:
self.y -= self.tank_speed
self.rect.y = self.y
def draw_healthbar(self):
pygame.draw.rect(self.screen, (255,0,0), (self.rect.x, self.rect.y - 20, 100, 10))
pygame.draw.rect(self.screen, (0,255,0), (self.rect.x, self.rect.y - 20, 100 - (100 - self.health), 10))
def blitme(self):
""" draw the image of the tank """
self.screen.blit(self.image, self.rect)
def tank_asteroid_collision(self):
#get rid of asteroids that collide with tank
for asteroid in self.asteroids.copy():
if pygame.Rect.colliderect(self.rect, asteroid.rect):
if self.health > 25:
self.health -= 25
self.asteroids.remove(asteroid)
print(self.health)
else:
self._tank_death()
self.sb.reset_score()
self.game_active = False
pygame.mouse.set_visible(True)
def _update_screen(self):
""" update screen """
self.screen.fill(self.bg_color)
self.blitme()
for bullet in self.bullets.sprites():
self.bullets.draw(self.screen)
collisions = pygame.sprite.groupcollide(self.bullets, self.asteroids, True, True)
if collisions:
for asteroids in collisions.values():
self.sb.score += 100 * len(asteroids)
self.sb.prep_score()
self.sb.check_high_score()
#draw healthbar if game active
if self.game_active:
self.draw_healthbar()
self.asteroids.draw(self.screen)
#draw the play button and other buttons if game is paused
if not self.game_active:
self.play_button.draw_button()
self.sb.show_score()
pygame.display.flip()
def _tank_death(self):
sleep(0.5)
self.bullets.empty()
self.asteroids.empty()
self.center_ship()
self.health = 100
def _check_KEYDOWN(self, event):
""" when key is press either quit, or move direction of arrow pressed and flip image """
if event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_RIGHT:
self.tank_moving_right = True
self.image = self.direction_right
self.current_direction = self.direction_right
elif event.key == pygame.K_LEFT:
self.tank_moving_left = True
self.image = self.direction_left
self.current_direction = self.direction_left
elif event.key == pygame.K_UP:
self.tank_moving_up = True
elif event.key == pygame.K_DOWN:
self.tank_moving_down = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_KEYUP(self, event):
""" when key is let go stop moving """
if event.key == pygame.K_RIGHT:
self.tank_moving_right = False
elif event.key == pygame.K_LEFT:
self.tank_moving_left = False
elif event.key == pygame.K_UP:
self.tank_moving_up = False
elif event.key == pygame.K_DOWN:
self.tank_moving_down = False
def _fire_bullet(self):
""" create a bullet and add it to the bullets group """
if self.current_direction == self.direction_left:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = False
new_bullet.bullet_shootLeft = True
#change direction of bullet starting point
new_bullet.x -= 100
#add bullet to sprite list
self.bullets.add(new_bullet)
elif self.current_direction == self.direction_right:
#create new bullet and set bullet path
new_bullet = Bullet(self)
new_bullet.bullet_shootRight = True
new_bullet.bullet_shootLeft = False
#add bullet to sprite list
self.bullets.add(new_bullet)
def center_ship(self):
""" centers the ship in middle of screen """
self.rect.center = self.screen_rect.center
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def _update_tank(self):
""" move tank or quit game based on keypress """
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_KEYDOWN(event)
elif event.type == pygame.KEYUP:
self._check_KEYUP(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self, mouse_pos):
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
self.next_object_time += self.time_interval
self._create_asteroids()
def run_game(self):
""" loops the game/ updates screen/ checks for key clicks"""
while True:
self._update_tank()
if self.game_active:
self.move()
self.bullets.update()
self.tank_asteroid_collision()
#after certain number of time create a asteroid
self._update_asteroids()
self.asteroids.update()
#get rid of asteroids that have disapeared
for asteroid in self.asteroids.copy():
if asteroid.x >= 1200 or asteroid.x <= 0:
self.asteroids.remove(asteroid)
#get rid of bullets that have disapeared
for bullet in self.bullets.copy():
if bullet.rect.left >= 1200 or bullet.rect.right <= 0:
self.bullets.remove(bullet)
self._update_screen()
class Bullet(Sprite):
""" A class to manage bullets fired from the ship """
def __init__(self, game):
""" create a bullet object at the ships current position """
super().__init__()
self.screen = game.screen
self.bullet_speed = 2.0
self.bullet_width = 20
self.bullet_height = 5
self.bullet_color = (0, 200, 200)
self.bullet_shootRight = False
self.bullet_shootLeft = False
self.image = pygame.Surface((self.bullet_width, self.bullet_height))
self.image.fill(self.bullet_color)
self.rect = self.image.get_rect()
self.rect.midright = game.rect.midright
self.rect.y -= 5 #the tanks barrel is 5 pixels above center
self.rect.x += 15
#store the bullets position as a decimal value
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self):
""" move the bullet up the screen """
#update the decimal position of the bullet.
if self.bullet_shootRight:
self.x += self.bullet_speed
self.rect.x = self.x
elif self.bullet_shootLeft:
self.x -= self.bullet_speed
self.rect.x = self.x
class Asteroid(Sprite):
""" a class that represents a single asteroid """
def __init__(self, game):
""" initialize the asteroid and set its starting position """
super().__init__()
self.screen = game.screen
self.speed = 0.1
self.direction = 1
#load the asteroid image onto the screen
self.image = pygame.image.load('images/asteroid.gif')
self.rect = self.image.get_rect()
#start each new asteroid in a random part just outside the map on either the left or right
self.rect.x = random.randint(*random.choice([(0, 0), (1160, 1160)]))
self.rect.y = random.randint(0, 760)
#store the asteroid's exact horizontal positon
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def update(self):
""" move the asteroid to the right or left """
self.x += self.speed * self.direction
self.rect.x = self.x
self.y += (self.speed / 3) * self.direction
self.rect.y = self.y
class Button:
def __init__(self, game, msg):
""" Initialize button """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
#set the dimensions and properties of the button
self.width, self.height = 200, 50
self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
#Build the button's rect object and center it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
self._prep_msg(msg)
def _prep_msg(self, msg):
""" Turn msg into a rendered image and center text on the button """
self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
""" draw blank button then draw message """
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
class Scoreboard:
""" A class to report scoring information """
def __init__(self, game):
""" initialize scorekeeping attributes """
self.screen = game.screen
self.screen_rect = self.screen.get_rect()
self.score = 0
self.highscore = 0
# Font settings for scoring information
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
self.prep_score()
self.prep_highscore()
def reset_score(self):
self.score = 0
self.prep_score()
def prep_highscore(self):
""" Turn the high score into a rendered image. """
high_score = round(self.highscore, -1)
high_score_str = "{:,}".format(high_score)
self.high_score_image = self.font.render(high_score_str, True, self.text_color, (255, 255, 255))
#center the high score at the top of the screen
self.high_score_rect = self.high_score_image.get_rect()
self.high_score_rect.centerx = self.screen_rect.centerx
self.high_score_rect.top = self.score_rect.top
def check_high_score(self):
""" check to see if there's a new high score """
if self.score > self.highscore:
self.highscore = self.score
self.prep_highscore()
def prep_score(self):
""" Turn the score into a renered image """
rounded_score = round(self.score, -1)
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str, True, self.text_color, (255, 255, 255))
#Display the score at the top of the screen
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right
self.score_rect.top = 0
def show_score(self):
""" draw the score to the screen """
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
if __name__ == '__main__':
a = Game()
a.run_game()
The spawning of the asteroids depends on the next_object_time. next_object_time is initialized with 0. You need to set self.next_object_time when the play button is pressed:
class Game(Sprite):
# [...]
def _check_play_button(self, mouse_pos):
if self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
pygame.mouse.set_visible(False)
self.next_object_time = pygame.time.get_ticks() + self.time_interval
To make the algorithm more robust you can set next_object_time depending on the current time when an asteroid spawns (this is optional):
class Game(Sprite):
# [...]
def _update_asteroids(self):
""" afer 'x' milliseconds create a new asteroid """
current_time = pygame.time.get_ticks()
if current_time > self.next_object_time:
# self.next_object_time += self.time_interval
self.next_object_time = current_time + self.time_interval
self._create_asteroids()

Display "You Win" when I reach a certain Point in python using Pygame

I want to know how to Display a "You Win" and a picture at the end of the game when my player reaches 2000 score in the game. I also want to randomly display a hint when the player collides with the class Reseta. Below is my current code. Please be patient with me. Thank you!
import pygame
import os
import random
pygame.init()
pygame.display.set_caption("Chimera")
SCREEN_HEIGHT = 576
SCREEN_WIDTH = 936
SCREEN = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
RUNNING = [pygame.transform.scale(pygame.image.load("images/Main1_side_right.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_1.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_2.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_3.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_4.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_5.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_6.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Main1_side_right_7.png"), (64,64))]
JUMPING = pygame.transform.scale(pygame.image.load("images/Main1_jump_right.png"), (64,64))
DUCKING = [pygame.transform.scale(pygame.image.load("images/slide3.png"), (64, 64)),
pygame.transform.scale(pygame.image.load("images/slide3.png"), (64,64))]
TREE = [pygame.transform.scale(pygame.image.load("images/Tree_1.png"), (64,140)),
pygame.transform.scale(pygame.image.load("images/Tree_1.png"), (64,140)),
pygame.transform.scale(pygame.image.load("images/Tree_2.png"), (64,140))]
BOX = [pygame.transform.scale(pygame.image.load("images/box1.png"), (110,90)),
pygame.transform.scale(pygame.image.load("images/box2.png"), (110,90)),
pygame.transform.scale(pygame.image.load("images/box3.png"), (110,90))]
SHADOW = [pygame.transform.scale(pygame.image.load("images/Enemy_1.png"), (64,64)),
pygame.transform.scale(pygame.image.load("images/Enemy_2.png"), (64,64)),]
PORTAL = [pygame.transform.scale(pygame.image.load("images/portal_real.png"), (64,128)),
pygame.transform.scale(pygame.image.load("images/portal_real.png"), (64,128)),
pygame.transform.scale(pygame.image.load("images/portal_real.png"), (64,128))]
RESETA = [pygame.transform.scale(pygame.image.load("images/reseta_real.png"), (45,120)),
pygame.transform.scale(pygame.image.load("images/reseta_real.png"), (45,120)),
pygame.transform.scale(pygame.image.load("images/reseta_real.png"), (45,120))]
DRUG = [pygame.transform.scale(pygame.image.load("images/Drug.png"), (45,90)),
pygame.transform.scale(pygame.image.load("images/Drug.png"), (45,90)),
pygame.transform.scale(pygame.image.load("images/Drug.png"), (45,90))]
STANDING = pygame.transform.scale(pygame.image.load("images/Main1_front.png"), (64,64))
BG = pygame.image.load(os.path.join("images", "Background_2.jpg"))
class Boy:
X_POS = 80
Y_POS = 390
Y_POS_DUCK = 430
JUMP_VEL = 8.5
def __init__(self):
self.duck_img = DUCKING
self.run_img = RUNNING
self.jump_img = JUMPING
self.boy_duck = False
self.boy_run = True
self.boy_jump = False
self.step_index = 0
self.jump_vel = self.JUMP_VEL
self.image = self.run_img[0]
self.boy_rect = self.image.get_rect()
self.boy_rect.x = self.X_POS
self.boy_rect.y = self.Y_POS
def update(self, userInput):
if self.boy_duck:
self.duck()
if self.boy_run:
self.run()
if self.boy_jump:
self.jump()
if self.step_index >= 10:
self.step_index = 0
if userInput[pygame.K_UP] and not self.boy_jump:
self.boy_duck = False
self.boy_run = False
self.boy_jump = True
elif userInput[pygame.K_DOWN] and not self.boy_jump:
self.boy_duck = True
self.boy_run = False
self.boy_jump = False
elif not (self.boy_jump or userInput[pygame.K_DOWN]):
self.boy_duck = False
self.boy_run = True
self.boy_jump = False
def duck(self):
self.image = self.duck_img[self.step_index // 5]
self.boy_rect = self.image.get_rect()
self.boy_rect.x = self.X_POS
self.boy_rect.y = self.Y_POS_DUCK
self.step_index += 1
def run(self):
self.image = self.run_img[self.step_index // 5]
self.boy_rect = self.image.get_rect()
self.boy_rect.x = self.X_POS
self.boy_rect.y = self.Y_POS
self.step_index += 1
def jump(self):
self.image = self.jump_img
if self.boy_jump:
self.boy_rect.y -= self.jump_vel * 4
self.jump_vel -= 0.8
if self.jump_vel < - self.JUMP_VEL:
self.boy_jump = False
self.jump_vel = self.JUMP_VEL
def draw(self, SCREEN):
SCREEN.blit(self.image, (self.boy_rect.x, self.boy_rect.y))
class Obstacle:
def __init__(self, image, type):
self.image = image
self.type = type
self.rect = self.image[self.type].get_rect()
self.rect.x = SCREEN_WIDTH
def update(self):
self.rect.x -= game_speed
if self.rect.x < -self.rect.width:
obstacles.pop()
def draw(self, SCREEN):
SCREEN.blit(self.image[self.type], self.rect)
class Box(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 380
class Tree(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 325
class Shadow(Obstacle):
def __init__(self, image):
self.type = 0
super().__init__(image, self.type)
self.rect.y = 390
self.index = 0
def draw(self, SCREEN):
if self.index >= 9:
self.index = 0
SCREEN.blit(self.image[self.index//5], self.rect)
self.index += 1
class Drug(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 325
class Portal(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 300
class Reseta(Obstacle):
def __init__(self, image):
self.type = random.randint(0, 2)
super().__init__(image, self.type)
self.rect.y = 350
def main():
global game_speed, x_pos_bg, y_pos_bg, points, obstacles
run = True
clock = pygame.time.Clock()
player = Boy()
# cloud = Cloud()
game_speed = 10
x_pos_bg = 0
y_pos_bg = 0
points = 0
font = pygame.font.Font('freesansbold.ttf', 20)
obstacles = []
death_count = 0
def score():
global points, game_speed
points += 1
if points % 500 == 0:
game_speed += 1
text = font.render("Points: " + str(points), True, (0, 0, 0))
textRect = text.get_rect()
textRect.center = (850, 30)
SCREEN.blit(text, textRect)
def background():
global x_pos_bg, y_pos_bg
image_width = BG.get_width()
SCREEN.blit(BG, (x_pos_bg, y_pos_bg))
SCREEN.blit(BG, (image_width + x_pos_bg, y_pos_bg))
if x_pos_bg <= -image_width:
SCREEN.blit(BG, (image_width + x_pos_bg, y_pos_bg))
x_pos_bg = 0
x_pos_bg -= game_speed
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
SCREEN.fill((255, 255, 255))
userInput = pygame.key.get_pressed()
background()
player.draw(SCREEN)
player.update(userInput)
if len(obstacles) == 0:
if random.randint(0, 2) == 0:
obstacles.append(Box(BOX))
elif random.randint(0, 2) == 1:
obstacles.append(Tree(TREE))
elif random.randint(0, 2) == 2:
obstacles.append(Shadow(SHADOW))
elif random.randint(0, 2) == 0:
obstacles.append(Portal(PORTAL))
elif random.randint(0, 2) == 0:
obstacles.append(Reseta(RESETA))
elif random.randint(0, 2) == 0:
obstacles.append(Drug(DRUG))
for obstacle in obstacles:
obstacle.draw(SCREEN)
obstacle.update()
if player.boy_rect.colliderect(obstacle.rect):
pygame.time.delay(2000)
death_count += 1
menu(death_count)
score()
clock.tick(30)
pygame.display.update()
def menu(death_count):
global points
run = True
while run:
# SCREEN.fill((255, 255, 255))
SCREEN.blit(BG, (0,0))
font = pygame.font.Font('freesansbold.ttf', 30)
if death_count == 0:
text = font.render("Press any Key to Start", True, (250, 245, 225))
save = font.render("Score 1000 to save the Girl", True, (250, 245, 225))
saveRect = save.get_rect()
saveRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50)
SCREEN.blit(save, saveRect)
elif death_count > 0:
text = font.render("Press any Key to Restart", True, (250, 245, 225))
score = font.render("Your Score: " + str(points), True, (250, 245, 225))
scoreRect = score.get_rect()
scoreRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50)
SCREEN.blit(score, scoreRect)
textRect = text.get_rect()
textRect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
SCREEN.blit(text, textRect)
SCREEN.blit(STANDING, (SCREEN_WIDTH // 2 - 20, SCREEN_HEIGHT // 2 - 140))
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
if event.type == pygame.KEYDOWN:
main()
menu(death_count=0)
below is the file for the folder of images used in my game.
https://drive.google.com/file/d/1t_kDNw3G1Q6X4KKZ9IfsCmYKpEecz9Ci/view?usp=sharing
There is a very simple way that you can have a "You Win" show up in your game when the score reaches 200. You would need a variable for the score that increases every time something happens. First, you would have to load an image for the "You Win" by doing pygame.image.load(file name.png) then you would have to assign variables for the x-axis and y-axis of the image (I used You_WinX and You_WinY for the example) then you can simply do this:
if score == 2000:
You_WinX = (location on x-axis)
You_WinY = (location in y-axis)
What this would do is when the score reaches the value of 2000, the image of "You Win" will appear where you want it to be on the screen (x,y)
I created a little exemple for you. The only thing you have to remember is the purpose of the method main_loop() contained in my class Game (comments will help you to understand it). Just remember that you add several while loops that depends on a variable you will change in order to change state of the game. Each loop print out different things according to what is its goal.
For the point to reach, just create an invisible rect and check if your player rect collide with the invisible one. If it does, switch your variables in order to get into another loop that will print out something else than your game (in this case you could print out "You won".
For the score, you could add score every time the player does something and check every frame if the score exceed the score you have set to win. In this case, just switch variables in order to change state of the game.
import pygame
# creating screen
screen = pygame.display.set_mode((1000, 500))
class Player(pygame.sprite.Sprite):
"""Defining a little class just to show the player on the screen"""
def __init__(self):
# initialize super class (pygame.sprite.Sprite)
super().__init__()
# defining rect of the player
self.rect = pygame.Rect((100, 100), (100, 100))
self.color = (255, 0, 0)
def move_right(self):
self.rect.x += 20
def show_player(self):
pygame.draw.rect(screen, self.color, self.rect)
class Game:
"""Defining the game class that contains the main loop and main variables"""
def __init__(self):
# defining variables for game loops
self.running = True
self.end_menu = False
self.game = True
# initialising player class
self.pl = Player()
# creating an invisible rect that the player has to reach
self.reach_point = pygame.Rect((500, 100), (100, 100))
def main_loop(self):
"""The goal here is to create several loops. One will be for the main game loop that will contains
the two other loops (as many as you want), when an event you chose will happens, the variable that makes
the loop run will turn on false and the variable that makes the loop of the other stage you want to reach
will turn on true. So you will constantly run a loop because your main loop will end up only if you quit the
game."""
while self.running:
while self.game:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
self.game = False
if event.type == pygame.KEYDOWN:
# defining just a key for the example
if event.key == pygame.K_d:
self.pl.move_right()
# detecting if the player reach the invisible rect
if self.pl.rect.colliderect(self.reach_point):
self.game = False
self.end_menu = True
# fill the screen in white
screen.fill((255, 255, 255))
# shows the player
self.pl.show_player()
# update the screen
pygame.display.flip()
while self.end_menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
self.end_menu = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p:
self.game = True
self.end_menu = False
# fills the screen in order to see the different states of the game
screen.fill((255, 255, 0))
# update the screen
pygame.display.flip()
# initializing the class game
game = Game()
pygame.init()
# calling function `main_loop()` contained in the class Game (initialised as game)
game.main_loop()
pygame.quit()

pygame sprite.Group - passing arguments out of a Group

I'm trying out pygame, using Sprites & Groups, and getting myself a little confused around passing arguments.
Basically I have a wrapper class MyGame() as my_game(). In that I have pygame.sprite.Group() - all_sprites.
I then have a class Alien(pygame.sprite.Sprite) for my aliens, and I add each alien to my all_sprites Group.
I want my Group of aliens to track across the screen (which they do) and then all drop as a block, a group (which they don't).
I can loop through my all_sprites.Group in my main loop after the self.all_sprites.update() and see if alien.rect.right > WIDTH or alien.rect.left < 0 , then loop through the Group again calling alien.end_row() then break the loop so it doesn't run for each alien on the screen edge, but that seems very clunky.
I've tried setting a flag in MyGame self.alien_drop = False but when I try to set my_game.alien_drop = True in the Alien class, it doesn't recognise my_game - not defined. I'm a little confused as MyGame is creating the instances of Alien, so they should be enclosed by the scope of MyGame?
I can pass MyGame self.alien_drop into my Alien init() but when I update the Alien self.alien_drop it doesn't update MyGame.alien_drop. Which I get because it's created a new variable local to Alien.
There doesn't seem to be a way to pass an argument through Group.update() which I guess is because it's just calling the .update() on all Sprite inside the group. I can't see an easy way to modify the Group.update() function so that I can pass values, and in fairness I probably don't want to go mucking around in there anyway.
I also can't return True back through update().
I'm kinda stuck at this point...
I know the self.aliens.row_end() probably won't work, I'll have to loop through self.aliens and call each alien.row_end(), but at the moment it's not even getting to that point.
import pygame
WIDTH = 360 # width of our game window
HEIGHT = 480 # height of our game window
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
class MyGame():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.all_sprites = pygame.sprite.Group()
self.alien_drop = False
for row_index in range(4):
for column_index in range(6):
alien = Alien(20 + (column_index * 40), 20 + (row_index *40))
alien.add(self.all_sprites)
while True:
self.screen.fill(BLACK)
self.all_sprites.update()
if self.alien_drop:
self.aliens.row_end()
self.all_sprites.draw(self.screen)
pygame.display.update()
self.clock.tick(60)
class Alien(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((25, 25))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.image.fill(GREEN)
self.dx = 1
def update(self):
self.rect.x += self.dx
if self.rect.right >= WIDTH or self.rect.left <= 0:
my_game.alien_drop = True
def row_end(self):
self.dx *= -1
self.rect.y += 40
if __name__ == "__main__":
my_game = MyGame()
So to do the traditional Space Invaders move and drop, the entire row drops when any of the invaders hits either side.
In the example below I have modified the Alien.update() to detect when the screen-side is hit, but if-so, only set a Boolean flag Alien.drop to True.
The algorithm becomes: First move all the aliens. Next, check if any alien hit the side-wall by checking this flag. If so, move every alien down 1 step & stop checking.
The Alien.row_end() function moves the aliens down. It also needs to clear the Alien.drop flag, so they don't all move down again.
import pygame
WIDTH = 360 # width of our game window
HEIGHT = 480 # height of our game window
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
class MyGame():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.all_sprites = pygame.sprite.Group()
self.alien_drop = False
for row_index in range(4):
for column_index in range(6):
alien = Alien(20 + (column_index * 40), 20 + (row_index *40))
alien.add(self.all_sprites)
exiting = False
while not exiting:
# Handle events
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
exiting = True # exit this loop
self.screen.fill(BLACK)
self.all_sprites.update()
for alien1 in self.all_sprites:
if ( alien1.shouldDrop() ):
### If any alien drops, we all drop
for alien2 in self.all_sprites:
alien2.row_end()
break # only drop once
if self.alien_drop:
self.aliens.row_end()
self.all_sprites.draw(self.screen)
pygame.display.update()
self.clock.tick(60)
class Alien(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((25, 25))
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.image.fill(GREEN)
self.dx = 1
self.drop = False # should I drop-down a level
def update(self):
self.rect.x += self.dx
if self.rect.right >= WIDTH or self.rect.left <= 0:
self.drop = True
else:
self.drop = False
def shouldDrop( self ):
""" In this alien in a position where it should drop down
one row in the screen-space (i.e.: it's hit a side) """
return self.drop
def row_end(self):
self.dx *= -1
self.rect.y += 40
self.drop = False # drop drop again just yet
if __name__ == "__main__":
my_game = MyGame()

Categories

Resources