Pyganim Help in Sidescroller Game - python

I know how to use Pyganim, it's fairly straightforward. But my problem lies with how I build my game, it starts pyganim's animation 60 times a second, therefore it never animates at all (according to the human eye). I need help in terms of how do I tell if an animation is currently playing or not, then call the play or not afterwards?
My code:
class gameStart(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.background = pygame.image.load("BG" + str(level) + ".png")
self.player = pygame.image.load("2_scaled.png")
self.icon = pygame.image.load("1_scaled.png")
self.background_S = pygame.transform.scale(self.background, (width, height)) #Scale the background to match the screen resolution
screen.blit(self.background_S, (0,0))
screen.blit(self.player, (0, height/2))
screen.blit(self.icon, (0,0))
self.position = self.player.get_rect()
self.health = 100
#Setup the players Idle, Attack, Attack 2 and Death Animations
self.PlayerIdleAnim = pyganim.PygAnimation([('2_scaled.png', 1), ('3_scaled.png', 1), ('4_scaled.png', 1), ('5_scaled.png', 1), ('6_scaled.png', 1)])
updateDisplay()
def move(self):
global rightPressed
global leftPressed
global facing
global frame
leftPressed = False
rightPressed = False
if keyPressed(K_a):
leftPressed = True
elif keyPressed(K_d):
rightPressed = True
elif not keyPressed(K_a):
leftPressed = False
elif not keyPressed(K_d):
rightPressed = False
if rightPressed and (self.position.x < width - 200):
self.position.x += moveSpeed
screen.blit(self.background_S, (0,0))
self.PlayerIdleAnim.stop()
screen.blit(self.player, (self.position.x, height/2))
if not facing:
self.player = pygame.transform.flip(self.player, True, False)
facing = True
updateDisplay()
elif leftPressed and (self.position.x > 20):
self.position.x += -moveSpeed
screen.blit(self.background_S, (0,0))
self.PlayerIdleAnim.stop()
if facing:
self.player = pygame.transform.flip(self.player, True, False)
facing = False
screen.blit(self.player, (self.position.x, height/2))
updateDisplay()
elif not leftPressed and not rightPressed:
if not facing:
self.PlayerIdleAnim.flip(True, False)
self.PlayerIdleAnim.play()
else:
self.PlayerIdleAnim.play()
"""elif rightPressed and not (self.position.x < width - 200):
rightPressed = False
elif leftPressed and not (self.position.x > 200):
leftPressed = False"""
game = gameStart()
while not gameQuit:
for event in pygame.event.get():
if event.type == QUIT:
gameQuit = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
gameQuit = True
game.move()
updateDisplay()
fpsClock.tick(fps)

The animation will never technically run, because it is always stuck on the first frame. But if you do want to find out if it will run if you give it time to switch frames just make a loop activated only when a key is pressed that lets the animation run. something like ...
def anim_test():
clock = pg.time.Clock()
while run == true:
pg.display.update()
clock.tick(60)

Related

How do I fix my pause function in pygame?

I'm just recently finished watching a tutorial / introduction to pygame and I'm trying to add some changes to it, one of them being a pause function which I've gotten to work apart from some problems e.g when I jump it sets the score to zero and while I'm in the pause screen it still increases the score, I've tried fixing the two problems and looking other things similar but can't seem to fix it.
here's the code:
import pygame
from sys import exit
from random import randint, choice
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
player_walk_1 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/Player/player_walk_1.png').convert_alpha()
player_walk_2 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/Player/player_walk_2.png').convert_alpha()
self.player_walk = [player_walk_1, player_walk_2]
self.player_index = 0
self.player_jump = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/Player/jump.png').convert_alpha()
self.image = self.player_walk[self.player_index]
self.rect = self.image.get_rect(midbottom=(80, 300))
self.gravity = 0
self.jump_sound = pygame.mixer.Sound('C:/Users/user/Documents/Dragon_Runner/audio/jump.mp3')
self.jump_sound.set_volume(0.5)
def player_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] and self.rect.bottom >= 300:
self.gravity = -22
self.jump_sound.play()
def apply_gravity(self):
self.gravity += 1
self.rect.y += self.gravity
if self.rect.bottom >= 300:
self.rect.bottom = 300
def animation(self):
if self.rect.bottom < 300:
self.image = self.player_jump
else:
self.player_index += 0.060
if self.player_index >= len(self.player_walk): self.player_index = 0
self.image = self.player_walk[int(self.player_index)]
def update(self):
self.player_input()
self.apply_gravity()
self.animation()
class Obstacle(pygame.sprite.Sprite):
def __init__(self, type):
super().__init__()
if type == 'snail':
snail_1 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/snail/snail1.png').convert_alpha()
snail_2 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/snail/snail2.png').convert_alpha()
self.frames = [snail_1, snail_2]
y_pos = 300
else:
dragon_1 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/Green Dragon1.png').convert_alpha()
dragon_2 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/Green Dragon2.png').convert_alpha()
dragon_3 = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/Green Dragon3.png').convert_alpha()
self.frames = [dragon_1, dragon_2, dragon_3]
y_pos = 190
self.animation_index = 0
self.image = self.frames[self.animation_index]
self.rect = self.image.get_rect(midbottom=(randint(900, 1100), y_pos))
def animation(self):
self.animation_index += 0.1
if self.animation_index >= len(self.frames):
self.animation_index = 0
self.image = self.frames[int(self.animation_index)]
def update(self):
self.animation()
self.rect.x -= 7
self.destroy()
def destroy(self):
if self.rect.x <= -100:
self.kill()
def display_score():
current_time = int(pygame.time.get_ticks() / 1000) - start_time
score_surf = font1.render(f'Score: {current_time}', False, 'black')
score_rect = score_surf.get_rect(center=(400, 50))
screen.blit(score_surf, score_rect)
return current_time
def collision_sprite():
if pygame.sprite.spritecollide(player.sprite, obstacle_group, False):
obstacle_group.empty()
return False
else:
return True
pygame.init()
screen = pygame.display.set_mode((800, 400))
pygame.display.set_caption('Dragon Runner')
clock = pygame.time.Clock()
font1 = pygame.font.Font('C:/Users/user/Documents/Dragon_Runner/font/Pixeltype.ttf', 50)
game_active = False
paused = False
start_time = 0
score = 0
bg_music = pygame.mixer.Sound('C:/Users/user/Documents/Dragon_Runner/audio/music.wav')
bg_music.set_volume(0.2)
bg_music.play(loops=-1)
player = pygame.sprite.GroupSingle()
player.add(Player())
obstacle_group = pygame.sprite.Group()
sky_surface = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/Sky.png').convert()
ground_surface = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/ground.png').convert()
player_stand = pygame.image.load('C:/Users/user/Documents/Dragon_Runner/graphics/player/player_stand.png').convert_alpha()
player_stand = pygame.transform.rotozoom(player_stand, 0, 2)
player_stand_rect = player_stand.get_rect(center=(400, 200))
game_name = font1.render('Dragon Runner', False, (11, 196, 169))
game_name_rect = game_name.get_rect(center=(400, 80))
game_message = font1.render('Press space to run', False, (11, 196, 169))
game_message_rect = game_message.get_rect(center=(400, 330))
obstacle_timer = pygame.USEREVENT + 1
pygame.time.set_timer(obstacle_timer, 1500)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
else:
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
game_active = True
start_time = int(pygame.time.get_ticks() / 1000)
if game_active and not paused:
if event.type == obstacle_timer:
obstacle_group.add(Obstacle(choice(['fly', 'snail', 'snail', 'snail'])))
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
paused = True
if paused:
if event.type == pygame.KEYDOWN and event.key == pygame.K_c:
paused = False
if game_active and not paused:
screen.blit(sky_surface, (0, 0))
screen.blit(ground_surface, (0, 300))
score = display_score()
player.draw(screen)
player.update()
obstacle_group.draw(screen)
obstacle_group.update()
game_active = collision_sprite()
if not game_active:
screen.fill((94, 129, 162))
screen.blit(player_stand, player_stand_rect)
score_message = font1.render(f'Your score: {score}', False, (11, 196, 169))
score_message_rect = score_message.get_rect(center = (400, 330))
screen.blit(game_name, game_name_rect)
if score == 0:
screen.blit(game_message, game_message_rect)
else:
screen.blit(score_message, score_message_rect)
if paused:
screen.fill((94, 129, 162))
screen.blit(player_stand, player_stand_rect)
pause_message = font1.render('GAME PAUSED', False, (11, 196, 169))
pause_message_rect = pause_message.get_rect(center = (400, 80))
unpause_message = font1.render("Press 'c' to continue", False, (11, 196, 169))
unpause_message_rect = unpause_message.get_rect(center = (400, 330))
screen.blit(pause_message, pause_message_rect)
screen.blit(unpause_message, unpause_message_rect)
pygame.display.update()
clock.tick(60)
You need to minus paused time from the score, which
you can create a variable to store the accumulated paused time, as follows:
Define a new variable paused_total_time:
start_time = 0
paused_total_time = 0
score = 0
When game paused, you start the pause timer:
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
paused = True
pause_start_time = int(pygame.time.get_ticks() / 1000)
When game continue, you add paused time to paused_total_time:
if paused:
if event.type == pygame.KEYDOWN and event.key == pygame.K_c:
paused = False
paused_total_time = paused_total_time + (int(pygame.time.get_ticks() / 1000) - pause_start_time)
Finally in display_score(), you can minus current_time by paused time.
def display_score():
current_time = int(pygame.time.get_ticks() / 1000) - start_time - paused_total_time
score_surf = font1.render(f'Score: {current_time}', False, 'black')
score_rect = score_surf.get_rect(center=(400, 50))
screen.blit(score_surf, score_rect)
return current_time
Edit: There is a bug that the score reduces when character jumps, it is because start_time is reset when jump is triggered, you can add a statement to check if the game is started before resetting start_time and other variables for game start
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
# check whether game is active before start game
if game_active == False:
game_active = True
start_time = int(pygame.time.get_ticks() / 1000)
paused_total_time = 0
score = 0

