I need help. Im new on coding, so I've developed a game with pygame. It's a game where you fight as a robot against a zombie. If a fireball collides with the zombie, the heart picture will be updated from filled to half and so on.
The Tech-Lead said that this code is not efficient because of the many if statements in the def hearts() method in the Enemy class.
Could you please help me to shorten it? I have absolutely 0 idea what I could do. Thinking about loops, but dont know how to do it. Please help me
Here is my code:
#pygame importieren
import pygame
#pygame initialisieren
pygame.init()
#Konstante für die Farben initialisieren
WHITE = [0xFF, 0xFF, 0xFF]
ORANGE = [0xFF, 0x8C, 0x00]
RED = [0xFF, 0x00, 0x00]
GREEN = [0x00, 0xFF, 0x00]
BLUE = [0x00, 0x00, 0xFF]
BLACK = [0x00, 0x00, 0x00]
#Hintergrundbilder und Sounds einer Variablen zuweisen
background = pygame.image.load("Grafiken/hintergrund.jpg")
attackLeft = pygame.image.load("Grafiken/angriffLinks.png")
attackRight = pygame.image.load("Grafiken/angriffRechts.png")
jumping = pygame.image.load("Grafiken/sprung.png")
going_right = [pygame.image.load("Grafiken/rechts1.png"), pygame.image.load("Grafiken/rechts2.png"), pygame.image.load("Grafiken/rechts3.png"), pygame.image.load("Grafiken/rechts4.png"), pygame.image.load("Grafiken/rechts5.png"), pygame.image.load("Grafiken/rechts6.png"), pygame.image.load("Grafiken/rechts7.png"), pygame.image.load("Grafiken/rechts8.png")]
going_left = [pygame.image.load("Grafiken/links1.png"), pygame.image.load("Grafiken/links2.png"), pygame.image.load("Grafiken/links3.png"), pygame.image.load("Grafiken/links4.png"), pygame.image.load("Grafiken/links5.png"), pygame.image.load("Grafiken/links6.png"), pygame.image.load("Grafiken/links7.png"), pygame.image.load("Grafiken/links8.png")]
jumping_sound = pygame.mixer.Sound("Sound/sprung.wav")
shooting_sound = pygame.mixer.Sound("Sound/shoot.mp3")
#Fenster erstellen
screen = pygame.display.set_mode([1280, 800])
#Fenstertitel erstellen
pygame.display.set_caption("Mein erstes pygame-Spiel")
#Spielerklasse
class Spieler:
def __init__(self, x, y, speed, jump, player_width, player_height, direction, steps_left, steps_right):
self.x = x
self.y = y
self.speed = speed
self.jump = jump
self.player_width = player_width
self.player_height = player_height
self.direction = direction
self.steps_left = steps_left
self.steps_right = steps_right
#Springt er?
self.sprung = False
#Anfangsposition des Spielers
self.lastPosition = [1, 0]
#Schießbereitschaft
self.ready2shoot = True
#Eine Methode zum nach links und nach rechts laufen
def walking(self, liste):
if liste[0]:
#Index 0 des Arrays direction ist Links. Wenn nach Links, dann Position des Spielers auf x - Geschwindigkeit
self.x -= self.speed
self.direction = [1, 0, 0, 0]
self.steps_left = 1
if liste[1]:
#Index 1 des Arrays direction ist Rechts. Wenn nach Rechts, dann Position des Spielers auf x Geschwindigkeit
self.x = self.speed
self.direction = [0, 1, 0, 0]
self.steps_right = 1
#Schritte werden zurückgesetzt
def resetsteps(self):
self.steps_left = 0
self.steps_right = 0
#Index 3 des Arrays direction ist stehen. Wenn stehen, dann werden die Schritte zurückgesetzt
def standing(self):
self.direction = [0, 0, 1, 0]
self.resetsteps()
#Sprungmethode wenn die springen-Taste gedrückt wird
def sprungsetzen(self):
#Wenn der SPieler -16px springen will, ist die Sprungbereitschaft True.
if self.jump == -16:
self.sprung = True
self.jump = 15
pygame.mixer.Sound.play(jumping_sound)
#Berechnung des Sprunges
def springen(self):
if self.sprung:
self.direction = [0, 0, 0, 1]
if self.jump >= -15:
n = 1
if self.jump < 0:
n = - 1
self.y -= (self.jump**2)*0.15*n
self.jump -= 1
else:
self.sprung = False
#Die Erstellung des Spielers
def spZeichnen(self):
if self.steps_left == 63:
self.steps_left = 0
if self.steps_right == 63:
self.steps_right = 0
if self.direction[0]:
screen.blit(going_left[self.steps_left//8], (self.x, self.y))
self.lastPosition = [1, 0]
if self.direction[1]:
screen.blit(going_right[self.steps_right//8], (self.x, self.y))
self.lastPosition = [0, 1]
if self.direction[2]:
if self.lastPosition[0]:
screen.blit(attackLeft, (self.x, self.y))
else:
screen.blit(attackRight, (self.x, self.y))
if self.direction[3]:
screen.blit(jumping, (self.x, self.y))
class Enemy:
def __init__(self, x, y, speed, player_width, player_height, direction, xMin, xMax):
self.x = x
self.y = y
self.speed = speed
self.player_width = player_width
self.player_height = player_height
self.direction = direction
self.xMin = xMin
self.xMax = xMax
self.steps_left = 0
self.steps_right = 0
self.health = 12
self.left_walk = [pygame.image.load("Grafiken/l1.png"), pygame.image.load("Grafiken/l2.png"), pygame.image.load("Grafiken/l3.png"), pygame.image.load("Grafiken/l4.png"), pygame.image.load("Grafiken/l5.png"),
pygame.image.load("Grafiken/l6.png"), pygame.image.load("Grafiken/l7.png"), pygame.image.load("Grafiken/l8.png")]
self.right_walk = [pygame.image.load("Grafiken/r1.png"), pygame.image.load("Grafiken/r2.png"), pygame.image.load("Grafiken/r3.png"), pygame.image.load("Grafiken/r4.png"), pygame.image.load("Grafiken/r5.png"),
pygame.image.load("Grafiken/r6.png"), pygame.image.load("Grafiken/r7.png"), pygame.image.load("Grafiken/r8.png")]
self.health_full = pygame.image.load("Grafiken/voll.png")
self.health_half = pygame.image.load("Grafiken/halb.png")
self.health_zero = pygame.image.load("Grafiken/leer.png")
def hearts(self):
if self.health >= 2:
screen.blit(self.health_full, (440, 75))
if self.health >= 4:
screen.blit(self.health_full, (500, 75))
if self.health >= 6:
screen.blit(self.health_full, (560, 75))
if self.health >= 8:
screen.blit(self.health_full, (620, 75))
if self.health >= 10:
screen.blit(self.health_full, (680, 75))
if self.health == 12:
screen.blit(self.health_full, (740, 75))
if self.health == 1:
screen.blit(self.health_half, (440, 75))
if self.health == 3:
screen.blit(self.health_half, (500, 75))
if self.health == 5:
screen.blit(self.health_half, (560, 75))
if self.health == 7:
screen.blit(self.health_half, (620, 75))
if self.health == 9:
screen.blit(self.health_half, (680, 75))
if self.health == 11:
screen.blit(self.health_half, (740, 75))
if self.health == 0:
screen.blit(self.health_zero, (440, 75))
if self.health <= 2:
screen.blit(self.health_zero, (500, 75))
if self.health <= 4:
screen.blit(self.health_zero, (560, 75))
if self.health <= 6:
screen.blit(self.health_zero, (680, 75))
if self.health <= 8:
screen.blit(self.health_zero, (680, 75))
if self.health <= 10:
screen.blit(self.health_zero, (740, 75))
def enemy_zeichnen(self):
if self.steps_left == 63:
self.steps_left = 0
if self.steps_right == 63:
self.steps_right = 0
if self.direction[0]:
screen.blit(self.left_walk[self.steps_left//8], (self.x, self.y))
if self.direction[1]:
screen.blit(self.right_walk[self.steps_right//8], (self.x, self.y))
def enemy_walking(self):
self.x = self.speed
if self.speed < 0:
self.direction = [1, 0]
self.steps_left = 1
if self.speed > 0:
self.direction = [0, 1]
self.steps_right = 1
def enemy_back_forth(self):
if self.x > self.xMax:
self.speed *= -1
elif self.x < self.xMin:
self.speed *= -1
self.enemy_walking()
class FireBall:
def __init__(self, spX, spY, fb_Direction, fb_radius, fb_color, speed):
#Der Feuerball befindet sich auf der selben Position wie der Spieler
self.x = spX
self.y = spY
#Feuerballrichtung nach Links, befindet sich immer 5px vom Spieler entfernt und bewegt sich *1,5 des speeds in - y-Richtung (-1 * speed)
if fb_Direction[0]:
self.x = 5
self.speed = - 1 * speed * 1.5
#Feuerballrichtung nach Rechts, befindet sich immer 90px vom Spieler entfernt und bewegt sich * 1,5 des speeds in y-Richtung
elif fb_Direction[1]:
self.x = 90
self.speed = speed * 1.5
#Feuerball ist 90px unter dem Spieler
self.y = 90
self.fb_radius = fb_radius
self.fb_color = fb_color
#Bewegung des Feuerballs
def move(self):
self.x = self.speed
#Zeichnung des Feuerballs
def fb_Zeichnen(self):
pygame.draw.circle(screen, self.fb_color, (self.x, self.y), self.fb_radius, 0)
#Methode um Zeichnungen anzuzeigen
def show_player():
screen.blit(background, (0,0))
for f in fireball:
f.fb_Zeichnen()
spieler1.spZeichnen()
zombie.hearts()
zombie.enemy_zeichnen()
zombie.hearts()
pygame.display.flip()
#Verhalten vom Feuerball
def feuerkugeln():
for f in fireball:
if f.x >= 0 and f.x <= 1270:
f.move()
else:
fireball.remove(f)
def collision():
global fireball, win, loose, game_active
zombieRect = pygame.Rect(spieler1.x 18,spieler1.y 36,spieler1.player_width-36, spieler1.player_height-36)
player_rect = pygame.Rect(zombie.x 18,zombie.y 24,zombie.player_width-36, zombie.player_height-24)
for f in fireball:
fbRect = pygame.Rect(f.x-f.radius,f.y-f.radius,f.radius*2,f.radius*2)
if zombieRect.colliderect(fbRect):
fireball.remove(f)
zombie.health -= 1
if zombie.health <=0 and not verloren:
gewonnen = True
game_active = False
if zombieRect.colliderect(player_rect):
loose = True
won = False
game_active = False
#Grenzen erstellen
left_border = pygame.draw.rect(screen, BLACK, [0, 0, 2, 800], 0)
right_border = pygame.draw.rect(screen, BLACK, [1225, 0, 2, 800], 0)
#Klasse aufrufen
spieler1 = Spieler(300, 575, 5, -16, 30, 50, [0, 0, 1, 0], 0, 0)
zombie = Enemy(600, 575, 8, 30, 50, [0,0], 0, 1200)
loose = False
win = False
#fireball Array. Wieviele fireballs auf einmal auf dem Spielfeld sein können
fireball = []
#Bedingung für den Spielstart
game_active = True
#Bildschirmaktualisierung einstellen
clock = pygame.time.Clock()
#Hauptschleife des Spiels
while game_active:
#Nutzeraktion überprüfen
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_active = False
player_rect = pygame.Rect(spieler1.x, spieler1.y, spieler1.player_width, spieler1.player_height)
pressed = pygame.key.get_pressed()
#Spielelogik wird berechnet (Bewegung der Figur, Kollision etc)
#Ereignis wenn man sich nach links bewegt und auf die Grenze stößt
if pressed[pygame.K_LEFT] and not player_rect.colliderect(left_border):
spieler1.walking([1, 0])
#Ereignis wenn man sich nach rechts bewegt und auf die Grenze stößt
elif pressed[pygame.K_RIGHT] and not player_rect.colliderect(right_border):
spieler1.walking([0, 1])
#Ereignis wenn keine Taste gedrückt wird
else:
spieler1.standing()
#Ereignis wenn die Sprung-Taste gedrückt wird
if pressed[pygame.K_UP]:
spieler1.sprungsetzen()
#Ereignis wenn die Space-Taste gedrückt wird
if pressed[pygame.K_SPACE]:
#Solange 5 oder weniger Feuerbälle im fireball Array sind und der Spieler schießt, nimmt der Array um einen Index zu.
if len(fireball) <= 4 and spieler1.ready2shoot:
fireball.append(FireBall(spieler1.x, spieler1.y, spieler1.lastPosition, 8, RED, 7))
pygame.mixer.Sound.play(shooting_sound)
spieler1.ready2shoot = False
if not pressed[pygame.K_SPACE]:
spieler1.ready2shoot = True
feuerkugeln()
#Sprungvorgang
spieler1.springen()
#Die Anzeige vom Spieler
show_player()
zombie.hearts()
#Die Anzeige vom Zombie
zombie.enemy_back_forth()
collision()
#Framerate des Spiels
clock.tick(60)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_active = False
show_player()
pygame.quit()
CodePudding user response:
The tech-lead is wrong: your code is perfectly efficient the way it is written. Making the code shorter does not make it faster or more "elegant".
However, shorter code can be easier to maintain and change. Your code is fine as long as the number of heart containers is always exactly 12. But if you want to change that (to increase/decrease the difficultly of the game, or let the player get new heart containers) then this code won't work. It is hard-coded to work with exactly 12 heart containers only.
To change this, put this repetitive code in a loop. You'll need to look at the pattern of how the numbers change and create a small math formula for it. I've come up with the following. (I've also added constants instead of the integer literals, so that the code is easier to read and change.)
MAX_HEALTH = 12
LEFTMOST_HEART = 440
TOPMOST_HEART = 75
HEART_HALF_WIDTH = 30
def hearts(self):
# Draw all the full hearts:
for i in range(0, self.health, 2):
screen.blit(self.health_full(LEFTMOST_HEART (i * HEART_HALF_WIDTH), TOPMOST_HEART))
# Draw the half-heart, if needed:
if self.health % 2 == 1:
screen.blit(self.health_half(LEFTMOST_HEART ((i - 1) * HEART_HALF_WIDTH), TOPMOST_HEART))
# Draw the empty hearts:
for i in range(MAX_HEALTH, self.health, -2):
screen.blit(self.health_zero(LEFTMOST_HEART ((i - 2) * HEART_HALF_WIDTH), TOPMOST_HEART))
This code works with any number of health (as long as it's an even number). You can also change the placement of the hearts by changing LEFMOST_HEART
and TOPMOST_HEART
.
This code is just as "efficient" as your code, but it is easier to maintain because changing the max health or position of the hearts only requires changing some constants.