Having trouble getting a button to render in Python Crash Course - python

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.

Related

Can't get ship to move after modifying ship speed in alien invasion

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)

PyGame lags when I try to add sprites to the screen

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)

Pygame Python crash course bullets not firing page 253 AttributeError: 'AlienInvasion' object has no attribute 'settingsbullets_allowed'

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

AttributeError 'pygame.Surface' object has no attribute 'rect'

Here is a game from the book "Python Crash Course (author: Eric Matthes)"
I do exactly what the book told. But I found these problems, and why?
File "D:\pythoncode\l_one\venv\lib\site-packages\pygame\sprite.py", line 1586, in spritecollideany
spritecollide = sprite.rect.colliderect
AttributeError: 'pygame.Surface' object has no attribute 'rect'
Here are my codes: https://github.com/yushu-liu/Learn_Python/tree/master/Alien_Invasion
And part of my codes as follows(aline_invasion.py):
import sys
from settings import Settings
from ship import Ship
from pygame.sprite import Group
import game_functions as gf
from alien import Alien
from game_stats import GameStats
import pygame
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")
# Set the background Color
bg_color = (230,230,230)
# 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)
# Create an instance to store game statistics.
stats = GameStats(ai_settings)
# Start the main loop for the game
while True:
# Watch for keyboard and mouse events
gf.check_events(ai_settings,screen,ship,bullets)
if stats.game_active:
ship.update()
# Make the most recently drawn screen visible
gf.update_bullets(ai_settings,screen,ship,aliens,bullets)
gf.update_aliens(ai_settings,stats,ship,screen,aliens,bullets)
gf.update_screen(ai_settings,screen,ship,aliens,bullets)
run_game()
And another part of my codes as follows(game_function.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):
'''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 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, 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)
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 update_screen(ai_settings,screen,ship,aliens, bullets):
'''Update images screen during each pass through the loop'''
# 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()
# Redraw the ship and the alien
ship.blitme()
# When calling draw, Pygame automatically draws each element in the group
# at the position defined by its rect
aliens.draw(screen)
# 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 the latest positions of bullets
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:
# If the entire fleet is destroyed, start a new level
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.'''
availabe_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(availabe_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 an 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 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 appropirately if any aliens have reached and 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 ship_left
stats.ships_left -= 1
# Update scoreboard.
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 get 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 positions of all
aliens in the fleet.
'''
check_fleet_edges(ai_settings,aliens)
aliens.update()
# Look for alien-ship collisions.
# Look for aliens hitting the bottom of the screen.
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)
alien.py as follow:
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 an set its starting position.'''
super(Alien,self).__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the alien image an sets 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() # 注意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.'''
self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x = self.x
ship.py as fllow:
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 center of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's center.
self.center = float(self.rect.centerx)
# Movement flags
self.moving_right = False
self.moving_left = False
def center_ship(self):
'''Make sure the ship in the center of screen.'''
self.center = self.screen_rect.centerx
def update(self):
'''Update the ship's position based on the movement flags.'''
# Update the ship's 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)
game_stats.py as follow:
class GameStats():
'''Track statistics for Alien Invasion'''
def __init__(self,ai_settings):
'''Start Alien Invasion in an active state'''
self.ai_settings = ai_settings
self.reset_stats()
# Start Alien Invasion in an active state
self.game_active = True
def reset_stats(self):
'''Initialize statistics than can change during the game'''
self.ships_left = self.ai_settings.ship_limit
You have two issues.
Your first one is located at line #42 of your aline_invasion.py file:
gf.update_aliens(ai_settings,stats,ship,screen,aliens,bullets)
should be:
gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)
If you look at the method declaration for update_aliens in the game_functions.py file you can see that your order was incorrect, which is important. You want to pass things into a method in the order they belong.
Your second issue is located at line #171 of game_functions.py
if pygame.sprite.spritecollideany(aliens, ship):
should be:
if pygame.sprite.spritecollideany(ship, aliens):
Per this post, and this documentation the method you are calling accepts a sprite first, then a group. You were passing the group in first, and then the sprite. Again, the order is important.

Pygame Error: self.spritedict[spr] = surface_blit(spr.image, spr.rect)

