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()
Related
I'm trying to familiarise myself with Python and am trying to make an "alien invaders" style game using Pygame. I'm able to import an image of a ship and move it left and right.
Now I'm trying to the ship to fire a bullet whenever the space bar is pressed, but when I press it nothing happens.
This is my main program that trigger the behaviour on a key press:
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
#calls a function that increments the x coordinate of the ship
if keys[pygame.K_RIGHT]:
ship.updateRight()
#calls a function that decrements the x coordinate of the ship
if keys[pygame.K_LEFT]:
ship.updateLeft()
#calls a function that updates the screen to create a bullet
if keys[pygame.K_SPACE]:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
pygame.quit()
sys.exit()
When the space bar is pressed the following functions from the Bullet class are called:
class Bullet(Sprite):
def __init__ (self, ai_settings, screen, ship):
"""Create a bullet object at the ship's current position"""
super().__init__()
self.screen = screen
#create bullet rect (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
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet to the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)'''
and finally the "update_screen" function called in the main loop (it includes some other updates for the ship):
def update_screen(ai_settings, screen, ship, bullets):
screen.fill(ai_settings.bg_color)
#ship.blitme()
pygame.display.flip()
for bullet in bullets.sprites():
bullet.draw_bullet()
If anyone has any ideas as to why a bullet is not appearing please help! Haven't been able to figure this one out yet as I'm still very new to Python.
Thank you so much!
As #Rabbid76 said, pygame.display.flip() has to be done after the for bullet in bullets.sprites(): bullet.draw_bullet
The reason is that when you "update" the screen, everything's that been drawn so far will be put onto the screen. Simple, right?
When you put something AFTER the pygame.display.flip(), it will not draw it the time it is run. Plus, when you refill the screen with ai_settings.bg_color, after you drew the bullets, they're all covered up by the new screen you filled. So, it never shows up.
So, update_screen() needs to be:
def update_screen(ai_settings, screen, ship, bullets):
screen.fill(ai_settings.bg_color)
#ship.blitme()
for bullet in bullets.sprites():
bullet.draw_bullet()
pygame.display.flip()
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
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()
I am new to python programming, and I am practicing by making a Alien Invasion game. This is a classic game configuration, there are rows of aliens you have to shot down before they make it to the bottom and collide with your ship.
Here is my game_functions code:
import sys
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit()
def fire_bullet(ai_settings, screen, ship, bullets):
"""Fire a bullet if mimit not reached yet."""
#Create a new bullet and add it to the bullets group
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_events(event, ship):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_events(ai_settings, screen, 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 update_screen(ai_settings, screen, ship, aliens, 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)
#Redraw bullets behind the ship and aliens
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_bullets(ai_settings, screen, bullets, aliens, ship):
"""Update position of bullets and get rid of olf bullets."""
#Update bullet position
bullets.update()
#Check for any bullets that hit an alien.
#if so, get rid of the bullet and alien
#Get rid of bullets that have disappeared
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisons(ai_settings, screen, bullets, aliens, ship)
def check_bullet_alien_collisons(ai_settings, screen, bullets, aliens, ship):
"""Respond to bullet-alien collions."""
#Remove any bullets and aliens that collide.
collisons = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) == 0:
#Destroy existing bullets and create new fleet.
bullets.empty()
create_fleet(ai_settings, screen, aliens, ship)
def get_number_aliens_x(ai_settings, alien_width):
"""Determine the number of aliens that fit in a row."""
available_space_x = ai_settings.screen_width - 2 * alien_width
number_aliens_x = int(available_space_x / (2 * alien_width))
return number_aliens_x
def get_number_rows(ai_settings, ship_height, alien_height):
"""Find the number of rows for screen."""
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = int(available_space_y / (3 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""Create an alien and place it in the row."""
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
aliens.add(alien)
def create_fleet(ai_settings, screen, ship, aliens):
"""Create a full fleet of aliens."""
#Create an alien and find the number of aliens in a row
alien = Alien(ai_settings, screen)
number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height, alien.rect.height)
#Create the 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 appropriately if any aliens reached an edge."""
for alien in aliens.sprites():
if alien.check_edges:
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""Drop the entire fleet and change the fleet's direction."""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def update_aliens(ai_settings, aliens, ship):
"""Check for fleet on edge, then update positions of fleet."""
check_fleet_edges(ai_settings, aliens)
aliens.update()
#Look for alien-ship collisons.
if pygame.sprite.spritecollideany(aliens, ship):
print("Ship hit!")
And this is my game init code so I can run the functions and play the game
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
from alien import Alien
import game_functions as gf
def run_game():
#Initialze game, 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, a group of aliens, and a group of bullets
ship = Ship(ai_settings, screen)
aliens = Group()
bullets = Group()
#Create a fleet of aliens
gf.create_fleet(ai_settings, screen, ship, aliens)
# Start the main loop for the game
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(ai_settings, screen, bullets, aliens, ship)
gf.update_aliens(ai_settings, aliens, ship)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
run_game()
I have most of the game done, but when I add the pygame.sprite.spritecollideany() in the game_functions file, I get
"Attribute Error: the 'Group' has no attribute 'rect'"
I know there is probably something simple I am missing, but I cannot figure it out. Sorry of the code is not correctly typed as I copied and pasted to save time when posting my question.
I am also using Ubuntu 14.04 LTS, and running python-3.4.3.
Read Pygame doc: pygame.sprite.spritecollideany()
This function expects single sprite as first argument, and group as second argument but you use aliens which is group as first argument, and ship which is sprite as second argument.
You have to change order of arguments.
BTW: next time simply use print() - it is most popular method (if you don't know how to debug) to check what you have in variables and see what is wrong.
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.