Running a if statement only once in pygame
I have a running game where you can move and collect coins. For each coin I have a if statement:
if cn1+25 < x and cn1 + 50 > x:
cn1 = -1000
if cn1 < -3:
coincount += 1
textObj = INVFONT.render(str(coincount+1), True, WHITE, BLACK)
This if statement keeps happening over and over again, adding hundreds of coins to the variable coincount
. Later in my code I blit the number of coins in coincount on to the screen.
Is there a way to make sure this only runs once?
(Here's the rest of my code, ut might be helpful)
import pygame,sys,random
pygame.init()
width = 900
height = 300
screenDim = (width,height)
green=(0,255,0)
yellow=(255,255,0)
red=(255,0,0)
WHITE = (0,0,0)
BLACK = (255,255,255)
screen = pygame.display.set_mode(screenDim)
placePlatformX = 0
placePlatformY = 275
coin = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Desktop\\coin.png').convert_alpha()
coin = pygame.transform.scale(coin, (25,25))
background = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Desktop\\background.jpg').convert_alpha()
background = pygame.transform.scale(background, (width,height))
guy = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Desktop\\guy.png').convert_alpha()
guy = pygame.transform.rotate(guy,90)
cloud1 = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Downloads\\cloud.png').convert_alpha()
cloud1 = pygame.transform.scale(cloud1,(156,88))
cloud2 = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Downloads\\cloud.png').convert_alpha()
cloud2 = pygame.transform.scale(cloud1,(131,53))
cloud3 = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Downloads\\cloud.png').convert_alpha()
cloud3 = pygame.transform.scale(cloud1,(200,100))
cloud4 = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Downloads\\cloud.png').convert_alpha()
cloud4 = pygame.transform.scale(cloud1,(39,20))
bplatform = pygame.image.load('C:\\Users\\eliya_s1suf2x\\Desktop\\platform.jpg').convert_alpha()
coincount = 0
x = 450
y = -20
block = 1000
cn1 = 100
cn2 = 100
cn3 = 100
cn4 = 100
cn5 = 100
cloudmove = 20
INVFONT = pygame.font.Font('C:\\Users\\eliya_s1suf2x\\Desktop\\MINECRAFT\\Raleway-ExtraBold.ttf', 18)
textObj = INVFONT.render(str(coincount), True, WHITE, BLACK)
def update():
screen.blit(background,(0,0))
screen.blit(textObj,(860,0))
screen.blit(cloud1,(cloudmove,0))
screen.blit(cloud2,(cloudmove+120,30))
screen.blit(cloud3, (cloudmove+500,10))
screen.blit(cloud4, (cloudmove + 340,20))
screen.blit(bplatform,(placePlatformX,placePlatformY))
screen.blit(bplatform,(placePlatformX+190,placePlatformY))
screen.blit(bplatform,(placePlatformX+380,placePlatformY))
screen.blit(bplatform,(placePlatformX+570,placePlatformY))
screen.blit(bplatform,(placePlatformX+760,placePlatformY))
screen.blit(bplatform,(placePlatformX+1760,placePlatformY+100))
#block list update
screen.blit(bplatform,(block-500,190))
screen.blit(bplatform,(block,100))
screen.blit(bplatform,(block+250,200))
screen.blit(bplatform,(block+500,210))
screen.blit(bplatform,(block+830,190))
screen.blit(bplatform,(block+1000,140))
screen.blit(coin,(cn1 + 40,245))
screen.blit(coin,(cn2 + 80,245))
screen.blit(coin,(cn3 + 120,245))
screen.blit(coin,(cn4+ 160,245))
screen.blit(coin,(cn5 + 200,245))
screen.blit(coin,(830,0))
screen.blit(guy,(x,y))
#pygame.sprite.spritecollide()
standOnBrick = 255
speed = 2
gravity = 3
pygame.display.set_caption("Run")
finished = False
while finished == False:
#processing all the events
for event in pygame.event.get(): # event1, event2,..
if event.type == pygame.QUIT:
finished = True
pygame.quit()
sys.exit()
if y < standOnBrick:
y += gravity
update()
#where the block #where the block
#starts #ends
elif cn1+25 < x and cn1 + 50 > x:
cn1 = -1000
if cn1 < -3:
coincount += 1
textObj = INVFONT.render(str(coincount+1), True, WHITE, BLACK)
#WORK ON THIS
elif cn2 + 50 < x and cn2 + 100 > x:
cn2 = -1025
if cn2 < -3:
coincount += 1
print('coincount',coincount)
textObj = INVFONT.render(str(coincount+1), True, WHITE, BLACK)
elif cn3 + 75 < x and cn3 + 150 > x:
cn3 = -1050
if cn3 < -3:
coincount += 1
print('coincount',coincount)
textObj = INVFONT.render(str(coincount+1), True, WHITE, BLACK)
elif cn4 + 100 < x and cn2 + 200 > x:
cn4 = -1000
if cn4 < -3:
coincount += 1
print('coincount',coincount)
textObj = INVFONT.render(str(coincount+1), True, WHITE, BLACK)
elif cn5 + 125 < x and cn5 + 230 > x:
cn5 = -1000
if cn5 < -3:
coincount += 1
print('coincount',coincount)
print('hello')
textObj = INVFONT.render(str(coincount+1), True, WHITE, BLACK)
elif block-525 < x and block-330 > x:
#how high guy is
standOnBrick = 167
elif block-16 < x and block+180 > x:
standOnBrick = 79
elif block + 230 < x and block + 426 > x:
standOnBrick = 179
elif block + 480 < x and block + 480+196>x:
standOnBrick = 189
elif block + 810 < x and block + 1026 > x:
standOnBrick = 169
elif block + 980 < x and block +1176 > x:
standOnBrick = 119
else:
standOnBrick = 255
pressedKeys = pygame.key.get_pressed()
if pressedKeys[pygame.K_LEFT] == 1:
x -= speed
print(x)
update()
elif pressedKeys[pygame.K_RIGHT] == 1:
block -= 0.5
cn1 -= 0.5
cn2 -= 0.5
cn3 -= 0.5
cn4 -= 0.5
cn5 -= 0.5
cloudmove -= 0.2
update()
if x < 450:
cloudmove += 0.25
x+=speed
cn1 += 0.5
cn2 += 0.5
cn3 += 0.5
cn4 += 0.5
cn5 += 0.5
update()
print(x)
block += 0.5
elif pressedKeys[pygame.K_UP] == 1:
y -= 5
print(y)
update()
#bounderies
if x < 2:
x += 2.1
pygame.display.flip()#Update method/load next frame
#pygame.quit()
Solution 1:
What your code is missing is abstraction.
Instead of a bunch of screen.blit
and a lot of if
statements, use lists.
Instead of hard-coded values (like cn1+25 < x and cn1 + 50 > x
), use data structures (in your case, objects) that contain all information they need.
Here's a simple example.
Let's say we have this nice world where the clouds move on a sunny day:
import pygame
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
cloud = pygame.Surface((50, 20))
cloud.set_colorkey((11, 12, 13))
cloud.fill((11, 12, 13))
pygame.draw.ellipse(cloud, pygame.Color('white'), cloud.get_rect())
cloudmove = 20
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit()
screen.fill(pygame.Color('lightblue'))
screen.blit(cloud, (cloudmove+120,30))
cloudmove += 1
pygame.display.flip()
clock.tick(30)
But of course we want more than one single cloud. What we don't want is to duplicate code and raise the complexity. We could do something like this:
import pygame
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
cloud = pygame.Surface((50, 20))
cloud.set_colorkey((11, 12, 13))
cloud.fill((11, 12, 13))
pygame.draw.ellipse(cloud, pygame.Color('white'), cloud.get_rect())
cloud2 = pygame.Surface((50, 20))
cloud2.set_colorkey((11, 12, 13))
cloud2.fill((11, 12, 13))
pygame.draw.ellipse(cloud2, pygame.Color('white'), cloud2.get_rect())
cloud3 = pygame.Surface((50, 20))
cloud3.set_colorkey((11, 12, 13))
cloud3.fill((11, 12, 13))
pygame.draw.ellipse(cloud3, pygame.Color('white'), cloud3.get_rect())
cloudmove = 20
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit()
screen.fill(pygame.Color('lightblue'))
screen.blit(cloud, (cloudmove-120,30))
screen.blit(cloud2, (cloudmove-220,40))
screen.blit(cloud3, (cloudmove-170,50))
cloudmove += 1
pygame.display.flip()
clock.tick(30)
You can already see where it is going to end if we add a dozen objects more.
So let's try to use objects here that represent the clouds:
import pygame
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
class Cloud:
def __init__(self, x, y):
self.image = pygame.Surface((50, 20))
self.image.set_colorkey((11, 12, 13))
self.image.fill((11, 12, 13))
pygame.draw.ellipse(self.image, pygame.Color('white'), self.image.get_rect())
self.x = x
self.y = y
def update(self):
self.x += 1
if self.x > 300:
self.x = 0
clouds = [Cloud(0, 30), Cloud(100, 40), Cloud(50, 50)]
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit()
screen.fill(pygame.Color('lightblue'))
for cloud in clouds:
screen.blit(cloud.image, (cloud.x, cloud.y))
cloud.update()
pygame.display.flip()
clock.tick(30)
(no new image here because it basically looks the same)
Much better! Now, to add more clouds, we would simply add a new Cloud
instance to the clouds
list. See how we not only store the image of a cloud in the class, but also the position. And we've put the cloud behaviour (moving along the sky and start from the left once we moved off the screen) into the class.
But we can still do better! Pygame offers some handy classes and functions that we can use. Take a look at the following code:
import pygame
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
class Coin(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((44, 40))
self.image.set_colorkey((11, 12, 13))
self.image.fill((11, 12, 13))
cx, cy = self.image.get_rect().center
pygame.draw.circle(self.image, pygame.Color('grey'), (cx + 2, cy), 20)
pygame.draw.circle(self.image, pygame.Color('orange'), (cx - 2, cy), 20)
self.rect = self.image.get_rect(topleft=(x, y))
def update(self):
mouse_pos = pygame.mouse.get_pos()
if self.rect.collidepoint(mouse_pos) and pygame.mouse.get_pressed()[0]:
self.kill()
class Cloud(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((50, 20))
self.image.set_colorkey((11, 12, 13))
self.image.fill((11, 12, 13))
pygame.draw.ellipse(self.image, pygame.Color('white'), self.image.get_rect())
self.rect = self.image.get_rect(topleft=(x, y))
def update(self):
self.rect.move_ip(1, 0)
if not pygame.display.get_surface().get_rect().colliderect(self.rect):
self.rect.right = 0
stuff = pygame.sprite.Group(Cloud(0, 30), Cloud(100, 40), Cloud(50, 50),
Coin(100, 100), Coin(150, 180), Coin(80, 200))
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
quit()
screen.fill(pygame.Color('lightblue'))
stuff.draw(screen)
stuff.update()
pygame.display.flip()
clock.tick(30)
We abstracted drawing and updating our objects away by using pygame's Group
and Sprite
classes.
And see how easy it is to add new stuff to our little game. I added coins you can click wo collect, and all the behaviour of the coins is located in the Coin
class. Once you click a coin, the kill
method of the Sprite
class is called, which simply removes the object from all its groups, so it's no longer in the stuff
group, and there is no longer being drawn on the screen, and effectively removed from the game (and answering your question of "Running a if statement only once").