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

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()

Related

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)

Can't move catcher character in pygame

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

Python Crash Course game: Bullets not firing

My Alien Invasion game does not show bullets being fired. I am copying most of the code from the Python Crash Course book (with minor changes here and there), so I'm guessing it is due to a syntax error that I can't find.
I've looked over the code in the book and tried to find differences between them. I changed K_SPACE to K_w to see if the spacebar wasn't registering or something like that, but the bullets still would not fire.
alien_invasion.py
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions 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')
# set the background color
bg_color= ai_settings.bg_color
# make a ship
ship = Ship(ai_settings, screen)
# make a group to store bullets in
bullets = Group()
# start the main loop for the game
running = True
while running:
running = gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
pygame.quit()
run_game()
game_functions.py
import pygame
from bullet import Bullet
def check_events(ai_settings, screen, ship, bullets):
"""respond to keypresses and mouse events"""
running = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False # different from Python Crash Course
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
return running
def update_screen(ai_settings, screen, ship, bullets):
"""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)
ship.blitme()
# make the most recently drawn screen visible
pygame.display.flip()
# redraw all bullets behind ship and aliens
for bullet in bullets.sprites():
bullet.draw_bullet()
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""respond to keypresses"""
if event.key == pygame.K_d:
ship.moving_right = True
elif event.key == pygame.K_a:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
# create a new bullet and add it to the bullets groups
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_d:
ship.moving_right = False
elif event.key == pygame.K_a:
ship.moving_left = False
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().__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)
left out settings.py and ship.py but can provide them if necessary
Expected outcome: bullet go pew pew
Actual outcome: Game does not crash, but nothing happens when spacebar is pressed.
The problem seems to be on the update_screen function - you first draw
everything, updates the screen, and then draws the bullets, after the screen is updated. Simply moving the call to ...display.flip() to the end of the function might make your bullets show up (unless there is something other that is also wrong).
def update_screen(ai_settings, screen, ship, bullets):
"""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)
ship.blitme()
# take this out of here
# pygame.display.flip()
# redraw all bullets behind ship and aliens
for bullet in bullets.sprites():
bullet.draw_bullet()
# to here
pygame.display.flip()

Pygame Rect not appearing on display

I know this must have been answered a number of times, but unfortunately, the topic is difficult to search and I am truly at a los, as Python is not throwing an error.
I am following a Python Crash Course tutorial (Eric Matthes) and am programming a game along the lines of space invaders. The below Module is intended to control the bullets:
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 bullet at the ships current positio"""
super().__init__()
self.screen = screen
# Create a bullet rect at (0, 0) and then set the 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 on the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)
The actual game screen is printing the bullet count, so I know the bullets are on the screen and also vanish again, as the move towards the edge of the screen, but they are not displayed.
The game itself looks as follows:
import sys
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
import game_functions 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")
# Make a ship.
ship = Ship(ai_settings, screen)
# Make a group to store the bullets in.
bullets = Group()
# Start main loop for the game
while True:
#Watch for keyboard and mouse events.
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, bullets)
# Redraw Screen during each pass through.
screen.fill(ai_settings.bg_color)
ship.blitme()
# Make most recently drawn visible
pygame.display.flip()
run_game()
The settings are there and no, the bullets are not the same color as the screen;-)
Can anyone help me find my error in thinking?
My impression is, the pygame.draw.rect function should make them display in combination with the gf.update_bullets(bullets) call.
Many thanks and best regards,
Simon
Additional File added:
game_functions.py
import sys
import pygame
from bullet import Bullet
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.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
def fire_bullet(ai_settings, screen, ship, bullets):
# 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 keypresses"""
if event.key == pygame.K_RIGHT:
# Move the ship to the 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()
if event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship, bullets):
# Redraw all bullets behind the ship and aliens
for bullet in bullets.sprites():
bullet.draw_bullet()
def update_bullets(bullets):
"""Update position of bullets and get rid of old bullets"""
# Update bullet position
bullets.update()
# Get rid of bullets that have disappeared.
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
print(len(bullets))
"""Update images on the screen and flip to the new screen."""
Check the order in which you draw things to the screen surface.
You call gf.update_screen(ai_settings, screen, ship, bullets), and after that, you actually erase the whole screen with screen.fill(ai_settings.bg_color).
Make sure to call screen.fill(ai_settings.bg_color) first, then draw your other stuff.
There are some other issues with your code, e.g.
you use the Sprite class, but your sprites don't have an image attribute, which makes using Sprite kind of pointless
you already use the Group class for your sprites, but draw them manually with a for loop and the draw_bullet function. Just give your bullets an image and call bullets.draw(screen) instead
you check if bullets are outside the screen in update_bullets. The the bullet class handle it itself, by simply using something like this:
if not self.screen.rect.contains(self.rect):
self.kill
your entire game_functions.py file makes your code hard to read

