Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 years ago.
Improve this question
So the main code works like this:
import sys, pygame
from settings import Settings
from ship import Ship
from pygame.sprite import Group
import game_function as gf
def run_game():
#initialize game and create a screen object
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion 2000")
#Make ship
ship = Ship(ai_settings, screen)
#make a group to store bullets in
bullets = Group()
aliens = Group()
#create the fleet of aliens
gf.create_fleet(ai_settings, screen, aliens)
#start the main loop for game
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
Additionally I have a module called game_function.py, but the only part of game_function which is not working is these last parts def(get_number_aliens_x, create_alien and create_fleet). Which returns errors. First I thought I missed something reading the book Python Crashcourse, but I've went through the newly added code many times now with no avail. The book mentions a name 'alien_number' but I can't figure out where to place it
Question: does anybody see what's wrong, where the 'alien_number' needs to be inserted or have a answer for why the error generates?
What it's supposed to do: get a image from another file called Alien (150x150px .png) and try to see how many rows it can fill the game with:
import sys, pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to keypresses"""
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_UP:
ship.moving_up = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
#lyckas inte stänga av spelet
#elif event.key == pygame.K_q:
# sys.exit()
def check_keyup_events(event, ai_settings, screen, ship):
"""Respond to keyreleases"""
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
elif event.key == pygame.K_DOWN:
ship.moving_left = False
def check_events(ai_settings, screen, ship, bullets):
for event in pygame.event.get():
#lyckas inte stänga av spelet
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ai_settings, screen, ship)
def update_screen(ai_settings, screen, ship, aliens, bullets):
"""Update images on the screen and flip to new screen."""
screen.fill(ai_settings.bg_color)
#redraw all bullets behind ship and aliens
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
#make the most recently drawn screen visible
pygame.display.flip()
def update_bullets(bullets):
"""update position of bullets and get rid of old bullets"""
#updates bullet position
bullets.update()
#get rid of bullets that have disappeared
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def fire_bullet(ai_settings, screen, ship, bullets):
"""Fire a bullet if limit not reached yet"""
#Create a new bullet and add it to the bullet group
if len(bullets) < ai_settings.bullets_allowed:
#create a new bullet and add it to the bullet group
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the number of aliens that can fit in a row"""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def create_alien(ai_settings, screen, aliens, aliens_number):
"""Create an alien and place it in the row"""
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
aliens.add(alien)
def create_fleet(ai_settings, screen, aliens):
"""Create a full fleet of aliens"""
#create an alien and find ithe number of alien in a row
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
#create the first row of alien
for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, aliens, alien_number)
--
Traceback (most recent call last):
File "python\alien_invasion\alien_invasion.py", line 29, in <module>
run_game()
File "python\alien_invasion\alien_invasion.py", line 21, in run_game
gf.create_fleet(ai_settings, screen, aliens)
File "python\alien_invasion\game_function.py", line 94, in create_fleet
create_alien(ai_settings, screen, aliens, alien_number)
File "python\alien_invasion\game_function.py", line 81, in create_alien
alien.x = alien_width + 2 * alien_width * alien_number
NameError: name 'alien_number' is not defined
beginning of path deleted, not any concern.
Your param is named aliens_number (plural) but you're trying to use it as alien_number (singular).
def create_alien(ai_settings, screen, aliens, aliens_number):
# ...
alien.x = alien_width + 2 * alien_width * alien_number
Related
I am trying to follow along in the Python Crash Course book and create the Alien Invasion game. I've noticed that there are a bit of issues with this book but have yet to find a good answer to the issue I am having. I've gotten pretty far in the development but I am currently stuck on this AttributeError: 'Alien' object has no attribute 'draw_bullet' error, after adding in the play button. (For reference, this is roughly page 296 of the 7th edition book) Here is the error message. I'm a bit confused as the bullet attribute shouldn't be in the Alien object, but I am too new to understand where my wires are getting crossed. Any help would be appreciated. Thanks!
[Callback error message]
alien_invasion.py
import pygame
from pygame.sprite import Group
from settings import Settings
from game_stats import GameStats
from button import Button
from ship import Ship
from alien import Alien
import game_functions as gf
def run_game():
# Initialize pygame, settings, and screen object.
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Make the Play Button.
play_button = Button(ai_settings, screen, "Play")
#Create an instance to store game statistics.
stats = GameStats(ai_settings)
# Make a ship, a group of bullets, and a group of aliens.
ship = Ship(ai_settings, screen)
bullets = Group()
aliens = Group()
# Create the fleet of aliens.
gf.create_fleet(ai_settings, screen, ship, bullets)
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, stats, play_button, ship, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
gf.update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button)
run_game()
game_functions.py
import sys
from time import sleep
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
"""Fire a bullet if limit is not reached."""
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, stats, play_button, ship, bullets):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
check_play_button(stats, play_button, mouse_x, mouse_y)
def check_play_button(stats, play_button, mouse_x, mouse_y):
"""Start a new game when the player clicks Play."""
if play_button.rect.collidepoint(mouse_x, mouse_y):
stats.game_active = True
def update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button):
"""Update images on the screen and flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
# Redraw all bullets behind ship and aliens.
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# Draw the play button if the game is inactive.
if not stats.game_active:
play_button.draw_button()
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_bullets(ai_settings, screen, ship, aliens, bullets):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions.
bullets.update()
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)
def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) ==0:
#Destroy existing bullets and create new fleet.
bullets.empty()
create_fleet(ai_settings, screen, ship, aliens)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the number of aliens that fit in a row."""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def get_number_rows(ai_settings, ship_height, alien_height):
"""Determine the number of rows of aliens that fit on the screen."""
available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""Create an alien and place it in the row."""
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def create_fleet(ai_settings, screen, ship, aliens):
"""Create a full fleet of aliens."""
# Create an alien and find the number of aliens in a row.
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
# Create the first row of aliens.
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, aliens, alien_number, row_number)
def check_fleet_edges(ai_settings, aliens):
"""Respond appropriately if any aliens have reached an edge."""
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""Drop the entire fleet and change the fleet's direction."""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""Respond to ship being hit by alien."""
if stats.ships_left > 0:
# Decrement ships_left.
stats.ships_left -= 1
else:
stats.game_active = False
# Empty the list o faliens and bullets.
aliens.empty()
bullets.empty()
# Create a new fleet and center the ship.
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
# Pause.
sleep(0.5)
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
"""Check if any aliens have reached the bottom of the screen."""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# Treat this the same as if ship gets hit.
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
break
def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
"""Check if the fleet is at an edge, and update positions of all aliens in fleet."""
check_fleet_edges(ai_settings, aliens)
aliens.update()
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
print("Ship hit!!!")
# Look for aliens hitting the bottom of the screen.
check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
alien.py
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""A class to represent a single alien in the fleet."""
def __init__(self, ai_settings, screen):
"""Initialize the alien and its starting position."""
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the alien image and set its rect attribute.
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
# Start each new alien near the top left of the screen.
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# Store the alien's exact position.
self.x = float(self.rect.x)
def blitme(self):
"""Draw the alien at its current location."""
self.screen.blit(self.image, self.rect)
def check_edges(self):
"""Return true if alien is at edge of screen."""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
def update(self):
"""Move the alien right of left."""
self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x = self.x
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship."""
def __init__(self, ai_settings, screen, ship):
"""Create a bullet object at the ship's current position."""
super(Bullet, self).__init__()
self.screen = screen
# Create a bullet rect at (0,0) and then set correct position.
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# Store the bullet's position as a decimal value.
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""Move the bullet up the screen."""
# Update the decimal position of the bullet.
self.y -= self.speed_factor
#Update the rect position.
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)
the create_fleet function signature in game_function is :
def create_fleet(ai_settings, screen, ship, aliens):
while in run_game() you are passing bullets instead of alien as arg, so this should fix it :
...
# Create the fleet of aliens.
gf.create_fleet(ai_settings, screen, ship, aliens) #last parametr should be aliens
This question already has answers here:
Pygame is running slow
(1 answer)
Lag when win.blit() background pygame
(2 answers)
How Can I Improve My Terrain Generator Performance I Get 40 FPS
(1 answer)
Closed 1 year ago.
Here is the main code for the game. It's basically just a shooter that moves vertically on the left of the screen. However, once I added in the aliens, the game just slowed down a lot, and is basically unplayable. What can I do to make it faster? I believe that the issue started once I added the aliens, maybe I have included something in the While loop that makes it work too hard every single loop? Because when I delete the self.aliens.draw(self.screen) the game works perfectly fine. I also tried changing the image for the aliens and it seems to work fine, could it be that the image is too high quality??
import pygame
import sys
from shooter_bien import Shooter
from shooter_bullet import Bullet
from alien_shooter import Alien
class SidewayShooter():
"""Class to manage game."""
def __init__(self):
"""Initialize game and resources."""
pygame.init()
self.WID = 1200
self.HEI = 700
self.bg = pygame.image.load('bga.jpg')
self.screen = pygame.display.set_mode((self.WID, self.HEI))
pygame.display.set_caption("Sideways Shooter")
self.shooter = Shooter(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_grid()
def run_game(self):
"""Main loop for the game."""
while True:
self._check_events()
self._update_screen()
self._update_bullets()
def _check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
if event.key == pygame.K_UP:
self.shooter.moving_up = True
if event.key == pygame.K_DOWN:
self.shooter.moving_down = True
if event.key == pygame.K_SPACE:
self._create_bullet()
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
self.shooter.moving_up = False
if event.key == pygame.K_DOWN:
self.shooter.moving_down = False
def _create_bullet(self):
"""Create new bullet and add to group."""
bullet = Bullet(self)
self.bullets.add(bullet)
def _update_bullets(self):
"""Update bullets on screen and delete off screen."""
self.bullets.update()
for bullet in self.bullets.copy():
if bullet.rect.right >= self.WID:
self.bullets.remove(bullet)
def _create_grid(self):
"""Create alien grid."""
alien = Alien(self)
alien_width, alien_height = alien.rect.size
amount_aliens_x = (self.WID // alien_width - 5) // 2
amount_aliens_y = (self.HEI // alien_height) // 2
for row_number in range(amount_aliens_x):
for alien_number in range(amount_aliens_y):
self._create_alien(alien_number, row_number)
def _create_alien(self, alien_number, row_number):
"""Create an alien and add to grid."""
alien = Alien(self)
alien_width, alien_height = alien.rect.size
alien.rect.centerx = (row_number + 3) * alien_width * 2
alien.rect.centery = alien_number * alien_height * 2 + alien_height
self.aliens.add(alien)
def _update_screen(self):
"""Update bg and shooter."""
self.screen.blit(self.bg, (0, 0))
self.shooter.blitme()
self.shooter.update()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.aliens.draw(self.screen)
pygame.display.flip()
if __name__ == '__main__':
ss = SidewayShooter()
ss.run_game()
Below is the full code I have been working on creating a game from a book by Eric Matthes Chapter 12 and it is showing errors for line 5 and line 10:
import sys
import pygame
from bullet import Bullet
def check_keydown_events(event, ai_settings, screen, ship, bullets):
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
def fire_bullet(ai_settings, screen, ship, bullets):
"""Fire a bullet if limit not reached yet."""
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_events(ai_settings, screen, ship, bullets):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
# Move the ship to the right.
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def check_keyup_events(event, ship):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def update_screen(ai_settings, screen, ship, bullets):
"""Update images on the screen and flip to the new screen."""
# Redraw all bullets behind ship and aliens.
for bullet in bullets.sprites():
bullet.draw_bullet()
screen.fill(ai_settings.bg_color)
ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_bullets(bullets):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions.
bullets.update()
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
I think the problem is here:
def check_keydown_events(event, ai_settings, screen, ship, bullets):
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
You are using elif without if. It is invalid
Also this line if len(bullets) < ai_settings.bullets_allowed: has an indentation error.
This question already has an answer here:
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 2 years ago.
My problem with the code is that the ship's movement is strange; sometimes when I press the left or right arrow key it doesn't move the correct way. Or, the ship doesn't move at all. In this project I have the modules alien.py, alien_invasion.py, bullet.py, button.py, gamefunctions.py, game_stats.py, scoreboard.py, settings.py, and ship.py. However, I will only provide the most relevant ones.
ship.py:
import pygame
class Ship():
def __init__(self, screen, ai_settings):
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load('images/spaceship.png')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.center = float(self.rect.centerx)
self.moving_right = False
self.moving_left = False
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
self.rect.centerx = self.center
def blitme(self):
self.screen.blit(self.image, self.rect)
def center_ship(self):
self.center = self.screen_rect.centerx
game_functions.py:
import sys
import pygame
from bullet import Bullet
from alien import Alien
from time import sleep
def check_keydown_events(event, ai_settings, screen, ship, bullets):
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
def check_keyup_events(event, ship):
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
alien_invasion.py:
import sys
import pygame
import game_functions as gf
from settings import Settings
from ship import Ship
from pygame.sprite import Group
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
screen = pygame.display.set_mode((1200, 650))
pygame.display.set_caption("Alien Invasion")
while True:
gf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets)
gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
gf.update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets, play_button)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.bg_color)
ship.blitme()
screen.fill(bg_color)
run_game()
The problem is caused by multiple calls to pygame.event.get().
pygame.event.get() get all the messages and remove them from the queuepygame.event.get() get all the messages and remove them from the queue. See the documentation:
This will get all the messages and remove them from the queue. [...]
If pygame.event.get() is called in multiple event loops, only one loop receives the events, but never all loops receive all events. As a result, some events appear to be missed.
Get the events once and use them in multiple loops or pass the list or events to functions and methods where they are handled:
def check_events(event_list, ai_settings, screen, stats, play_button, ship, aliens, bullets):
for event in event_list:
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def run_game():
# [...]
while True:
event_list = pygame.event.get()
gf.check_events(event_list, ai_settings, screen, stats, play_button, ship, aliens, bullets)
# [...]
for event in event_list :
if event.type == pygame.QUIT:
sys.exit()
# [...]
Beginner here in need of help.
I have been following a Python crash course project Alien Invasion using pygame.
Everything was going well until I added a scoreboard to the game and now when I start up the game all that shows is the background screen and none of my sprites or the play button show on the screen.
However if I mouse click in the area of where the play button used to be the game starts and everything appears as normal.
I've tried retracing my steps and have been looking for the problem for hours! And can't find any difference in my code compared to the book.
Any help or advice would be great! Thanks.
Main Script
import pygame
from pygame.sprite import Group
from settings import Settings
from game_stats import GameStats
from scoreboard import Scoreboard
from button import Button
from ship import Ship
from alien import Alien
import game_functions as gf
def run_game():
# Initialize pygame, settings and screen object.
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Make the Play button.
play_button = Button(ai_settings, screen, "Play")
# Create an instance to store game statistics and create a scoreboard.
stats = GameStats(ai_settings)
sb = Scoreboard(ai_settings, screen, stats)
# Make a ship, a group of bullets, and a group of aliens.
ship = Ship(ai_settings, screen)
bullets = Group()
aliens = Group()
# Create the fleet of aliens.
gf.create_fleet(ai_settings, screen, ship, aliens)
# Start the main loop for the game.
while True:
gf.check_events(ai_settings, screen, stats, play_button, ship,
aliens, bullets)
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens,
bullets)
gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
gf.update_screen(ai_settings, screen, stats, sb, ship, aliens,
bullets, play_button)
run_game()
Game Functions
import sys
from time import sleep
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
"""Fire a bullet if limit not reached yet."""
# Create a new bullet and add it to the bullets group.
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""Respond to key releases.)"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, stats, play_button, ship, aliens,
bullets):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
check_play_button(ai_settings, screen, stats, play_button, ship,
aliens, bullets, mouse_x, mouse_y)
def check_play_button(ai_settings, screen, stats, play_button, ship, aliens,
bullets, mouse_x, mouse_y):
"""Start new game when the player clicks Play."""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:
# Reset the game statistics
ai_settings.initialize_dynamic_settings()
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
# Reset the game statistics.
stats.reset_stats()
stats.game_active = True
# Empty the list of aliens and bullets.
aliens.empty()
bullets.empty()
# Create a new fleet and center the ship.
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
def update_screen(ai_settings, screen, stats, sb, ship, aliens, bullets,
play_button):
"""Update images on the screen and flip to the new screen"""
# Redraw the screen through each pass through the loop.
screen.fill(ai_settings.bg_color)
# Redraw all bullets behind ship and aliens.
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# Draw the score information.
sb.show_score()
# Draw the play button if the game is inactive.
if not stats.game_active:
play_button.draw_button()
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_bullets(ai_settings, screen, stats, sb, ship, aliens, bullets):
"""Update the position bullets and get rid of old bullets."""
# Update bullet positions.
bullets.update()
# Get rid of bullets that have disapeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets)
def check_bullet_alien_collisions(ai_settings, screen, stats, sb, ship,
aliens, bullets):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if collisions:
stats.score += ai_settings.alien_points
sb.prep_score()
if len(aliens) == 0:
# Destroy existing bullets, speed up game, and create new fleet.
bullets.empty()
ai_settings.increase_speed()
create_fleet(ai_settings, screen, ship, aliens)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the number of aliens that fit in a row."""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def get_number_rows(ai_settings, ship_height, alien_height):
"""Determine the number of rows of aliens that fit across the screen."""
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""Create an alien and place it in the row."""
# create an alien and place it in the row.
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def create_fleet(ai_settings, screen, ship, aliens):
"""Create full fleet of aliens."""
# Create an alien and find the number of aliens in a row.
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height,
alien.rect.height)
# Create the fleet of aliens.
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
create_alien(ai_settings, screen, aliens, alien_number,
row_number)
def check_fleet_edges(ai_settings, aliens):
"""Respond appropriatly if any aliens have reached an edge."""
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""Drop the entire fleet and change the fleet's direction."""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
"""Respond to ship being hit by alien."""
if stats.ships_left > 0:
# Decrement ships_left.
stats.ships_left -= 1
# Empty the list of aliens and bullets.
aliens.empty()
bullets.empty()
# Create a new fleet and center the ship.
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
# Pause
sleep(0.5)
else:
stats.game_active = False
pygame.mouse.set_visible(True)
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
"""Check if any aliens have reached the bottom of the screen."""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# Treat this the same as if the ship got hit.
# Treat this the same as if the ship got hit.
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
break
def update_aliens(ai_settings, stats, screen, ship, aliens, bullets):
"""
Check if the fleet is at an edge,
and then update the position of all the aliens in the fleet.
"""
check_fleet_edges(ai_settings, aliens)
aliens.update()
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
# Look for aliens hitting the bottom of the screen.
check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
Settings
class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's static settings."""
# Screen Settings
self.screen_width = 1200
self.screen_height = 600
self.bg_color = (230, 230, 230)
# Ship Settings
self.ship_limit = 3
# Bullet Settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3
# Alien settings
self.fleet_drop_speed = 10
# How quickly the game speeds up
self.speedup_scale = 1.1
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
"""Initialize settings that change throuhout the game."""
self.ship_speed_factor = 1.5
self.bullet_speed_factor = 3
self.alien_speed_factor = 1
# Fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
# Scoring
self.alien_points = 50
def increase_speed(self):
"""Increase speed settings."""
self.ship_speed_factor *= self.speedup_scale
self.bullet_speed_factor *= self.speedup_scale
self.alien_speed_factor *= self.speedup_scale
Scoreboard
import pygame.font
class Scoreboard():
"""A class to report scoring information."""
def __init__(self, ai_settings, screen, stats):
"""Initialize scorekeeping attributes."""
self.screen = screen
self.screen_rect = screen.get_rect()
self.ai_settings = ai_settings
self.stats = stats
# Font settings for the scoring information.
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
# Prepare the initial score image.
self.prep_score()
def prep_score(self):
"""Turn the score into a rendered image."""
score_str = str(self.stats.score)
self.score_image = self.font.render(score_str, True, self.text_color,
self.ai_settings.bg_color)
# Display the score at the top of the screen.
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20
def show_score(self):
"""Draw score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
Game stats
class GameStats():
"""Track statistics for Alien Invasion."""
def __init__(self, ai_settings):
"""Initialize statistics."""
self.ai_settings = ai_settings
self.reset_stats()
# Start the game in an inactive state.
self.game_active = False
# High score should never be reset.
self.high_score = 0
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
Button
class Button():
def __init__(self, ai_settings, screen, msg):
"""Initialize button attributes."""
self.screen = screen
self.screen_rect = screen.get_rect()
# Set the dimensions and properties of the button.
self.width, self.height = 200, 50
self.button_color = (0, 255, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
# Build the button's rect object and center it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# The button message needs to be prepped only once.
self.prep_msg(msg)
def prep_msg(self, msg):
"""Turn msg into a rendered image and center text on the button."""
self.msg_image = self.font.render(msg, True, self.text_color,
self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
# Draw blank button then draw message.
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
Alien
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
""" A class to represent a single alien in the fleet."""
def __init__(self, ai_settings, screen):
"""Initialize the alien and set starting position."""
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the alien image and set its rect attribute.
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
# Start each new alien near the top left of the screen
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# Store the aliens exact position
self.x = float(self.rect.x)
def blitme(self):
"""Draw the alien in its current location."""
self.screen.blit(self.image, self.rect)
def check_edges(self):
"""Return True if the alien is at the edge of the screen."""
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
def update(self):
"""Move the alien right or left."""
self.x += (self.ai_settings.alien_speed_factor *
self.ai_settings.fleet_direction)
self.rect.x = self.x
Bullet
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from the ship"""
def __init__(self, ai_settings, screen, ship):
""" Create a bullet object from the ships current position"""
super(Bullet, self).__init__()
self.screen = screen
# Creat a bullet rect at (0, 0) and then set correct position.
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width,
ai_settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# Store the bullets position as a decimal value
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
"""Move the bullet up the screen."""
# Update the decimal position of the bullet.
self.y -= self.speed_factor
# Update the rect position
self.rect.y = self.y
def draw_bullet(self):
""" Draw the bullet to the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)
Ship
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""Initialize the ship and set its starting position."""
self.screen = screen
self.ai_settings = ai_settings
# Load the ship image and get its rect.
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new ship at the bottom of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ships center.
self.center = float(self.rect.centerx)
# Movement flag
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ships position based on the movement flags."""
# Update ships center value not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# Update rect object from self.center.
self.rect.centerx = self.center
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
def center_ship(self):
"""Center the ship on the screen."""
self.center = self.screen_rect.centerx
I bet stats.game_active is at fault. If that's False, nothing happens in the while loop. You're missing some relevant code, so I can't tell for sure, but it seems most likely.
I continued to the next part of the book and doing so I realised in gf.check_events() There was a word missing/not in the correct position. Upon fixing this my screen went back to normal.