I'm making a game with pygame using python 3. I can't get the image of a alien to display on the "screen" surface.
I've looked for hours and reviewed the book its based on but I can't find the source of the problem. I have also looked at other questions on here. Thanks
Heres's the trackback:
Traceback (most recent call last):
File "alien_invasion.py", line 35, in <module>
run_game()
File "alien_invasion.py", line 33, in run_game
g_funcs.update_screen(ai_settings, ai_screen, a_ship, aliens_g, bullets)
File ...
aliens_g.draw(ai_screen)
File...
self.spritedict[spr] = surface_blit(spr.image, spr.rect)
AttributeError: 'Alien' object has no attribute 'image'
Here's my game module:
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_function as g_funcs
def run_game():
"""Games definition with control while loop"""
#Initialize pygame, settings and screen surface
pygame.init()
ai_settings = Settings()
ai_screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_length))
pygame.display.set_caption("Alien Invasion")
#make a ship, group of bullets, group of aliens
a_ship = Ship(ai_settings, ai_screen)
bullets = Group()
aliens_g = Group()
#Create FLEET of aliens
g_funcs.create_fleet(ai_settings, ai_screen, aliens_g)
#start the main loop for the game
while True:
g_funcs.check_events(ai_settings, ai_screen, a_ship, bullets)
a_ship.update_location()
g_funcs.update_bullet(bullets)
g_funcs.update_screen(ai_settings, ai_screen, a_ship, aliens_g, bullets)
run_game()
Here's all the functions:
import sys
import pygame
from bullet import Bullet
from alien import Alien
def check_events(ai_settings, ai_screen, a_ship, bullets):
"""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:
check_keydown_event(event, ai_settings, ai_screen, a_ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_event(event, a_ship)
def check_keydown_event(event, ai_settings, ai_screen, a_ship, bullets):
"""ONLY callable by check_events function when keydown."""
if event.key == pygame.K_RIGHT:
a_ship.moving_right = True
elif event.key == pygame.K_LEFT:
a_ship.moving_left = True
elif event.key == pygame.K_UP:
a_ship.moving_up = True
elif event.key == pygame.K_DOWN:
a_ship.moving_down = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, ai_screen, a_ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, ai_screen, a_ship, bullets):
"""Fire bullets if bullets limit is not met"""
#CREATE a new bullet INSTANCE and add it to the bullets GROUP
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, ai_screen, a_ship)
bullets.add(new_bullet)
def check_keyup_event(event, a_ship):
"""ONLY callable by check_events function when keyup."""
if event.key == pygame.K_RIGHT:
a_ship.moving_right = False
if event.key == pygame.K_LEFT:
a_ship.moving_left = False
if event.key == pygame.K_UP:
a_ship.moving_up = False
if event.key == pygame.K_DOWN:
a_ship.moving_down = False
def update_screen(ai_settings, ai_screen, a_ship, aliens_g, bullets):
"""Update image on the screen and flip to the new screen."""
#Redraw the screen during each pass through the loop
ai_screen.fill(ai_settings.bg_color)
#REDRAW all bullets behind ship and aliens
for bullet in bullets.sprites():
bullet.draw_bullet()
a_ship.blitme()
aliens_g.draw(ai_screen)
#Make the most recently drawn screen visible
pygame.display.flip()
def update_bullet(bullets):
"""Update positions of bullets and delete old ones."""
#Update bullet positions
bullets.update()
#get rid of the old bullets that go off screen
for bullet in bullets.copy():
if bullet.bullet_rect.bottom <= 0:
bullets.remove(bullet)
def create_fleet(ai_settings, ai_screen, aliens_g):
"""Create a full fleet of aliens"""
alien = Alien(ai_settings, ai_screen)
alien_amount_x = get_number_aliens_x(ai_settings, alien.alien_rect.width)
#Create the first row of aliens
for one in range(alien_amount_x):
create_alien(ai_settings, ai_screen, aliens_g, one)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the amount of alien that fits in a row"""
#Calulations:
availabe_space_x = ai_settings.screen_width - 2 * alien_width
alien_amount_x = int(availabe_space_x / (2 * alien_width))
return alien_amount_x
def create_alien(ai_settings, ai_screen, aliens_g, one):
"""Create an alien and place it in the row."""
alien = Alien(ai_settings, ai_screen)
alien_width = alien.alien_rect.width
alien.x = alien_width + 2 * alien_width * one
alien.alien_rect.x = alien.x
aliens_g.add(alien)
And... heres my alien module:
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""A CLASS that MANAGES a single alienn in the fleet."""
def __init__(self, ai_settings, ai_screen):
"""Initialize the alien and start position"""
super().__init__()
self.ai_settings = ai_settings
self.screen = ai_screen
#Load the alien image and SET RECT attribute
self.alien_image = pygame.image.load('images/alien.bmp')
self.alien_rect = self.alien_image.get_rect()
#Set starting point of alien at the top left corner of screen
#settings the space(top and left) to the width and height of image_rect
self.alien_rect.x = self.alien_rect.width
self.alien_rect.y = self.alien_rect.height
self.x_float = float(self.alien_rect.x)
def blit_a(self):
"""Draw alien at its current location"""
self.screen.blit(self.alien_image, self.alien_rect)
Pygame sprites must have a self.image and a self.rect attribute, otherwise the sprite group won't be able to blit the image and an AttributeError gets raised.
If you take a look at the draw method of the sprite groups, you'll see that it iterates over the contained sprites and blits the spr.image at the spr.rect.
Use name self.image instead of self.alien_image and self.rect instead of self.alien_rect
naming conventions are important in pygame.sprites, so there's an another method to solve this
instead of group.draw(surface) you can use a for loop to print individual alien on screen
for alien in aliens_g.sprites():
alien.blit_a()

Categories

Resources