Pygame Rect not appearing on display - python

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

Related

How to extend ships' boundary when screen is resized Python Pygame?

I am trying to recreate the classic "Space Invaders" game and I want to allow anyone playing the game to resize their window using the box icon (restore / maximise) next to the close window button on the window. I have enabled the pygame.resizeable flag in the code. I am able to toggle the screen sizes using the box button and the background changes scale as it should. However, I am finding it a challenge to allow the ship to extend its boundary to the newly found "space".
The code is split into different modules according to their task. And so far I have the main game module, a settings module, game functions module, ship and bullets module respectively.
I have created a separate function (check_screen_resized(event,ship)) in the "game functions module" to respond to the pygame.VIDEORESIZE event.The function first toggles a "flag" (self.screen_resized = True) then sets the new resolution which is stored in the variable "screen" to self.screen_res = screen.
In the "ship module", when self.screen_resized = True, an "if" statement in the "update function" is activated to change the screen resolution. I am calling this "update" function in the main game module's while loop.
But my approach is not working. Can someone help me with this problem?
# MAIN GAME MODULE
---------------------------------------------------------------------------------------------
import pygame
from pygame.sprite import Group
import game_functions as gme_func
from settings import Settings
from ship import Ship
def run_game():
"""Initialise pygame, settings, ship and screen object."""
pygame.init()
ai_settings = Settings()
# screen settings.
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height), pygame.RESIZABLE
)
pygame.display.set_icon(ai_settings.icon)
pygame.display.set_caption("Alien Invasion")
# make a ship.
ship = Ship(screen, ai_settings)
# store bullets in a group.
bullets = Group()
# the main game loop.
while True:
# listen to keyboard and mouse events.
gme_func.check_events(ai_settings, screen, ship, bullets)
# update the ship's movement.
ship.update()
# update the bullet position and delete disappeared bullets.
gme_func.update_bullets(bullets)
# redraw the screen during each pass through the loop.
# make the most recently drawn screen visible.
gme_func.update_screen(screen, ai_settings, ship, bullets)
run_game()
---------------------------------------------------------------------------------------------
# GAME FUNCTIONS MODULE
---------------------------------------------------------------------------------------------
import sys
import pygame
from bullet import Bullet
*****
*****
def check_screen_resized(event, ship):
"""Update screen surfaces with resolution change."""
screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
ship.screen_resized = True
ship.screen_res = screen
def check_events(ai_settings, screen, ship, bullets):
"""Listen to keyboard and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# moving the ship
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
# resize the game window.
elif event.type == pygame.VIDEORESIZE:
check_screen_resized(event, ship)
*****
*****
---------------------------------------------------------------------------------------------
# SHIP MODULE
---------------------------------------------------------------------------------------------
import pygame
class Ship:
"""A class that creates a ship for the Alien Invasion game."""
def __init__(self, screen, ai_settings):
"""Initialise the ship and set its starting position."""
self.ai_settings = ai_settings
self.screen = screen
self.screen_rect = self.screen.get_rect()
# load the ship image and get its rectangle.
self.image = pygame.image.load(alien_invasion\images\ship3.png").convert_alpha()
self.ship_rect = self.image.get_rect()
# start each new ship at the bottom centre of the screen.
self.ship_rect.centerx = self.screen_rect.centerx
self.ship_rect.bottom = self.screen_rect.bottom
# convert the centerx and centery values to decimal.
self.centerx = float(self.ship_rect.centerx)
self.centery = float(self.ship_rect.centery)
# movement flags.
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
# screen resized flag.
self.screen_resized = False
self.screen_res = None
def update(self):
"""Update the ship's position based on the movement flags."""
if self.moving_right and self.ship_rect.right < self.screen_rect.right:
self.centerx += self.ai_settings.ship_speed_factor
if self.moving_left and self.ship_rect.left > self.screen_rect.left:
self.centerx -= self.ai_settings.ship_speed_factor
if self.moving_up and self.ship_rect.top > self.screen_rect.top:
self.centery -= self.ai_settings.ship_speed_factor
if self.moving_down and self.ship_rect.bottom < self.screen_rect.bottom:
self.centery += self.ai_settings.ship_speed_factor
# update the rectangle position from centerx and centery.
self.ship_rect.centerx = self.centerx
self.ship_rect.centery = self.centery
# responding to screen resizing.
if self.screen_resized:
self.screen = self.screen_res
def blit_ship(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.ship_rect)
---------------------------------------------------------------------------------------------
If I'm not mistaken, the following code is a basic snippet that starts a resizable screen and prints its current height and width. The following should work:
import pygame
pygame.init()
# resizable screen
win = pygame.display.set_mode((1000,500), pygame.RESIZABLE)
pygame.display.set_caption("First Game")
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
win.fill((0,0,0))
pygame.display.update()
# printing the width and height - we get the pygame.display.get_surface() object, and
# get the width and height with the get_width() and get_height() funcs that the
# returned obj has
print(pygame.display.get_surface().get_width(), pygame.display.get_surface().get_height())
pygame.quit()
You can put this code snippet to use by getting updated variables containing the current screen width and height, thus telling you where the real border is.

Pygame struggling to make object appear

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

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

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