Just wanted a few opinions on whether this game's code is efficient, from looking at it, it does look a bit all over the place but I just don't know how to 'tidy' it up to say so. Would love to hear your opinions and even better if you could show me how it's done.
#Here i am importing all necessary modules
import math,random,pygame,sys
#This class's purpose is to keep track of the current score
class Game():
def __init__(self): #This is the class constructor
self.score=0
self.goalNumbers=0
#This is the gun's class
class Gun(pygame.sprite.Sprite): #Sprite class from the Pygame module
def __init__(self): #This is the class constructor
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("turret.png") #Loading the gun's image
self.rect = self.image.get_rect() #Getting the image's Rect
self.rect.x = 240 #Setting the rect's X position
self.rect.y = 630 #Setting the rect's Y position
#This function allows the gun to move
def moveGun(self,orientation):
if orientation=="left" and self.rect.x>5:
self.rect.x-=5
if orientation=="right" and self.rect.x<(480-self.rect.width):
self.rect.x+=5
# Set up class for bullets
class Projectile(pygame.sprite.Sprite):
def __init__(self,gun):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("bullet.png")
self.rect=self.image.get_rect()
self.rect.x=gun.rect.x+(gun.rect.width/2)-(self.rect.width/2)
self.rect.y=gun.rect.y-gun.rect.height
# Set up method to move bullets up the screen
def updateProjectile(self):
if self.rect.y>0-self.rect.height:
self.rect.y-=5
else:
self.kill()
# Set up class for fruit
class Objects(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.obj=random.randint(1,3)
if self.obj==1: imagefile="raspberry"
if self.obj==2: imagefile="strawberry"
if self.obj==3: imagefile="cherry"
self.image=pygame.image.load(imagefile+".png")
self.image=pygame.transform.rotate(self.image,-15+random.randint(0,20))
self.rect=self.image.get_rect()
self.rect.y=-0-self.rect.height
self.rect.x=(random.randint(2,44)*10)
# Set up method to enable fruit to fall down the screen
def updateProjectile(self,game):
if self.rect.y<640:
self.rect.y+=3
else:
if self.obj==1:
game.score+=10
game.goalNumbers+=1
else:
game.score-=50
self.kill()
# Set up method to update score and remove fruit when shot
def shot(self,game):
if self.obj == 1:
game.score-=50
else:
game.score+=10
self.kill()
# Initialise the game
pygame.init()
pygame.key.set_repeat(1, 20)
countFont = pygame.font.Font(None,18)
statusFont = pygame.font.Font(None,18)
bColour = (52, 152, 219)
screen = pygame.display.set_mode([480, 640])
pygame.display.set_caption(' Game')
# Create initial object instances
game=Game()
gun=Gun()
sprites=pygame.sprite.Group()
sprites.add(gun)
obstacles=pygame.sprite.Group()
projectiles=pygame.sprite.Group()
# Initialise game over flag and timer
end_game=False
clock=pygame.time.Clock()
tick=0
# Main loop starts here
while end_game!=True:
clock.tick(30)
tick+=1
screen.fill(bColour)
# Process events
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_LEFT:
gun.moveGun("left")
if event.key==pygame.K_RIGHT:
gun.moveGun("right")
if event.key==pygame.K_SPACE:
projectile=Projectile(gun)
projectiles.add(projectile)
# Move objects
for projectile in projectiles:
projectile.updateProjectile()
for obstacle in obstacles:
obstacle.updateProjectile(game)
# Add new fruit if 2 seconds has elapsed
if tick>60:
if len(obstacles)<10:
obstacle=Objects()
obstacles.add(obstacle)
tick=0
# Check for collisions
collisions=pygame.sprite.groupcollide(obstacles,projectiles,False,True)
if collisions:
for obstacle in collisions:
obstacle.shot(game)
# Update player score
scoreText=countFont.render('Score:'+str(game.score),True,(255,255,255),(52, 152, 219))
screen.blit(scoreText,(0,620))
statusText=statusFont.render('Raspberries:'+str(10-game.goalNumbers),True,(255,210,210),(52, 152, 219))
screen.blit(statusText,(0,10))
# Update the screen and check for game over
sprites.draw(screen); projectiles.draw(screen); obstacles.draw(screen)
pygame.display.flip()
if game.goalNumbers>=10:
end_game=True
# Game over: display the player's final score
scoreCountHolder=pygame.image.load("scoreframe.png")
scoreCountHolder.convert_alpha()
left=90;top=250
screen.blit(scoreCountHolder,(left,top))
countFont=pygame.font.Font(None,52)
statusText=countFont.render('Your Score:'+str(game.score),True,(52, 152, 219),(231,230,33))
screen.blit(statusText,(105,300))
pygame.display.flip()
# Wait for the player to close the game window
while True:
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
1 Answer 1
I can't really comment on the performance since I found it difficult to read, especially the spaces around the operators throughout the code.
As you mention though, I would focus first on cleanup and applying PEP 8. There are many issues with spacing around operations, spacing between/after definitions, line length, etc. That would help with the readability.
The other part that seems difficult is the "main" logic near the bottom. It is good convention to place that in a "main" function and then use the "if main" at the bottom. So you would have somethning like this:
def main():
pygame.init()
pygame.key.set_repeat(1, 20)
countFont = pygame.font.Font(None, 18)
statusFont = pygame.font.Font(None, 18)
bColour = (52, 152, 219)
...
if __name__ == '__main__':
main()
Once you have that you can look to see if any of the parts in "main" need to be broken out into functions.
On the PEP 8 side, here is an example of some of your code with Pep 8 type formatting applied so you can see the difference in how it visually looks.
""" Pygame - Shooting fruit game. """
import random
import sys
import pygame
class Game():
""" The Game class's purpose is to keep track of the current score. """
def __init__(self):
self.score = 0
self.goalNumbers = 0
class Gun(pygame.sprite.Sprite):
""" This is the gun that the user moves to shoot the fruit. """
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("turret.png")
self.rect = self.image.get_rect()
self.rect.x = 240
self.rect.y = 630
def moveGun(self, orientation):
""" Move the gun on the screen. """
if orientation == "left" and self.rect.x > 5:
self.rect.x -= 5
if orientation == "right" and self.rect.x < (480 - self.rect.width):
self.rect.x += 5
class Projectile(pygame.sprite.Sprite):
""" This is for the bullets. """
def __init__(self, gun):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bullet.png")
self.rect = self.image.get_rect()
self.rect.x = gun.rect.x + (gun.rect.width / 2) - (self.rect.width / 2)
self.rect.y = gun.rect.y - gun.rect.height
def updateProjectile(self):
""" Move bullets up the screen. """
if self.rect.y > 0 - self.rect.height:
self.rect.y -= 5
else:
self.kill()