add() argument after * must be a sequence, not Settings

I'm trying to build a game that moves a ship left and right with the arrow keys and fires bullets when the spacebar is pressed. When I press the spacebar my game crashes and this error is shown:
Traceback (most recent call last):
TypeError: add() argument after * must be a sequence, not Settings
Here's my code:
class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 800
self.screen_height = 480
self.bg_color = (230, 230, 230)
# Ship settings
self.ship_speed_factor = 1.5
# Bullet settings
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
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)
import sys
import pygame
from bullet import Bullet
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:
# Create a new bullet and add it to the bullets group.
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""Respind 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)
And finally the main file:
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
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 a ship.
ship = Ship(ai_settings, screen)
# Make a group to store bullets in.
bullets = Group()
# Start the main loop for the game.
while True:
# Watch the keyboard and mouse events.
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
The trace:
Traceback (most recent call last):
File "C:\Users\martin\Desktop\python_work\alien_invasion\alien_invasion.py", line 30, in <module>
run_game()
File "C:\Users\martin\Desktop\python_work\alien_invasion\alien_invasion.py", line 25, in run_game
gf.check_events(ai_settings, screen, ship, bullets)
File "C:\Users\martin\Desktop\python_work\alien_invasion\game_functions.py", line 33, in check_events
check_keydown_events(event, ai_settings, screen, ship, bullets)
File "C:\Users\martin\Desktop\python_work\alien_invasion\game_functions.py", line 15, in check_keydown_events
new_bullet = Bullet(ai_settings, screen, ship)
File "C:\Users\martin\Anaconda3\lib\site-packages\pygame\sprite.py", line 124, in __init__
self.add(*groups)
File "C:\Users\martin\Anaconda3\lib\site-packages\pygame\sprite.py", line 142, in add
self.add(*group)
TypeError: add() argument after * must be a sequence, not Settings
You are missing an underscore _ in your Bullet.__init__ method. You currently have _init__ when it should be __init__.
This results in Python calling the Sprite.__init__ method with ai_settings as the first argument, since it cannot find any overridden __init__ for Bullet. That leads to problems.
Yes Jokab is right, you forgot the extra underscore. However, for future practice it is important to learn to read the Python TrackBack. It usually gives you a good idea of where the problem is. For example, take the TrackBack you pasted here. Python first tells you it had a problem running run_game(). So python then says that in your game running function it has a problem calling the method gf.check_events(ai_settings, screen, ship, bullets). It then looked at your initialization of the bullet class new_bullet = Bullet(ai_settings, screen, ship and has a problem with it. And in the very next line is were it gives the TypeError. Now, while you could figure out exactly what python is saying about the TypeError, which is a viable option. But just from looking at it you could determine that it has a problem adding your bullet object to the sprites group. Which means that if I were you, I'd start my searching in the Bullet class. And sure enough there is a typo in your __init__ function.
While it is not the end of the world if you don't learn how to read python TrackBack's, It will save you plenty of time in the long run.

Categories

Resources