Pointer not moving up or down despite using earlier and already working code

been trying for a while to find the problem with the following code. I'm trying to have the pointer move up and down like the title of the question states but it just won't move. Any and all help is welcome.
Code for Pause Screen event processing:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
print("Up we go")
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
print("Down we go")
if event.key == pygame.K_RETURN:
self.selection = int(pointer.Pointer.getPosition(self.pointer))
print(str(pointer.Pointer.getPosition(self.pointer)))
return True
return False
Code for displaying which shows the pointer in the same place.
self.active_sprite_list.draw(screen)
font = pygame.font.SysFont("serif", 25)
text = []
center_x = []
center_y = []
for counter in range(1,5):
text.append(font.render(self.options[counter-1], True, constants.WHITE))
center_x.append(150)
center_y.append((counter * 120) - (text[counter-1].get_height() // 2) + (self.pointer.image.get_height() // 2))
screen.blit(text[counter-1], [center_x[counter-1],center_y[counter-1]])
pygame.display.flip()
And for reference the same code in the Menu which has the pointer moving up and down:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
if event.key == pygame.K_RETURN:
self.selection = int(pointer.Pointer.getPosition(self.pointer))
#print(str(self.selection))
return True
###Some code later###
screen.fill(constants.BLACK)
font = pygame.font.SysFont("serif", 25)
for counter in range(1,5):
text = font.render(self.options[counter-1], True, constants.WHITE)
center_x = 150
center_y = (counter * 120) - (text.get_height() // 2) + (self.pointer.image.get_height() // 2)
screen.blit(text, [center_x, center_y])
self.active_sprite_list.draw(screen)
pygame.display.flip()
And before you suggest, the screen for the pause has been declared before here:
while notPaused == False:
#print("Received")
notPaused = pause.processEvents()
print(str(notPaused))
if firstTime == True:
self.pauseScreen.fill(constants.ABLACK)
pause.displayFrame(self.pauseScreen)
self.pauseScreen.set_alpha(128)
screen.blit(self.pauseScreen, [0,0])
firstTime = False
pause.displayFrame(self.pauseScreen)
clock.tick(60)
As per requested, here is the MoveUp and MoveDown functions in the Pointer Class:
def moveUp(self):
if self.rect.y <= 120:
self.rect.y = 480
else:
self.rect.y -= 120
def moveDown(self):
if self.rect.y >= 480:
self.rect.y = 120
else:
self.rect.y += 120
And as suggested, the modular/self-contained code that can be run on its own as long as you have some kind of image in a Resources Folder next to the saved code file.
import pygame, sys
"""
Global constants
"""
# Colors
ABLACK = ( 0, 0, 0, 125)
BLACK = ( 0, 0, 0)
WHITE = ( 255, 255, 255)
BLUE = ( 0, 0, 255)
YELLOW = ( 255, 255, 0)
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
class Pointer(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Resources/Pointer.png")
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 120
def moveUp(self):
if self.rect.y <= 120:
self.rect.y = 480
else:
self.rect.y -= 120
print("Within pointer object moving up from ",self.rect.x,self.rect.y)
def moveDown(self):
if self.rect.y >= 480:
self.rect.y = 120
else:
self.rect.y += 120
print("Within pointer object moving up from ",self.rect.x,self.rect.y)
def getPosition(self):
self.position = self.rect.y / 120
return self.position
class Pause(object):
def __init__(self,screen):
self.selection = 4
self.options = ["Resume Game","Review Controls","Back to Menu","Quit"]
self.active_sprite_list = pygame.sprite.Group()
self.pointer = Pointer()
self.active_sprite_list.add(self.pointer)
def processEvents(self):
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == pygame.K_w:
self.pointer.moveUp()
print("Up we go")
if event.key == pygame.K_DOWN or event.key == pygame.K_s:
self.pointer.moveDown()
print("Down we go")
if event.key == pygame.K_RETURN:
self.selection = int(Pointer.getPosition(self.pointer))
print(str(Pointer.getPosition(self.pointer)))
return False
return True
def displayFrame(self,screen):
self.active_sprite_list.draw(screen)
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
font = pygame.font.SysFont("serif", 25)
text = []
center_x = []
center_y = []
for counter in range(1,5):
text.append(font.render(self.options[counter-1], True, WHITE))
center_x.append(150)
center_y.append((counter * 120) - (text[counter-1].get_height() // 2) + (self.pointer.image.get_height() // 2))
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
screen.blit(text[counter-1], [center_x[counter-1],center_y[counter-1]])
for pointer in self.active_sprite_list:
print("The y value of this pointer is:" + str(pointer.rect.y))
pygame.display.flip()
pygame.init()
pygame.display.set_caption("Pause Error Finder")
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
Paused = True
clock = pygame.time.Clock()
pauseScreen = pygame.Surface(size,pygame.SRCALPHA,32)
pauseScreen.fill(ABLACK)
pause = Pause(screen)
pauseScreen.set_alpha(128)
Paused = pause.processEvents()
print(str(notPaused))
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
while Paused:
notPaused = pause.processEvents()
print(str(Paused))
pause.displayFrame(pauseScreen)
#screen.blit(pauseScreen, [0,0])
clock.tick(60)
Your issue is in the main game loop, first off, you had the blitting of Pause Screen to Screen commented out. readding that in gave the pointer seeming to multiply and go all over the place (getting closer!).
The reason it does that is you did not update your pauseScreen in each pass of the loop. Your displayFrame will add your pointer to the proper location, but the one from last frame, and 2 frames ago, and... are still there. by moving the lines
pauseScreen.fill(ABLACK)
pauseScreen.set_alpha(128)
And placing them within your game loop, the pause screen is reset ever frame and only the latest pointer is displayed. Here is the updated game loop:
pygame.init()
pygame.display.set_caption("Pause Error Finder")
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
notPaused = False
clock = pygame.time.Clock()
pauseScreen = pygame.Surface(size,pygame.SRCALPHA,32)
pause = Pause(screen)
notPaused = pause.processEvents()
print(str(notPaused))
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
while not notPaused:
notPaused = pause.processEvents()
print(str(notPaused))
pauseScreen.fill(ABLACK)
pauseScreen.set_alpha(128)
pause.displayFrame(pauseScreen)
screen.blit(pauseScreen, [0,0])
clock.tick(60)

why in the main loop ('running' in this code) is not responding after clicking the 'start' button?

I am creating a basic game. I have mainly two codes for :(i) Menu (ii) Basic Game. I want to run 'while game_over' loop after clicking 'start' button. But the code does not respond after I click 'start' button.
import pygame
import random
import sys
pygame.init()
w=800
h=600
yellow=(255,255,0)
player_size=25
player_pos=[w/2,h-(2*player_size)]
enemy_size=25
enemy_pos=[random.randint(0,w-enemy_size),0]
enemy_list=[ ]
Menu_bg_color=(34,139,34)
red=(255,0,0)
blue=(0,0,125)
bright_blue=(0,0,255)
font_size=35
b1_pos=[w/2-50,h/2]
b1_size=[105,50]
bg_color=(0,0,0)
screen=pygame.display.set_mode((w,h))
speed=10
score=0
clock=pygame.time.Clock()
myFont=pygame.font.SysFont("monospace",font_size)
Menu_myFont=pygame.font.SysFont("freesansbold.tff",font_size)
running=True
Menu_running=True
#GAME CODE
def GameCode():
global game_over
global score
global speed
global player_pos
def set_level(score,speed):
if score<10:
speed=5
elif score<20:
speed=6
elif score<30:
speed=8
elif score<40:
speed=10
elif score<50:
speed=13
elif score<200:
speed=15
else:
speed=20
return speed
def drop_enemies(enemy_list):
delay=random.random()
if len(enemy_list)<6 and delay<0.1:
x_pos=random.randint(0,w-enemy_size)
y_pos=0
enemy_list.append([x_pos,y_pos])
def draw_enemies(enemy_list):
for enemy_pos in enemy_list:
pygame.draw.rect(screen,blue, (enemy_pos[0],enemy_pos[1],enemy_size,enemy_size))
def update_enemy_pos(enemy_list,score):
for idx,enemy_pos in enumerate(enemy_list):
if enemy_pos[1]>=0 and enemy_pos[1]<h:
enemy_pos[1]+=speed
else:
enemy_list.pop(idx)
score+=1
return score
def detect_collision(player_pos,enemy_pos):
p_x=player_pos[0]
p_y=player_pos[1]
e_x=enemy_pos[0]
e_y=enemy_pos[1]
if (e_x>=p_x and e_x<(p_x+player_size)) or (p_x>=e_x and p_x<(e_x+enemy_size)):
if (e_y>=p_y and e_y<(p_y+player_size)) or (p_y>=e_y and p_y<(e_y+enemy_size)):
return True
return False
def collision_check(enemy_list,player_pos):
for enemy_pos in enemy_list:
if detect_collision(enemy_pos,player_pos):
return True
return False
while game_over==False:
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
if event.type==pygame.KEYDOWN:
x=player_pos[0]
y=player_pos[1]
if event.key==pygame.K_LEFT:
x-=player_size
elif event.key==pygame.K_UP:
y-=player_size
elif event.key==pygame.K_RIGHT:
x+=player_size
elif event.key==pygame.K_DOWN:
y+=player_size
player_pos=[x,y]
screen.fill(bg_color)
#screen.blit(road,(0,0))
drop_enemies(enemy_list)
score=update_enemy_pos(enemy_list,score)
speed=set_level(score,speed)
text='Your Score is:' + str(score)
label=myFont.render(text,1,yellow)
screen.blit(label,(w/2,h-40))
if collision_check(enemy_list,player_pos):
game_over=True
break
draw_enemies(enemy_list)
pygame.draw.rect(screen,red,(player_pos[0],player_pos[1],player_size,player_size))
clock.tick(30)
pygame.display.update()
pygame.display.flip()
#MENU CODE
def MenuCode():
global game_over
def button(b1_pos,b1_size):
mouse_pos=pygame.mouse.get_pos()
click=pygame.mouse.get_pressed()
if (b1_pos[0]<mouse_pos[0]<(b1_pos[0]+b1_size[0])) and (b1_pos[1]<mouse_pos[1]<(b1_pos[1]+b1_size[1])):
pygame.draw.rect(screen,bright_blue,(b1_pos[0],b1_pos[1],b1_size[0],b1_size[1]))
if click[0]==1:
game_over=False
else:
pygame.draw.rect(screen,blue,(b1_pos[0],b1_pos[1],b1_size[0],b1_size[1]))
text='START'
label=Menu_myFont.render(text,1,red)
screen.blit(label,(w/2-38,h/2+5))
Menu_running=True
while Menu_running:
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
screen.fill(Menu_bg_color)
button(b1_pos,b1_size)
#button(b1_pos,b1_size,'quit')
pygame.display.update()
clock.tick(30)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
break;
screen.fill(bg_color)
if MenuCode():
if game_over==False:
GameCode()
clock.tick(30)
pygame.display.update()
Here's a substantially revised version of your code that doesn't have the problem. I have revised the code so it closely adheres to the PEP 8 - Style Guide for Python Code guidelines and also eliminated many of the global variables you had by making them local to the function that uses them.
One of the primary cosmetic things I did was determine which of the globals were unchanging constants and which were variables whose values actually changed. After doing that, I changed the names of the constant ones to all UPPERCASE (as per PEP 8) and moved those with varying values to inside whichever function was actually using them.
The most extensive changes were made to the menu handling function — now named menu_code() — which was probably the main source of your problems. To help facilitate the rewrite, I've added a MenuButton class to encapsulate their behavior to a large degree and reduce repetitious code.
Note that there may be problems with the game_code() function because because I didn't attempted to optimize, test, or debug it.
Hopefully this will provide you with a good base upon which to further develop the game. I strongly suggest you read and start following PEP 8.
import random
import pygame as pyg
W, H = 800, 600
RED = (255, 0, 0)
BLUE = (0, 0, 125)
BRIGHT_BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PLAYER_SIZE = 25
ENEMY_SIZE = 25
FONT_SIZE = 35
BG_COLOR = BLACK
FPS = 30
START, QUIT = 0, 1 # Button selection codes returned by menu_code()
pyg.init()
screen = pyg.display.set_mode((W, H))
pyg.display.set_caption('Basic Game')
clock = pyg.time.Clock()
game_font = pyg.font.SysFont("monospace", FONT_SIZE)
menu_font = pyg.font.SysFont("freesansbold.ttf", FONT_SIZE)
def game_code():
enemy_pos = [random.randint(0, W-ENEMY_SIZE), 0]
enemy_list = []
game_over = False
player_pos = [W/2, H - 2*PLAYER_SIZE]
score = 0
speed = 10
def get_speed(score):
if score < 10:
speed = 5
elif score < 20:
speed = 6
elif score < 30:
speed = 8
elif score < 40:
speed = 10
elif score < 50:
speed = 13
elif score < 200:
speed = 15
else:
speed = 20
return speed
def drop_enemies(enemy_list):
delay = random.random()
if len(enemy_list) < 6 and delay < 0.1:
x_pos = random.randint(0, W-ENEMY_SIZE)
y_pos = 0
enemy_list.append([x_pos, y_pos])
def draw_enemies(enemy_list):
for enemy_pos in enemy_list:
pyg.draw.rect(screen, BLUE,
(enemy_pos[0], enemy_pos[1], ENEMY_SIZE, ENEMY_SIZE))
def update_enemy_pos(enemy_list, score):
for idx, enemy_pos in enumerate(enemy_list):
if enemy_pos[1] >= 0 and enemy_pos[1] < H:
enemy_pos[1] += speed
else:
enemy_list.pop(idx)
score += 1
return score
def detect_collision(player_pos, enemy_pos):
p_x, p_y = player_pos
e_x, e_y = enemy_pos
if ((e_x >= p_x and e_x < (p_x + PLAYER_SIZE))
or (p_x >= e_x and p_x < (e_x + ENEMY_SIZE))):
if ((e_y >= p_y and e_y < (p_y+PLAYER_SIZE))
or (p_y >= e_y and p_y < (e_y + ENEMY_SIZE))):
return True
return False
def collision_check(enemy_list, player_pos):
for enemy_pos in enemy_list:
if detect_collision(enemy_pos, player_pos):
return True
return False
while not game_over:
for event in pyg.event.get():
if event.type == pyg.QUIT:
game_over = True
break
if event.type == pyg.KEYDOWN:
x = player_pos[0]
y = player_pos[1]
if event.key == pyg.K_LEFT:
x -= PLAYER_SIZE
elif event.key == pyg.K_UP:
y -= PLAYER_SIZE
elif event.key == pyg.K_RIGHT:
x += PLAYER_SIZE
elif event.key == pyg.K_DOWN:
y += PLAYER_SIZE
player_pos = [x, y]
screen.fill(BG_COLOR)
drop_enemies(enemy_list)
score = update_enemy_pos(enemy_list, score)
speed = get_speed(score)
text = 'Your Score is:' + str(score)
label = game_font.render(text, 1, YELLOW)
screen.blit(label, (W/2, H-40))
if collision_check(enemy_list, player_pos):
game_over = True
break
draw_enemies(enemy_list)
pyg.draw.rect(screen, RED,
(player_pos[0], player_pos[1], PLAYER_SIZE, PLAYER_SIZE))
clock.tick(FPS)
pyg.display.update()
# pyg.display.flip() # Don't do both update() and flip().
class MenuButton:
def __init__(self, text, value, rect):
self.text = text
self.value = value
self.rect = rect
def draw(self):
# Background color determined by whether mouse is positioned over label.
mouse_pos = pyg.mouse.get_pos()
fg_color = RED
bg_color = BRIGHT_BLUE if self.rect.collidepoint(mouse_pos) else BLUE
pyg.draw.rect(screen, bg_color, self.rect)
pyg.draw.rect(screen, YELLOW, self.rect, 1) # Draw a border.
label = menu_font.render(self.text, 1, fg_color)
# Center lable text inside its rectangle.
txw, txh = menu_font.size(self.text)
screen.blit(label, (self.rect.left + txw/2, self.rect.top + txh/2))
def menu_code():
MENU_BG_COLOR = (34, 139, 34)
LABEL_SIZE = LABEL_WIDTH, LABEL_HEIGHT = (105, 50)
B1_RECT = pyg.Rect((W/2 - LABEL_WIDTH/2, H/2 - LABEL_HEIGHT/2), LABEL_SIZE)
B2_RECT = pyg.Rect((W/2 - LABEL_WIDTH/2, H/2 + LABEL_HEIGHT/2), LABEL_SIZE)
MENU_BUTTONS = [MenuButton('Start', START, B1_RECT),
MenuButton('Quit', QUIT, B2_RECT)]
choice = None
while choice is None:
screen.fill(MENU_BG_COLOR)
for button in MENU_BUTTONS:
button.draw()
for event in pyg.event.get():
if event.type == pyg.QUIT:
choice = QUIT
# Handle left mouse button clicks.
if event.type == pyg.MOUSEBUTTONDOWN and event.button == 1:
for button in MENU_BUTTONS:
if button.rect.collidepoint(event.pos):
choice = button.value
break
pyg.display.update()
clock.tick(FPS)
return choice
# Main loop.
running = True
while running:
for event in pyg.event.get():
if event.type == pyg.QUIT:
running = False
break;
screen.fill(BG_COLOR)
choice = menu_code()
if choice == START:
game_code()
if choice == QUIT:
running = False
pyg.display.update()
clock.tick(FPS)

Sprite disappears after holding SPACE or DOWN when using scrolling background

When SPACE or DOWN is held, then the sprite disappears because of the scrolling background. When the background is a still image, it works. Furthermore the animation is fast so I used a timer to make it slower, but when the DOWN button is let go of, the screen speeds up.
I have tried searching up syntax online and it works when the background image is still.
from time import sleep as s
import random
import pygame
pygame.init()
import time
window = pygame.display.set_mode((1000, 500))
pygame.display.set_caption("Practice Game")
image = pygame.image.load('pixel6.png')
image = pygame.transform.scale(image, (1000, 500))
jump1 = [pygame.image.load('adventurer-jump-00.png'),pygame.image.load('adventurer-jump-01.png'),pygame.image.load('adventurer-jump-02.png'),pygame.image.load('adventurer-jump-03.png'), pygame.image.load('adventurer-smrslt-00.png'),pygame.image.load('adventurer-smrslt-01.png'),pygame.image.load('adventurer-smrslt-02.png'),pygame.image.load('adventurer-smrslt-03.png')]
run2 = [pygame.image.load('adventurer-run-00.png'), pygame.image.load('adventurer-run-01.png'),pygame.image.load('adventurer-run-02.png'),pygame.image.load('adventurer-run-03.png')]
slide1 = [pygame.image.load('adventurer-slide-00.png'),pygame.image.load('adventurer-slide-01.png'),pygame.image.load('adventurer-stand-00.png'),pygame.image.load('adventurer-stand-01.png'),pygame.image.load('adventurer-stand-02.png')]
#attack = [pygame.image.load('
imagex = 0
imagex2 = image.get_width()
clock = pygame.time.Clock()
run = True
run1 = True
jump2 = True
slide2 = True
imagess = True
x = 40
y = 391
FPS = 60
speed = 0.6
jumpcount = 10
jumpcount1 = 0
runcount = 0
slide = 0
isJump = False
down = False
class obstacles(object):
img = [pygame.image.load('img.png')]
def __init__(self, x,y, width, height):
self.x = x
self.y =y
self.width = width
self.height = height
self.hitbox = (x,y,width,height)
self.count = 0
def draw(self, window):
self.hitbox = (self.x + 5, self.y + 5, self.width, self.height)
if self.count >= 8:
self.count = 0
self.count +=1
window.blit(pygame.transform.scale(self.img[self.count//1000], (150,100)), (self.x, self.y))
pygame.draw.rect(window, (255,0,0), self.hitbox, 2)
objects = []
def keepdrawing():
global runcount, slide, run1,jumpcount1
window.blit(image, (imagex,0))
window.blit(image, (imagex2,0))
for object1 in objects:
object1.draw(window)
if runcount >= 3:
runcount = 0
if run1 == True:
window.blit(run2[runcount],(int(x),int(y)))
runcount +=1
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN and y == 391:
run1 = False
if slide >= 4:
slide = 0
if slide2:
window.blit(slide1[slide],(int(x),int(y)))
slide +=1
if event.key == pygame.K_SPACE:
run1 = False
if jumpcount1 >= 7:
jumpcount1 = 0
if jump2 and y!=391:
window.blit(jump1[jumpcount1],(int(x),int(y)))
jumpcount1 +=1
if event.type == pygame.KEYUP:
if event.key == pygame.K_DOWN:
run1 = True
if event.key == pygame.K_SPACE:
run1=True
pygame.display.update()
pygame.time.set_timer(pygame.USEREVENT+1, 500)
pygame.time.set_timer(pygame.USEREVENT+2, random.randrange(1000,2000))
obstacles = obstacles(1050,300,64,64)
while run:
clock.tick(60)
imagex -= 2
imagex2 -= 2
if imagex < image.get_width() * -1:
imagex = image.get_width()
if imagex2 < image.get_width() * -1:
imagex2 = image.get_width()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.USEREVENT+1:
speed += 1
if event.type == pygame.USEREVENT+2:
objects.append(obstacles)
for object1 in objects:
object1.x -= 1
if object1.x < -100:
objects.pop(objects.index(object1))
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if not(isJump):
if keys[pygame.K_SPACE]:
isJump =True
else:
if jumpcount >= -10:
neg=1
if jumpcount <0:
neg = -1
s(0.01)
y-= (jumpcount**2)*0.3*neg
s(0.01)
jumpcount -=1
else:
isJump = False
jumpcount = 10
keepdrawing()
I cannot test your game due to the lot of images I do not have, but I think you are misunderstanding how the event system works, or at least you have neglected a case.
When you press a key, the KEYDOWN is emitted, and when you lift the key, the KEYUP is emitted.
These event are instantaneous (well, not really, but they are very fast), and are catched by the event manager only when the status of the button changes (from pressed to unpressed and viceversa). You must not rely on them to check if a button is kept pressed. Have a look at this post for a better understanding.
With this in mind, let's see what happening to your keepdrawing function. Here the relevant parts:
def keepdrawing():
#...
if run1 == True:
#blitting
if event.type == pygame.KEYDOWN:
run1 = False
#stuff and blitting
if event.type == pygame.KEYUP:
run1 = True
As you can see, when you hold the button pressed (that is, between KEYDOWN and KEYUP events) the boolean run1 is False. So nothing is blit. That's why your image disappear.
You should still be able to see the beginning of the movement: the frame when you press the button the KEYDOWN event is catched and that part of the code is executed. But the frame later, nothing is blit because run1 is False and there is no event to be catched.
I do not have a working solution, as I said I cannot test the game, but I would suggest at least to be sure that keepdrawing always draw something. Try to figure what you should draw when run1 is False and add a couple of lines about it in keepdrawing.
As a more general advice, do not use the pygame event system in the keepdrawing function. Just check if a button is pressed like you do here:
keys = pygame.key.get_pressed()

Pygame: sound only playing 8 times, then off 4 times, then on, then off, then back on

I'm having an odd phenomenon where my sound file is playing 8 times, then off, then playing, then off, then playing again.
import sys, pygame, os, time
# Force static position of screen
os.environ['SDL_VIDEO_CENTERED'] = '1'
# to get rid of sound lag
pygame.mixer.pre_init(44100, -16, 8, 2048)
# Runs imported module
pygame.init()
# Constants
UP = 'up'
DOWN = 'down'
BOOSTSP = 12
NORMSP = 8
WIN_W = 920
WIN_H = 570
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
PADDLE_HEIGHT = 440
BALL_WIDTH = BALL_HEIGHT = 20
class Entity(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
class Paddle(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.x = x
self.y = y
self.speed = 8
self.pWidth = 20
self.score = 0
self.pHeight = PADDLE_HEIGHT
self.paddle = pygame.Surface((self.pWidth, self.pHeight))
self.paddle = self.paddle.convert()
self.lp_moveUP = self.lp_moveDOWN = self.rp_moveUP = self.rp_moveDOWN = False
def update(self, down, up):
# Adjust speed
if up or down or up or down:
if up:
self.y -= self.speed
if down:
self.y += self.speed
# paddle movement
if self.y < 0:
self.y = 0
if self.y > WIN_H - self.pHeight:
self.y = WIN_H - self.pHeight
class Ball(Entity):
def __init__(self, x, y):
Entity.__init__(self)
self.speed = [-5, -5]
self.ball = pygame.Surface((BALL_WIDTH, BALL_HEIGHT))
self.ball = self.ball.convert()
self.rect = pygame.Rect(WIN_W/2, WIN_H/2-(BALL_HEIGHT/2), BALL_WIDTH, BALL_HEIGHT)
def restart(self, lPaddle, rPaddle, screen):
time.sleep(2)
self.rect.y = WIN_H/2-(BALL_HEIGHT/2)
self.rect.x = WIN_W/2
lPaddle.y = (WIN_H/2) - (PADDLE_HEIGHT/2)
rPaddle.y = (WIN_H/2) - (PADDLE_HEIGHT/2)
return True
Notice that in the 'update' method, I am playing a sound if the ball hits the paddle. You can see that I print the return value of the 'playSound' function which is the return value of the play() method.
def update(self, lPaddle, rPaddle, sound, playSound):
# If ball hits the top or bottom
if self.rect.top < 0 or self.rect.top > WIN_H - BALL_HEIGHT:
self.speed[1] = -self.speed[1]
print playSound(sound["bop"], 1)
# If ball hits paddle
if(self.speed[0] < 0):
if (self.rect.left > lPaddle.x + lPaddle.pWidth - 15 and self.rect.left < lPaddle.x + lPaddle.pWidth-10) and (self.rect.top > lPaddle.y and self.rect.top < (lPaddle.y + lPaddle.pHeight)):
self.speed[0] = -self.speed[0]
print playSound(sound["beep"], 1)
else:
if (self.rect.left > rPaddle.x - 15 and self.rect.left < rPaddle.x - 5) and (self.rect.top > rPaddle.y and self.rect.top < (rPaddle.y + rPaddle.pHeight)):
self.speed[0] = -self.speed[0]
print playSound(sound["beep"], 1)
self.rect = self.rect.move(self.speed)
# Returns True for .5 seconds, then False for .5 seconds.
def checkTime(cur, beg):
return (cur - beg) % 1000 < 500
# Takes in string, (x, y) and size. Returns text and rect.
def txtRect(sen, xpos, ypos, size):
phrase = pygame.font.Font(None, size)
phrase = phrase.render(sen, 1, BLACK)
phraseRect = phrase.get_rect()
phraseRect.x = xpos
phraseRect.y = ypos
return phrase, phraseRect
# Loads sound files
def playSound(sound, volume):
sound.set_volume(volume)
return sound.play()
# Loads sound files
def loadSound():
sound = {}
sound["beep"] = pygame.mixer.Sound("sound/beep.ogg")
sound["boom"] = pygame.mixer.Sound("sound/boom.ogg")
sound["bop"] = pygame.mixer.Sound("sound/bop.ogg")
sound["choose"] = pygame.mixer.Sound("sound/choose.ogg")
sound["count"] = pygame.mixer.Sound("sound/count.ogg")
sound["end"] = pygame.mixer.Sound("sound/end.ogg")
sound["music"] = pygame.mixer.Sound("sound/music.ogg")
sound["select"] = pygame.mixer.Sound("sound/select.ogg")
return sound
def main():
size = WIN_W, WIN_H
fps = 60
# Used for count down
countDown = 3
decrement = True
pygame.display.set_caption('Pong')
screen = pygame.display.set_mode(size, pygame.SRCALPHA)
# Create our objects
ball = Ball((WIN_W/2)-(BALL_WIDTH/2), WIN_H/2-(BALL_HEIGHT/2))
lPaddle = Paddle(WIN_W/15, (WIN_H/2)-(PADDLE_HEIGHT/2))
rPaddle = Paddle(WIN_W/1.1, (WIN_H/2)-(PADDLE_HEIGHT/2))
# Create sound objects
sound = loadSound()
clock = pygame.time.Clock()
beg_time = pygame.time.get_ticks()
intro = count = play = outro = True
lp_moveUP = lp_moveDOWN = rp_moveDOWN = rp_moveUP = False
while intro:
# Print background
screen.fill(WHITE)
# Title Text: Pong
text = txtRect("Pong", 0, 0, 200)
text = txtRect("Pong", WIN_W/2-(text[1].width/2), WIN_H/4, 200)
screen.blit(text[0], text[1])
# Blinking Text: Click here to start
text = txtRect("- Click here to start -", 0, 0, 50)
text = txtRect("- Click here to start -", WIN_W/2-(text[1].width/2), WIN_H/1.7, 50)
if checkTime(beg_time, pygame.time.get_ticks()):
screen.blit(text[0], text[1])
# Checks if window exit button pressed
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN or pygame.key.get_pressed()[pygame.K_RETURN] != 0:
screen.blit(text[0], text[1])
pygame.display.flip()
playSound(sound["select"], .3)
pygame.time.wait(1500)
intro = False
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
# Gameplay
while play:
# Print background
screen.fill(WHITE)
screen.blit(lPaddle.paddle, (lPaddle.x, lPaddle.y))
screen.blit(rPaddle.paddle, (rPaddle.x, rPaddle.y))
screen.blit(ball.ball, ball.rect)
# Print Score
sen = "Player 1 score: " + str(lPaddle.score)
text = txtRect(sen, WIN_W/6.5, WIN_H/57, 40)
screen.blit(text[0], text[1])
sen = "Player 2 score: " + str(rPaddle.score)
text = txtRect(sen, WIN_W - WIN_W/6.5 - text[1].width, WIN_H/57, 40)
screen.blit(text[0], text[1])
# Countdown
if count:
text = txtRect(str(countDown), 0, 0, 75)
text = txtRect(str(countDown), WIN_W/3.5 - (text[1].width/2), WIN_H/4, 75)
screen.blit(text[0], text[1])
text = txtRect(str(countDown), WIN_W/1.4 - (text[1].width/2), WIN_H/4, 75)
screen.blit(text[0], text[1])
# Writes to main surface
pygame.display.flip()
playSound(sound["count"], 1)
time.sleep(1)
countDown -= 1
# bug fix: prevent display of 0.
if countDown == 0:
count = False
# Gameplay
else:
# Checks if window exit button pressed
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
# Keyboard mechanics
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == pygame.K_UP:
rp_moveUP = True
rp_moveDOWN = False
elif event.key == pygame.K_DOWN:
rp_moveUP = False
rp_moveDOWN = True
if event.key == pygame.K_w:
lp_moveUP = True
lp_moveDOWN = False
elif event.key == pygame.K_s:
lp_moveUP = False
lp_moveDOWN = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
rp_moveUP = False
elif event.key == pygame.K_DOWN:
rp_moveDOWN = False
if event.key == pygame.K_w:
lp_moveUP = False
elif event.key == pygame.K_s:
lp_moveDOWN = False
lPaddle.update(lp_moveDOWN, lp_moveUP)
rPaddle.update(rp_moveDOWN, rp_moveUP)
ball.update(lPaddle, rPaddle, sound, playSound)
# If ball moves off the screen
if ball.rect.left < 0 - ball.rect.width or ball.rect.left > WIN_W + ball.rect.width:
if ball.rect.left < 0:
rPaddle.score += 1
elif ball.rect.left > WIN_H + ball.rect.width:
lPaddle.score += 1
playSound(sound["end"], 1)
count = ball.restart(lPaddle, rPaddle, screen)
countDown = 3
# Game ends
if lPaddle.score == 3 or rPaddle.score == 3:
playSound(sound["boom"], 1)
break
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
# Gameplay
while outro:
# Print background
screen.fill(WHITE)
# End Text: Player wins
if lPaddle.score == 3 or rPaddle.score == 3:
if lPaddle.score == 3:
sen = "Player 1 Wins!"
else:
sen = "Player 2 Wins!"
text = txtRect(sen, 0, 0, 40)
text = txtRect(sen, WIN_W/2-(text[1].width/2)-130, WIN_H/4, 100)
screen.blit(text[0], text[1])
text = txtRect("- Click here to continue -", 0, 0, 50)
text = txtRect("- Click here to continue -", WIN_W/2-(text[1].width/2), WIN_H/1.7, 50)
# Blinking Text: Click here to start
if checkTime(beg_time, pygame.time.get_ticks()):
screen.blit(text[0], text[1])
# Checks if window exit button pressed
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN or pygame.key.get_pressed()[pygame.K_RETURN] != 0:
screen.blit(text[0], (WIN_W/2-(text[1].width/2), WIN_H/1.7))
pygame.display.flip()
loadSound("choose", 1)
pygame.time.wait(1500)
outro = False
main()
# Limits frames per iteration of while loop
clock.tick(fps)
# Writes to main surface
pygame.display.flip()
if __name__ == "__main__":
main()
When I play, the intro sounds play and then when the ball bounces off the paddles, the program only plays 8 times with the exact same result every time I play the game. Here is my output. Any help would be much appreciated.
/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /Users/DanielLee/PycharmProjects/newPong/pong7_sound.py
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
None
None
None
None
Channel object at 0x1002c42e8
None
Channel object at 0x1002c42e8
Channel object at 0x1002c42e8
The reason I believe the sound was playing then not playing had something to do with the channel being used repeatedly. The solution was to set a 'maxtime' for the sound, turning it off and freeing up the channel for later use.
from documentation: play(loops=0, maxtime=0, fade_ms=0)
As can be seen in the documentation for play(), the second argument is 'maxtime' which can be set to ensure that channels will be available.
I don't have a clear understanding of the mechanics of how this works exactly or even if my understanding of the error is valid, so any clarification would be appreciated.

Categories