I'm trying to make alien invasion in PyGame, When I blit the spaceship and the bullets everything works smoothly but when I try to draw the aliens from the sprite group, the game suddenly lags. I've tried using the convert() function but it still lags even after using it.
main:
import sys
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
class AlienInvasion:
"""Overall class to manage game assests and behavior"""
def __init__(self):
"""Initialize the game and create resources"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
def run_game(self):
"""Start the main loop for the game"""
while True:
clock.tick(60)
self._check_events()
self.ship.update()
self._update_bullets()
self._create_fleet()
self._update_aliens()
self._update_screen()
def _check_events(self):
#watch for keyboard and mouse events
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Respond to keypress"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_UP:
self.ship.moving_up = True
elif event.key == pygame.K_DOWN:
self.ship.moving_down = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
self.ship.firing = True
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
if len(self.bullets) < self.settings.bullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
self.bullets.update()
#delete bullets
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
def _update_aliens(self):
"""Update position of aliens"""
self.aliens.update()
def _create_fleet(self):
alien = Alien(self)
alien_width,alien_height = alien.rect.size
available_space_x = self.settings.screen_width - (2 * alien_width)
number_aliens_x = available_space_x // (2 * alien_width)
#determine number of rows that fit on the screen
ship_height = self.ship.rect.height
available_space_y = self.settings.screen_height - (3*alien_height) - ship_height
number_aliens_y = available_space_y // (2* alien_height)
for row_number in range(number_aliens_y):
for alien_number in range(number_aliens_x):
self._create_alien(alien_number, row_number)
def _create_alien(self,alien_number,row_number):
"""Create alien and place it in a row"""
alien = Alien(self)
alien_width, alien_height = alien.rect.size
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.y = alien_height + 2 * alien_width * row_number
alien.rect.x = alien.x
self.aliens.add(alien)
def _update_screen(self):
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self._create_fleet()
self.aliens.draw(self.screen)
pygame.display.flip()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game(
ALien file
this is the alien class that stores the alien. I already converted my image to a .bmp file and I've also tried using the convert() function but its still lagging.
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""class that represents a single alien in a fleet"""
def __init__(self, ai_game):
#intialize alien and set position
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
#load alien image
self.image = pygame.image.load('images/alien.bmp').convert()
self.rect = self.image.get_rect()
#start the alien at the top left of the screen
self.rect.x = self.rect.width
self.rect.y = self.rect.height
#store alien exact horizontal position
self.x = float(self.rect.x)
def update(self):
#move alien to the right
self.x += self.settings.alien_speed
self.rect.x = self.x
The issue us that you create the fleet un every frame. Hence new aliens are append to the Group in every frame and you have to draw far more aliens than necessary. Even worse, 'images/alien.bmp' is loaded in the constructor of the class Alien and thus multiple images have to be loaded in every frame:
class AlienInvasion:
# [...]
def _update_screen(self):
# [...]
self._create_fleet() # <----
self.aliens.draw(self.screen)
I recommend to load the image once, to a class variable once, when the 1st alien is constructed:
(See Class and Instance Variables)
class Alien(Sprite):
alienimage = None
"""class that represents a single alien in a fleet"""
def __init__(self, ai_game):
# [...]
#load alien image
if self.alienimage == None:
self.alienimage = pygame.image.load('images/alien.bmp').convert()
self.image = self.alienimage
# [...]
Create the fleet before the main application loop:
class AlienInvasion:
# [...]
def run_game(self):
self._create_fleet() # <---- ADD
"""Start the main loop for the game"""
while True:
clock.tick(60)
self._check_events()
self.ship.update()
self._update_bullets()
# self._create_fleet() <---- DELETE
self._update_aliens()
self._update_screen()
# [...]
def _update_screen(self):
# [...]
# self._create_fleet() <---- DELETE
self.aliens.draw(self.screen)
Related
I can't get my ship to move after adding the ship speed in the settings. I identified that the problems has to do with the code self.rect.x = self.x under the Update method of the Ship class. If I comment it out, the ships is able to move, but when I uncomment the ship stops moving. Can't figure this problem out, followed the instructions of the book and don't know where I went wrong. Thank you.
import pygame
from settings import Settings
from ship import Ship
class AleinInvasion:
"""Overallclass to maange game assets and behavior."""
def __init__(self):
"""Intialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion")
self.ship= Ship(self)
#Set background color
self.bg_color = self.settings.bg_color
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self._update_screen()
#make the most recenlty drawn screen visible
#pygame.display.flip()
def _check_events(self):
#watch for keyboard and moust events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self,event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def _check_keyup_events(self,event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
"""Updates images on the screen, and flip to the new screen"""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
#make the most recenlty drawn screen visible
pygame.display.flip()
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AleinInvasion()
ai.run_game()
class Settings:
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen Settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230,230,230)
# Ship settings
self.ship_speed = 1.5
import pygame
class Ship:
"""A class to manage the ship"""
def __init__(self,ai_game):
""" Intialize the ship and set its starting postiion."""
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_game.screen.get_rect()
# Load the ship image and get its rect.
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# Start each new ship at the bottom of the screen.
self.rect.midbottom = self.screen_rect.midbottom
# Store a decimal value for the ship's horizontal position.
self.x = float(self.rect.x)
#movement flag
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's postiion based on the movement flag"""
#UPdate the ship's x value, not the rect
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.x += self.settings.ship_speed
if self.moving_left and self.rect.left > 0:
self.rect.x -= self.settings.ship_speed
#Update rect object from self.x
self.rect.x = self.x
def blitme(self):
""" Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
This question already has answers here:
Why is nothing drawn in PyGame at all?
(2 answers)
Why is my PyGame application not running at all?
(2 answers)
Closed 1 year ago.
Hi I would be grateful for some help with why the bullets are not being fired, i followed all of the steps in the book and it doesn't work.
the bullet.py file is newly written, so is the "#bullet settings" in settings.py, and some parts have been added to alien_invasion.py. and in alien_invasion.py The "for bullet in self.bullets.sprites(): bullet.draw_bullet". isnt drawing any bullets?
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
class AlienInvasion:
def __init__(self):
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
#self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
self.bg_color = (0, 0, 50)
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
def _check_keyup_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
""" Create a new bullet and add it to the bullets group."""
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _check_keydown_events(self, event):
if event.key == pygame.K_RIGHT:
self.ship.rect2.x += 10
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_ESCAPE:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _update_screen(self):
"""update images on the screen, and flip to the new screen"""
def _check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
if event.type == pygame.KEYUP:
self._check_keyup_events(event)
for bullet in self.bullets.sprites():
bullet.draw_bullet()
def run_game(self):
while True:
self._check_events()
self.ship.update()
self.bullets.update()
self._update_screen()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
pygame.display.flip()
self.screen.fill(self.bg_color)
self.ship.blitme()
self.ship.blitme2()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
settings.py
class Settings:
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
#Ship settings
self.ship_speed = 15
#Bullet settings
self.bullet_speed = 1.0
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage blullets fired from the ship"""
def __init__(self, ai_game):
"""Create a bullet object at the ships current position."""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color
# Create a bullet rect at (0, 0) and then the correct position.
self.rect = pygame.Rect(0, 0, self.settings.bullet_width, self.settings.bullet_height)
self.rect.midtop = ai_game.ship.rect.midtop
# Store the bullets position as a decimal value.
self.y = float(self.rect.y)
def update(self):
""" Move the bullet up the screen."""
# Update the decimal position of the bullet.
self.y -= self.settings.bullet_speed
#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.py
import pygame
class Ship:
def __init__(self, ai_game):
self.screen = ai_game.screen
self.screen_rect = ai_game.screen.get_rect()
self.settings = ai_game.settings
self.image = pygame.image.load('C:/Users/Documents/Python Learning/python crash course lessons/Alien Invasion/ship.bmp')
self.image2 = pygame.image.load('C:/Users/Documents/Python Learning/python crash course lessons/Alien Invasion/ship1.bmp')
self.rect = self.image.get_rect()
self.rect2 = self.image2.get_rect()
self.rect.midbottom = self.screen_rect.midbottom
self.moving_right = False
self.moving_left = False
self.x = float(self.rect.x)
self.x2 = float(self.rect2.x)
def update(self):
"""Update the ships position based on the movement flagg."""
if self.moving_right == True:
self.rect2.x += 5
self.rect.x +=2
if self.moving_right and self.rect2.right < self.screen_rect.right:
self.x += self.settings.ship_speed
if self.moving_left == True:
self.rect2.x -= 5
self.rect.x -= 2
if self.moving_left and self.rect2.left > 0:
self.x -= self.settings.ship_speed
#Update rect object from self.x
self.rect2.x = self.x
#self.rect.x = self.x
def blitme(self):
self.screen.blit(self.image, self.rect)
def blitme2(self):
self.screen.blit(self.image2, self.rect2.move(0, 670))
You have to draw the bullets in the _update_screen method:
class AlienInvasion:
# [...]
def _update_screen(self):
for bullet in self.bullets.sprites():
bullet.draw_bullet()
You have to call _update_screen after clearing the screen and before updating the screen:
class AlienInvasion:
# [...]
def run_game(self):
while True:
self._check_events()
self.ship.update()
self.bullets.update()
self.screen.fill(self.bg_color)
self.ship.blitme()
self.ship.blitme2()
self._update_screen()
pygame.display.flip()
The start position of the bullets is wrong. Change the position in Bullet.__init__:
self.rect.midtop = ai_game.ship.rect.midtop
class Bullet(Sprite):
def __init__(self, ai_game):
# [...]
self.rect = pygame.Rect(0, 0, self.settings.bullet_width, self.settings.bullet_height)
self.rect.midtop = ai_game.ship.rect2.move(0, 670).midtop
Python Pygame issue in Alien Invasion game causing the game to keep crashing when trying to fire bullets. I just finished chapter 12 in Python Crash Course, and my ship wont fire bullets and the game crashed when pressing spacebar to fire bullets. Any help I can get in solving this issue is much appreciated. Attribute error is listed in title.
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
class AlienInvasion:
"""Overall class to manange game assests and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode(
(self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
#Set the background color.
self.bg_color = (230, 230, 230)
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_screen()
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
#print(len(self.bullets))
# Watch for keyboard and mouse events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Redraw the screen during each pass through the loop.
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
# Make the most recently drawn screen availible.
pygame.display.flip()
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
#Move th ship to the right
self.ship.rect.x += 1
def _check_keydown_events(self, event):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self, event):
"""Responds to releases."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
"""Create a new bullet and add it to the new bullets group."""
if len(self.bullets) < self.settingsbullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions.
self.bullets.update()
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
# Make the most recently drawn screen availible.
#pygame.display.flip()
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AlienInvasion()
ai.run_game()
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""A class to manage bullets fired from a ship"""
def __init__(self, ai_game):
"""Create a bullet object at the ship's current position."""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color
# Create a bullet rect at (0, 0) and then set correct position.
self.rect = pygame.Rect(0, 0, self.settings.bullet_width,
self.settings.bullet_height)
self.rect.mid_top = ai_game.ship.rect.mid_top
# Store the bullet's position at a decimal value.
self.y = float(self.rect.y)
def update(self):
"""Move the bullet up the screen."""
# Update the decimal position of the bullet.
self.y -= self.settings.bullet_speed
# 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)
settings.py
class Settings:
"""A Class to store all settings for Alien Invasion."""
def __init__(self):
"""Initializing the game's settings."""
#Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
#Ship settings
self.ship_speed = 1.5
#Bullet settings
self.bullet_speed = 1.0
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 3
ship.py
import pygame
class Ship:
"""A class to manage the ship."""
def __init__(self, ai_game):
"""Initialize the ship and set its starting position."""
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_game.screen.get_rect()
#Load the ship and imagine its rect.
self.image = pygame.image.load('images_ai/ship.bmp')
self.rect = self.image.get_rect()
#Start each new ship at the bottom of the screen.
self.rect.midbottom = self.screen_rect.midbottom
#Store a decimal value for the ship's horizontal position.
self.x = float(self.rect.x)
# Movement flags
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's position based on movement flags."""
# Update the ship's x value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.x += self.settings.ship_speed
if self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
# Update rect object from self.x.
self.rect.x = self.x
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
The issue is caused by a typo. self.settingsbullets_allowed has to be self.settings.bullets_allowe
So I'm learning python using Python Crash Course, and I think I have the correct code and yet its not yielding the desirable effect. The code is supposed to be freezing the game and displaying a "play button", but no play button is appearing.
Here is the "Alieninvasion.py" file:
import sys
from time import sleep
import pygame
from settings import Settings
from game_stats import GameStats
from ship import Ship
from bullet import Bullet
from alien import Alien
from button import Button
class AlienInvasion:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion")
# Create an instance to store game statistics.
self.stats = GameStats(self)
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
# Make the Play button.
self.play_button = Button(self, "Play")
# Set the background color.
self.bg_color = (230, 230, 230)
def run_game(self):
"""Start the main loop for the game."""
if self.stats.game_active:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
def _update_bullets(self):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions
self.bullets.update()
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
def _check_bullet_alien_collisions(self):
"""Respond to bullet-alien collisions."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
def _ship_hit(self):
"""Respond to the ship being hit by an alien."""
if self.stats.ships_left > 0:
# Decrement ships_left.
self.stats.ships_left -=1
# Get rid of any remaining aliens and bullets.
self.aliens.empty()
self.ship.center_ship()
# Pause.
sleep(0.5)
else:
self.stats.game_active = True
def _create_fleet(self):
"""Create the fleet of aliens."""
# Create an alien and find the number of aliens in a row.
# Spacing between each alien is equal to one alien width.
alien = Alien(self)
alien_width, alien_height = alien.rect.size
available_space_x = self.settings.screen_width - (2 * alien_width)
number_aliens_x = available_space_x // (2 * alien_width)
# Determine the number of rows of aliens that fit on the screen.
ship_height = self.ship.rect.height
available_space_y = (self.settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = available_space_y // (2 * alien_height)
# Create the full fleet of aliens.
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
self._create_alien(alien_number, row_number)
def _create_alien(self, alien_number, row_number):
"""Create an alien and place it in the row."""
alien = Alien(self)
alien_width, alien_height = alien.rect.size
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
self.aliens.add(alien)
def _check_events(self):
"""Respond to keypresses and mouse events."""
# Watch for keyboard and mouse events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self, event):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
"""Create a new bullet and add it to the bullets group."""
if len(self.bullets) < self.settings.bullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_aliens(self):
"""
Check if the fleet is at an edge,
then update the positions of all aliens in the fleet.
"""
self._check_fleet_edges()
self.aliens.update()
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
# Look for aliens hitting the bottom of the screen.
self._check_aliens_bottom()
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
# Redraw the screen during each pass through the loopp
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.aliens.draw(self.screen)
# Draw the play button if the game is inactive.
if not self.stats.game_active:
self.play_button.draw_button()
# Draw the play button if the game is inactive.
if not self.stats.game_active:
self.play_button.draw_button()
pygame.display.flip()
def _check_fleet_edges(self):
"""Respond appropriately if any aliens have reached an edge."""
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""Drop the entire fleet and change the fleet's direction."""
for alien in self.aliens.sprites():
alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
def _check_aliens_bottom(self):
"""Check if any aliens have reached the bottom of the screen."""
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# Treat this the same as if the ship got hit.
self._ship_hit()
break
if __name__ == '__main__':
# Make a game instance, and run the game.
ai = AlienInvasion()
ai.run_game()
and here is the button and game stats codes:
gamestats.py
class GameStats:
"""Track statistics for Alien Invasion."""
def __init__(self, ai_game):
"""Initialize statistics."""
self.settings = ai_game.settings
self.reset_stats()
# Start Alien Invasion in an inactive state.
self.game_active = True
def reset_stats(self):
"""Initialize statistics that can change during the game."""
self.ships_left = self.settings.ship_limit
and
button.py
import pygame.font
class Button:
def __init__(self, ai_game, msg):
"""Initialize button attributes."""
self.screen = ai_game.screen
self.screen_rect = self.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 and then draw message.
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
Thanks in advance!
self.game_active = True
this line in game_stats should be set to False to start the game...since it's set to True, this condition is never met in alien_invasion update_screen:
if not self.stats.game_active:
self.play_button.draw_button()
Hope you already caught that, came across this looking for another issue with that book. Also, you have the above comment/line/line duplicated in your code...not that it makes any difference.
Trying to make a game where my catcher catches a baseball. If the baseball is caught, then a new baseball appears randomly at the top of the screen. I can't seem to move the catcher after the changes I've made. The game is made through python and pygame.
catch.py
import sys
import pygame
from pygame.sprite import Group
from settings import Settings
from catcher import Catcher
from baseball import Baseball
import game_functions as gf
def run_game():
"""Initializes the game, screen, and settings."""
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Catch")
catcher = Group()
baseball = Group()
while True:
gf.check_events(catcher)
gf.update_catcher(ai_settings, screen, catcher)
gf.update_baseball(ai_settings, screen, catcher, baseball)
gf.update_screen(ai_settings, screen, catcher, baseball)
run_game()
baseball.py
import pygame
from pygame.sprite import Sprite
class Baseball(Sprite):
"""A class to represent a baseball."""
def __init__(self, ai_settings, screen):
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the baseball image and set its rect attribute.
self.image = pygame.image.load('images/baseball.bmp')
self.rect = self.image.get_rect()
# Start each new baseball at a random position at the top 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)
self.y = float(self.rect.y)
def blitme(self):
"""Draw the baseball at tis current location."""
self.screen.blit(self.image, self.rect)
def update(self):
"""Dropping the baseball."""
self.y += self.ai_settings.baseball_drop_speed
self.rect.y = self.y
catcher.py
import pygame
from pygame.sprite import Sprite
class Catcher(Sprite):
def __init__(self, ai_settings, screen):
"""Initialize the catcher and set its starting position."""
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the catcher image and get its rect.
self.image = pygame.image.load('images/catcher.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new catcher at the bottom center of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the catcher's center.
self.center = float(self.rect.centerx)
# Movement flags
self.moving_right = False
self.moving_left = False
def update(self, ai_settings, screen):
"""Update the catcher's position baed on the movement flag."""
# Update the catcher's center value, not the rect.
if self.moving_right and self.rect.right <= self.screen_rect.right:
self.center += self.ai_settings.catcher_speed_factor
if self.moving_left and self.rect.left >= 0:
self.center -= self.ai_settings.catcher_speed_factor
# Update rect object from self.center.
self.rect.centerx = self.center
def blitme(self):
"""Draw the catcher at its current location."""
self.screen.blit(self.image, self.rect)
game_functions.py
import sys
import pygame
from baseball import Baseball
from catcher import Catcher
from random import randint
def check_keydown_events(event, catcher):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
catcher.moving_right = True
elif event.key == pygame.K_LEFT:
catcher.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, catcher):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
catcher.moving_right = False
elif event.key == pygame.K_LEFT:
catcher.moving_left = False
def check_events(catcher):
"""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, catcher)
elif event.type == pygame.KEYUP:
check_keyup_events(event, catcher)
def update_screen(ai_settings, screen, catcher, baseball):
"""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)
catcher.draw(screen)
baseball.draw(screen)
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_baseball(ai_settings, screen, catcher, baseball):
"""Update the positions of the baseball and detect collisions."""
screen_rect = screen.get_rect()
baseball.update()
if len(baseball) == 0:
new_ball = Baseball(ai_settings, screen)
new_ball.x = randint(new_ball.rect.width, screen_rect.right - new_ball.rect.width)
new_ball.rect.x = new_ball.x
new_ball.y = new_ball.rect.height
new_ball.rect.y = new_ball.y
baseball.add(new_ball)
collisions = pygame.sprite.groupcollide(baseball, catcher, True, False)
def update_catcher(ai_settings, screen, catcher):
screen_rect = screen.get_rect()
catcher.update(ai_settings, screen)
if len(catcher) == 0:
new_catcher = Catcher(ai_settings, screen)
new_catcher.center = screen_rect.centerx
new_catcher.rect.centerx = new_catcher.center
new_catcher.rect.bottom = screen_rect.bottom
catcher.add(new_catcher)
settings.py
import pygame
class Settings():
"""Class to store settings."""
def __init__(self):
"""Initializes the game's settings."""
# Screen settings.
self.screen_width = 800
self.screen_height = 600
self.bg_color = (255, 255, 255)
# Ship settings.
self.catcher_speed_factor = 1.5
# Baseball settings.
self.baseball_drop_speed = float(1 / 2)
The issue is that catcher is an instance of Group rather than Catcher(Sprite):
catcher = Group()
while True:
check_events(catcher)
That causes that the attributes moving_right and moving_left are set to the Group object in check_keydown_events respectively check_keyup_events.
You have to change the attributes of the objects in the Group:
def check_keydown_events(event, catcher):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
for c in catcher:
c.moving_right = True
elif event.key == pygame.K_LEFT:
for c in catcher:
c.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, catcher):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
for c in catcher:
c.moving_right = False
elif event.key == pygame.K_LEFT:
for c in catcher:
c.moving_left = False