Preamble: I feel I probably have wasted so much time for a simple situation...
Now, I am making a game with pygame, and at some point, I wanted to split files into two, namely main.py
and configurations.py
in order to make it more readable.
Everything was going good, until I ran into this problem.
I will share whole code at the bottom, but I want to summarize first:
Now first of all, in main.py
, I am importing by,
from configurations import *
now, the game loop on the main.py
depends on the variable running
by
while running:
.......
.......
.......
And the variable running
is initialized in configurations.py
by,
# initialize some variables
running = True
So, the main.py
has to be getting variable running
because it does not give any error and uses it in while running
statement.
In the main loop, there is a section where I check for events as follows,
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
This part works just as expected, it alters variable running and program gets out of the while loop.
Now, here comes the problematic part.
In one of the classes(Player class) there is a method as decrease_HP
,
def decrease_HP(self):
self.HP -= 1
print("-1 HP", "Current HP:", self.HP)
if self.HP <= 0:
running = False
However, the point I could not figure out is that, it is not changing running variable properly, and game never stops(gets out of while loop). Here is one example output which shows the problem.
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
-1 HP Current HP: 2
-1 HP Current HP: 1
-1 HP Current HP: 0
-1 HP Current HP: -1
-1 HP Current HP: -2
-1 HP Current HP: -3
-1 HP Current HP: -4
-1 HP Current HP: -5
-1 HP Current HP: -6
So, I hope I could make it clear. I probably have a misunderstanding about importing variables or variable scopes.
By the way, I have tried adding global running
above the running = False
statement in Player.decrease_HP
function.
Thanks in advance.
Exact codes in the files
main.py
# Pygame template - skeleton for a new pygame project
from configurations import *
# initiate some variables
max_bullet = 10
# initialize pygame and create window
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("CORONA RACE")
clock = pygame.time.Clock()
player = Player()
all_sprites.add(player)
# initialize some variables
running = True
# have to use this because, otherwise, for the first SPACE key pressing, the newest_bullet is not defined yet.
newest_bullet = Bullet(0, 0)
# Game loop
while running:
# keep loop running at the right speed
clock.tick(FPS)
# Process input (events)
for event in pygame.event.get():
# check for closing window
if event.type == pygame.QUIT:
running = False
else:
pass
while len(mobs) != 5:
m = Mob()
all_sprites.add(m)
mobs.add(m)
keystate = pygame.key.get_pressed()
player.speedx = 0
if keystate[pygame.K_RIGHT]:
player.speedx += player.SPEED
if keystate[pygame.K_LEFT]:
player.speedx -= player.SPEED
if keystate[pygame.K_SPACE] and player.rect.top - newest_bullet.rect.bottom > BULLET_H + MARGIN and not len(bullets) >= max_bullet:
newest_bullet = player.shoot()
# BULLET_H refers to height of the bullet and margin refers to the minimum allowable margin between two consequent b
# If there are more than 10 bullets at a time on the screen, then no more new bullets can be fired.
if keystate[pygame.K_ESCAPE]:
running = False
if random.randint(0, 14530) > 14470:
power_up = PowerUp()
all_sprites.add(power_up)
powerups.add(power_up)
hits = pygame.sprite.spritecollide(player, powerups, True)
for pu in hits:
power_up_funcs[pu.type](player)
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
for m in hits:
pass
hits = pygame.sprite.spritecollide(player, mobs, True)
if hits:
player.decrease_HP()
# print(player.HP)
# Update
all_sprites.update()
# Draw / render
screen.fill(WHITE)
all_sprites.draw(screen)
# *after* drawing everything, flip the display
pygame.display.flip()
pygame.quit()
raise SystemExit # to exit python
configurations.py
import pygame
import random
# define constants
WIDTH = 600
HEIGHT = 960
FPS = 30
BULLET_H = 24
BULLET_W = 8
POWERUP_H = 30
POWERUP_W = 30
MOB_W = 50
MOB_H = 80
MARGIN = 10
# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
MAGENTA = (255, 0, 255)
CYAN = (0, 255, 255)
# create sprite groups
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
powerups = pygame.sprite.Group()
mobs = pygame.sprite.Group()
# initialize some variables
running = True
# player sprite
class Player(pygame.sprite.Sprite):
SPEED = 15
def __init__(self):
super().__init__()
# pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((100, 150))
self.image.fill(CYAN)
pygame.draw.circle(self.image, RED, (50, 75), 15, 5)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 5
self.speedx = 0
self.HP = 3
def update(self):
self.rect.x += self.speedx
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
return bullet # I need this to set the margin in continious fire.
def change_color(self):
pass
def increase_HP(self):
if self.HP <= 2:
self.HP += 1
print("+1 HP", "Current HP:", self.HP)
else:
print("HP IS ALREADY FULL", "Current HP:", self.HP)
def decrease_HP(self):
self.HP -= 1
print("-1 HP", "Current HP:", self.HP)
if self.HP <= 0:
running = False
class Mob(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((MOB_W, MOB_H))
self.image.fill(MAGENTA)
self.rect = self.image.get_rect()
self.rect.left = random.randint(0, WIDTH - POWERUP_W)
self.rect.bottom = random.randint(-2 * MOB_H, 0)
def update(self):
self.rect.y += 6
if self.rect.top > HEIGHT:
self.kill()
# Bullet sprite
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((BULLET_W, BULLET_H))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.bottom = y
self.speedx = 0
self.speedy = -20
def update(self):
self.rect.y += self.speedy
# kill it if it moves away from the screen
if self.rect.bottom < 0:
self.kill() # built in method of pygame.sprite
# powerup sprite
power_up_funcs = [Player.increase_HP, print] # container for to-do functs.
class PowerUp(pygame.sprite.Sprite):
SPEEDY = 8
def __init__(self):
super().__init__()
self.type = random.randint(0, 1) # [0,1] integer
if self.type == 0: # HP power up
self.image = pygame.Surface((POWERUP_W, POWERUP_H))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.left = random.randint(0, WIDTH - POWERUP_W)
# self.rect.centerx = player.rect.centerx #debug
self.rect.bottom = 0
elif self.type == 1: # shield
self.image = pygame.Surface((POWERUP_W, POWERUP_H))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.left = random.randint(0, WIDTH - POWERUP_W)
# self.rect.centerx = player.rect.centerx # debug
self.rect.bottom = 0
else:
pass
def update(self):
self.rect.y += self.SPEEDY
if self.rect.top > HEIGHT:
self.kill()