The ship is not shooting, even if I press space. This is truly frustrating and the aliens are note generating either, can someone help? The code is below.
alien.py:
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
def __init__(self, ai_settings, screen):
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load('alien.bmp')
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.x = float(self.rect.x)
def check_edges(self):
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
def update(self):
self.x += (self.ai_settings.alien_speed_factor *
self.ai_settings.fleet_direction)
self.rect.x = self.x
def blitme(self):
self.screen.blit(self.image, self.rect)
alien_invasioin.py:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
import random
from pygame.sprite import Group
from alien import Alien
class Square(pygame.sprite.Sprite):
def __init__(self, x, y, size1, size2, colour):
super().__init__()
self.image = pygame.Surface([size1, size2])
self.image.fill(colour)
self.rect=self.image.get_rect()
self.rect.x=x
self.rect.y=y
allspriteslist = pygame.sprite.Group()
for i in range(1, 250):
num = random.randint(1,3)
stars = Square(random.randint(0, 1000), random.randint(0, 700), num, num, (255, 255, 255))
allspriteslist.add(stars)
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("PEWPEWPEWPOWPOW DIE YOU LITTLE ALIEN ____S ")
ship = Ship(ai_settings, screen)
bullets = Group()
aliens = Group()
gf.create_fleet(ai_settings, screen, ship, aliens)
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(ai_settings, screen, ship, aliens, bullets)
gf.update_aliens(ai_settings, screen, ship, aliens, bullets)
gf.update_screen(ai_settings, screen, ship, aliens, bullets)
allspriteslist.draw(screen)
pygame.display.flip()
run_game()
bullet.py:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, ai_settings, screen, ship):
super(Bullet, self).__init__()
self.screen = screen
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
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
self.y -= self.speed_factor
self.rect.y = self.y
def draw_bullet(self):
pygame.draw.rect(self.screen, self.color, self.rect)
game_functions.py:
import sys
import pygame
from bullet import Bullet
from alien import Alien
def check_keydown_events(event, ai_settings, screen, ship, bullets):
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit
def check_keyup_events(event, ship):
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):
for event in pygame.event.get():
if event == 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):
screen.fill(ai_settings.bg_colour)
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
def update_bullets(ai_settings, screen, ship, aliens, bullets):
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)
def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) == 0:
bullets.empty()
create_fleet(ai_settings, screen, ship, aliens)
def check_fleet_edges(ai_settings, aliens):
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def update_aliens(ai_settings, screen, ship, aliens, bullets):
check_fleet_edges(ai_settings, aliens)
aliens.update()
def fire_bullet(ai_settings, screen, ship, bullets):
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def get_number_aliens_x(ai_settings, alien_width):
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):
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
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):
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)
settings.py:
class Settings():
def __init__(self):
#screen stuff
self.screen_width = 1000
self.screen_height = 750
self.bg_colour = (0, 0, 0)
self.ship_speed_factor = 1.5
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 0, 255, 0
self.bullets_allowed = 7
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
self.fleet_direction = 1
ship.py:
import pygame
class Ship():
def __init__(self, ai_settings, screen):
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load('ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.moving_right = False
self.moving_left = False
self.center = float(self.rect.centerx)
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
self.rect.centerx = self.center
def blitme(self):
self.screen.blit(self.image, self.rect)
This is kind of frustrating, so please help.
Anyways, it seems like my post is mostly made out of code, and need more details, I'm sorry about that!
Add aliens.add(alien) at the end of create_fleet:
def create_fleet(ai_settings, screen, ship, aliens):
# [...]
aliens.add(alien)
Explanation:
The bullets are constructed correctly, but the are destroyed immediately in check_bullet_alien_collisions:
def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) == 0:
bullets.empty()
create_fleet(ai_settings, screen, ship, aliens)
The bullets are destroyed by bullets.empty(), because the the group aliens is empty. The group is empty, because you missed to append the 1st alien to the group when it is constructed in create_fleet:
def create_fleet(ai_settings, screen, ship, aliens):
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)
aliens.add(alien) # <-- this is missing in your code
I think that Rabbid76 was very close on this but did not get it quite right this time (unusual for him/her). The diagnosis was correct, but not the resolution. It is in here:
def create_fleet(ai_settings, screen, ship, aliens):
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)
But create_fleet() is likely intended to create a fleet not just one alien. So I expect this is the needed behavior:
def create_fleet(ai_settings, screen, ship, aliens):
#alien = Alien(ai_settings, screen) <--- remove this!!!
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)
for col_number in number_aliens_x:
for row_number in number_rows:
create_alien(ai_settings, screen, aliens, col_number, row_number)
I actually missed the problem when I first looked at this. Just now after seeing Rabbid76's answer I realized that he/she found the problem, but do not think that she/he provided the proper resolution (based on the name of the method create_fleet and the fact that it was looking up the number of rows and columns but then not using them).
Related
I'm creating the alien invasion game in the python crash course book and i'm getting a name error that says bullets is not defined. So I changed 'bullets' to 'Bullet', which gives me an attribute error saying Bullet has no attribute 'sprites'.
here is my code so far.
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 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:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
settings.py
class Settings():
"""A class to store all settings for Alien Invasion."""
def __init__(self):
"""Initialize the game's settings."""
# Screen settings
self.screen_width = 1200
self.screen_height = 600
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
ship.py
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""Initialize the ship and set its starting position."""
self.screen = screen
self.ai_settings = ai_settings
# Load the ship image and get its rect.
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new ship at the bottom center of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's center.
self.center = float(self.rect.centerx)
# Movement flags
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's position based on movement flags."""
# Update the ship's center value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# Update rect object from self.center.
self.rect.centerx = self.center
def blitme(self):
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
game_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:
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):
"""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, ai_settings, ship)
# Redraw all bullets behind ship and aliens.
for bullet in Bullet.sprites():
bullet.draw_bullet()
def update_screen(ai_settings, screen, ship, bullets):
"""Update images on the screenand flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
# Redraw all bullets behind ship and aliens.
for bullet in Bullet.sprites():
bullet.draw_bullet()
ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
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 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 to the screen."""
pygame.draw.rect(self.screen, self.color, self.rect)
This is the origional error:
game_functions.py, line 36, in <module>
for bullet in bullets.sprites():
NameError: name 'bullets' is not defined
This is the error after I change 'bullets' to 'Bullet' in order to match the "class Bullet(Sprite)" in bullet.py:
game_functions.py, line 36, in <module>
for bullet in Bullet.sprites():
AttributeError: type object 'Bullet' has no attribute 'sprites'
I would greatly appreciate it if the problem were to be resolved. Also this is my first time asking a question as i am relatively new to coding. Let me know if I did something wrong when asking this question.
The error is coming from a two-line for-loop that is not inside any function. These lines will be executed only when the game_functions module is imported. This does not seem useful. You can probably just delete these lines without breaking anything.
The same two lines appear in the immediately following update_screen function. Between the names bullets and Bullet, only one has a sprites() method. That is the one you need for your for-loop.
for bullet in Bullet.sprites():
bullet.draw_bullet()
I think it should be:
for bullet in self.bullets.sprites():
bullet.draw_bullet()
you're not calling self which would cause it not to run.
For some reason, the python window says Alien not defined.
alien.py:
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
def __init__(self, ai_settings, screen):
super(Alien, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load('alien.bmp')
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.x = float(self.rect.x)
def check_edges(self):
screen_rect = self.screen.get_rect()
if self.rect.right >= screen_rect.right:
return True
elif self.rect.left <= 0:
return True
def update(self):
self.x += (self.ai_settings.alien_speed_factor *
self.ai_settings.fleet_direction)
self.rect.x = self.x
def blitme(self):
self.screen.blit(self.image, self.rect)
alien_invasion.py:
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
import random
from pygame.sprite import Group
from alien import Alien
class Square(pygame.sprite.Sprite):
def __init__(self, x, y, size1, size2, colour):
super().__init__()
self.image = pygame.Surface([size1, size2])
self.image.fill(colour)
self.rect=self.image.get_rect()
self.rect.x=x
self.rect.y=y
allspriteslist = pygame.sprite.Group()
for i in range(1, 250):
num = random.randint(1,3)
stars = Square(random.randint(0, 1000), random.randint(0, 700), num, num, (255, 255, 255))
allspriteslist.add(stars)
def run_game():
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("PEWPEWPEWPOWPOW DIE YOU LITTLE ALIEN ____S ")
ship = Ship(ai_settings, screen)
bullets = Group()
aliens = Group()
gf.create_fleet(ai_settings, screen, ship, aliens)
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update()
gf.update_bullets(bullets)
gf.update_screen(ai_settings, screen, ship, alien, bullets)
allspriteslist.draw(screen)
pygame.display.flip()
run_game()
bullet.py:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, ai_settings, screen, ship):
super(Bullet, self).__init__()
self.screen = screen
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
self.y = float(self.rect.y)
self.color = ai_settings.bullet_color
self.speed_factor = ai_settings.bullet_speed_factor
def update(self):
self.y -= self.speed_factor
self.rect.y = self.y
def draw_bullet(self):
pygame.draw.rect(self.screen, self.color, self.rect)
game_functions.py:
import sys
import pygame
from bullet import Bullet
def check_keydown_events(event, ai_settings, screen, ship, bullets):
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.key == pygame.K_SPACE:
fire_bullet(ai_settings, screen, ship, bullets)
elif event.key == pygame.K_q:
sys.exit
def check_keyup_events(event, ship):
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):
for event in pygame.event.get():
if event == 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):
screen.fill(ai_settings.bg_colour)
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
def update_bullets(bullets):
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def fire_bullet(ai_settings, screen, ship, bullets):
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def get_number_rows(ai_settings, ship_height, alien_height):
available_space_y = (ai_settings.screen_height -
(3 * alien_height) - ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
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):
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)
settings.py:
class Settings():
def __init__(self):
#screen stuff
self.screen_width = 1000
self.screen_height = 750
self.bg_colour = (0, 0, 0)
self.ship_speed_factor = 1.5
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 0, 255, 0
self.bullets_allowed = 7
ship.py:
import pygame
class Ship():
def __init__(self, ai_settings, screen):
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load('ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.moving_right = False
self.moving_left = False
self.center = float(self.rect.centerx)
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
self.rect.centerx = self.center
def blitme(self):
self.screen.blit(self.image, self.rect)
I'm getting an error message that Alien is not defined when I run this code, and I don't know why.
How is the game_functions.py file meant to know about the Alien it uses in createAlien() and createFleet()?
Python is good, but it can't read minds :-) I think you probably need to import the alien stuff into that file.
I encountered an error while trying to fire bullets in the alien invasion game I copied most of the code from the book but I must have left somethings out. This is my whole code and since I started to add the bullets i haven't been able to move the ship does anyone have a solution?
pythoncrashcoursebook.py
import pygame
import refactors as rf
from game_settings import Settings
from ship import Ship
from pygame.sprite import Group
def run_game():
# Initialize game and make a screen object and caption
pygame.init()
# Make a settings instance
settings = Settings()
# Make a screen
screen = pygame.display.set_mode((settings.screen_height, settings.screen_width))
# Make a ship
ship = Ship(screen, settings)
# Make a group to store bullets
bullets = Group()
# Display caption
pygame.display.set_caption("Alien Invasion")
# Main game loop
while True:
rf.check_events(settings, screen, ship, bullets)
ship.update()
bullets.update()
rf.update_screen(settings, screen, ship, bullets)
run_game()
ship.py
import pygame
class Ship:
def __init__(self, screen, settings):
# Initialize the ship and start position
self.screen = screen
self.settings = settings
# Load the image and get the rect of the ship
self.image = pygame.image.load("spaceship.png")
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Set the ship at the bottom of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's center
self.center = float(self.rect.centerx)
# Moving flag
self.moving_right = False
self.moving_left = False
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.settings.ship_speed_factor
# Update rect object from self.center
self.rect.centerx = self.center
def blitme(self):
# Draw the ship
self.screen.blit(self.image, self.rect)
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""Class to control bullet"""
def __init__(self, settings, screen, ship):
"""Create bullet at ship position"""
super(Bullet, self).__init__()
self.screen = screen
# Create bullet rect at (0, 0) and then correct position
self.rect = pygame.Rect(0, 0, settings.bullet_width, settings.bullet_height)
self.rect.centerx = ship.rect.centerx
self.rect.top = ship.rect.top
# Store the bullet's position as a decimal
self.y = float(self.rect.y)
# Store bullet colour and speed
self.colour = settings.bullet_colour
self.bullet_speed = settings.bullet_speed_factor
def update_bullet(self):
"""Move the bullet up the screen"""
self.y -= self.bullet_speed
# Update rect position
self.rect.y = self.y
def draw_bullet(self):
"""Draw bullet on the screen"""
pygame.draw.rect(self.screen, self.colour, self.rect)
game_settings.py
class Settings:
# A class to store all the game settings
def __init__(self):
# Ship settings
self.ship_speed_factor = 1.5
# Screen settings
self.bg_colour = (0, 0, 255)
self.screen_height = 800
self.screen_width = 600
# Bullet settings
self.bullet_colour = 60, 60, 60
self.bullet_height = 15
self.bullet_width = 3
self.bullet_speed_factor = 1
refactors.py
import sys
import pygame
from bullet import Bullet
def check_events(ship, settings, screen, bullets):
# Check for events and assign functions to them
for event in pygame.event.get():
# To quit
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_key_down_event(event, ship, settings, screen, bullets)
elif event.type == pygame.KEYUP:
check_key_up_event(event, ship)
def check_key_down_event(event, ship, settings, screen, bullets):
"""Respond to keypress"""
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 mew bullet and add it to the bullet group
new_bullet = Bullet(settings, screen, ship)
bullets.add(new_bullet)
def check_key_up_event(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 update_screen(settings, screen, ship, bullets):
screen.fill(settings.bg_colour)
# Redraw all bullets behind aliens and ship
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
pygame.display.flip()
And this is the error
line 14, in __init__
self.rect = pygame.Rect(0, 0, settings.bullet_width, settings.bullet_height)
AttributeError: 'pygame.Surface' object has no attribute 'bullet_width'
You should try changing:
rf.check_events(settings, screen, ship, bullets)
to:
rf.check_events(ship, settings, screen, bullets)
in your main code.
You can see my game_functions.py the 56th line.It says"stats.reset_stats"right?But the game didn't reset the score when it begins . How can this happen?(the code is long, but please be patient.)
I am following the book Python Crash Course Chapter 14. I compared it to the source code.I didn't find anything different except the commit and the content after it.
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
from game_stats import GameStats
from button import Button
from scoreboard import Scoreboard
def run_game():
#create the game
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width,ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
#create a ship
ship = Ship(ai_settings, screen)
#create the bullets
bullets = Group()
#create the Aliens
aliens = Group()
#create alien Group
gf.create_fleet(ai_settings, screen,ship , aliens)
#Create a instance to store game statistics and create a scoreboard.
stats = GameStats(ai_settings)
sb = Scoreboard(ai_settings, screen, stats)
#Make the Play button.
play_button = Button(ai_settings, screen, "Play")
#Start the main loop for the game.
while True:
gf.check_events(ai_settings ,screen, stats,play_button,ship,aliens,
bullets)
if stats.game_activate == True:
ship.update()
bullets.update()
gf.update_bullets(ai_settings,screen,stats,sb,ship,aliens,bullets)
gf.update_aliens(ai_settings,stats,screen,ship,aliens,bullets)
gf.update_screen(ai_settings,screen,stats, sb,ship,aliens , bullets,
play_button)
run_game()
settings.py
class Settings():
"""Store all the settings in Alien Invasion"""
def __init__(self):
"""Initialize the game's static settings"""
#Screen settings
self.screen_width = 850
self.screen_height = 570
self.bg_color = (230, 230, 230)
#Ship settings
self.ship_limit = 3
#Bullet settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3
#Alien settings
self.fleet_drop_speed = 10
self.fleet_direction = 1
#How quickly the alien value increase.
self.score_scale = 1.5
#How quickly the game speeds up
self.speedup_scale = 1.1
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
"""Initialize settings that change throughout the game."""
self.ship_speed_factor = 1.5
self.bullet_speed_factor = 3
self.alien_speed_factor = 1
#fleet direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
#Scoring
self.alien_points = 50
def increase_speed(self):
"""Increase speed settings and alien point values."""
self.ship_speed_factor *= self.speedup_scale
self.bullet_speed_factor *= self.speedup_scale
self.alien_speed_factor *= self.speedup_scale
self.alien_points = int(self.alien_points * self.score_scale)
game_functions.py
import sys
import pygame
from bullet import Bullet
from alien import Alien
from time import sleep
def check_keydown_events(event ,stats,ai_settings ,screen, ship, bullets):
"""Respond to key presses."""
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()
elif event.key == pygame.K_p:
stats.game_activate = True
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, stats,play_button,ship,aliens, bullets):
"""Respond to key presses 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,stats,ai_settings,screen, ship,bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x,mouse_y = pygame.mouse.get_pos()
check_play_button(ai_settings,screen, stats, play_button, ship,
aliens,bullets, mouse_x, mouse_y)
def check_play_button(ai_settings, screen, stats ,play_button, ship, aliens,
bullets, mouse_x, mouse_y):
"""Start a new game when the player clicks Play"""
button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
if button_clicked and not stats.game_activate:
#Reset the game settings.
ai_settings.initialize_dynamic_settings()
#Hide the mouse cursor.
pygame.mouse.set_visible(False)
#Reset the game statistics
stats.reset_stats()
stats.game_activate = True
#Empty the list of aliens and bullets.
aliens.empty()
bullets.empty()
#Create a new fleet and center the ship.
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
def update_screen(ai_settings, screen, stats, sb,ship,aliens , bullets,
play_button):
"""Update the images on the screen, and flip to new screen"""
#Redraw the screen, each pass through the loop.
screen.fill(ai_settings.bg_color)
#Redraw all bullets, behind ship and aliens.
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
aliens.draw(screen)
#Draw the score information.
sb.show_score()
#Draw the play_button if the game is inactivate.
if not stats.game_activate:
play_button.draw_button()
#make the current surface visible
pygame.display.flip()
def update_bullets(ai_settings,screen,stats,sb,ship,aliens,bullets):
"""update the position of the bullet and delete invisible bullets"""
bullets.update()
#delete invisible bullets
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
check_bullet_alien_collisions(ai_settings,screen,stats,sb,ship,aliens,
bullets)
def check_bullet_alien_collisions(ai_settings,screen,stats,sb,ship,aliens,
bullets):
"""check bullet alien collisions"""
collisions = pygame.sprite.groupcollide(bullets,aliens, True , True)
if collisions:
for aliens in collisions.values():
stats.score += ai_settings.alien_points * len(aliens)
sb.prep_score()
if len(aliens) == 0:
#Destroy existing bullets, speed up game, and create new fleet.
bullets.empty()
ai_settings.increase_speed()
create_fleet(ai_settings,screen,ship,aliens)
def fire_bullet(ai_settings, screen, ship, bullets):
"""if it doesn't touch the limit ,shoot a bullet"""
#create a bullet and join it to group 'bullets'
if len(bullets) < ai_settings.bullets_allowed:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def get_aliens_x(ai_settings, alien_width):
"""calculate how many aliens can be included in one line"""
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):
"""calculate how many lines can be included on the screen"""
available_space_y = (ai_settings.screen_height - (3 * alien_height) -
ship_height)
number_rows = int(available_space_y / (2 * alien_height))
return number_rows
def create_alien(ai_settings, screen, aliens, alien_number, row_number):
"""create an alien and put it in the current line"""
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 alien group """
alien = Alien(ai_settings, screen)
number_aliens_x = get_aliens_x(ai_settings, alien.rect.width)
number_rows = get_number_rows(ai_settings, ship.rect.height,
alien.rect.height)
#create alien group
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):
"""if aliens touch edge then ... """
for alien in aliens.sprites():
if alien.check_edges():
change_fleet_direction(ai_settings, aliens)
break
def change_fleet_direction(ai_settings, aliens):
"""move the aliens down and change their direction"""
for alien in aliens.sprites():
alien.rect.y += ai_settings.fleet_drop_speed
ai_settings.fleet_direction *= -1
def ship_hit(ai_settings,stats,screen,ship,aliens,bullets):
"""respond the ship that are hit"""
if stats.ships_left > 0:
#change ships_left by -1
stats.ships_left -= 1
else:
stats.game_activate = False
pygame.mouse.set_visible(True)
#clear Alien group and Bullet group
aliens.empty()
bullets.empty()
#create a new group of alien, and put the ship BOTTOM-MIDDLE.
create_fleet(ai_settings, screen, ship, aliens)
ship.center_ship()
#pause
sleep(0.5)
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
"""check if any aliens touches the edge"""
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom > screen_rect.bottom:
#do as ship-alien collisions
ship_hit(ai_settings,stats,screen,ship,aliens,bullets)
break
def update_aliens(ai_settings,stats,screen,ship,aliens,bullets):
"""update the position of alien groups """
check_fleet_edges(ai_settings, aliens)
aliens.update()
#check alien-ship collisions
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings,stats,screen,ship,aliens,bullets)
check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets)
scoreboard.py
import pygame.font
class Scoreboard():
"""A class to report scoring information."""
def __init__(self, ai_settings, screen, stats):
"""Initialize scorekeeping attributes."""
self.screen = screen
self.screen_rect = screen.get_rect()
self.ai_settings = ai_settings
self.stats = stats
#Font settings for scoring information.
self.text_color = (30,30,30)
self.font = pygame.font.SysFont(None, 48)
#Prepare the inital score image.
self.prep_score()
def prep_score(self):
"""Turn the score into a rendered image."""
rounded_score = int(round(self.stats.score, -1))
score_str = "{:,}".format(rounded_score)
self.score_image = self.font.render(score_str,True, self.text_color,
self.ai_settings.bg_color)
#Display the score at the top right of the screen.
self.score_rect = self.score_image.get_rect()
self.score_rect.right = self.screen_rect.right - 20
self.score_rect.top = 20
def show_score(self):
"""Draw score to the screen."""
self.screen.blit(self.score_image, self.score_rect)
game_stats.py
class GameStats():
"""Track statistics for Alien Invasion."""
def __init__(self, ai_settings):
"""Initialize statistics"""
self.ai_settings = ai_settings
self.reset_stats()
#Start game in an inactivate state
self.game_activate = False
def reset_stats(self):
"""initialize changeable statistics during the game"""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
I'm learning python and also pygame, and I want to know why the bullets doesn't display, I tough that maybe the screen.update_screen() can be interfering but no, that's not the case, I need help to undertand how Bullets work in python because clearly my method is not working, I've seen many methods in other posts, and they use a limited ammount of bullets to shoot (don't know why, in my case I want infinite bullets) so what should I add to see the Bullets, I know that I need to add a remover for seing a "movement" in the display, but I don't know how, any help is appreciated.
# -*- coding: utf-8 -*-
import pygame
class Screen(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
pygame.display.set_caption("Space Game")
self.screen_width = 800
self.screen_heigh = 600
self.picture = pygame.image.load("screen.png")
self.screen = pygame.display.set_mode((self.screen_width, self.screen_heigh))
def update_screen(self):
self.screen.blit(self.picture, (0, 0))
def update_obj(self, object):
self.screen.blit(object.picture, object.rect)
def update_shoot(self, object):
for y in range(object.rect.centery, 600, 10):
self.screen.blit(object.picture, (object.rect.centerx, object.rect.centery + y))
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.picture = pygame.image.load("ship.png")
self.rect = self.picture.get_rect()
self.rect.centerx = 400
self.rect.centery = 500
class Bullet(pygame.sprite.Sprite):
def __init__(self, object):
pygame.sprite.Sprite.__init__(self)
self.picture = pygame.image.load("shoot.png")
self.rect = self.picture.get_rect()
self.rect.centerx = object.rect.centerx
self.rect.centery = (object.rect.centery + 25)
def main():
pygame.init()
done = False
clock = pygame.time.Clock()
screen = Screen()
ship = Ship()
bullet = Bullet(ship)
while not done:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
ship.rect.centerx -= 5
if keys[pygame.K_RIGHT]:
ship.rect.centerx += 5
if keys[pygame.K_UP]:
ship.rect.centery -= 5
if keys[pygame.K_DOWN]:
ship.rect.centery += 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
done = True
if event.key == pygame.K_SPACE:
print ("shoot")
#for some weird reason the bullet doesn't display
screen.update_shoot(bullet)
screen.update_screen()
screen.update_obj(ship)
pygame.display.update()
clock.tick(10)
pygame.quit()
if __name__ == "__main__":
main()
To shoot bullets you usually create instances of a Bullet class and add them to a list, pygame.sprite.Group or another container. Then you iterate over this container and call the update method of the bullet in which its position is changed. To blit the images of the sprites/objects you iterate again over the container and just blit the images onto the screen. With sprite groups you can just call sprite_group.update() and sprite_group.draw(screen) instead of iterating yourself. BTW, pygame sprites have to have a self.image attribute not a self.picture in order to work with sprite groups (take a look at Program Arcade Games for more information).
I started to modify a few things in your example to show you how to use sprite groups, but then ended up changing your whole Screen class into a Game class (which I recommend to use in the future).
import sys
import pygame
class Game:
def __init__(self):
self.done = False
self.screen_width = 800
self.screen_height = 600
self.image = pygame.Surface((800, 600))
self.image.fill((30, 40, 50))
self.screen = pygame.display.set_mode(
(self.screen_width, self.screen_height))
# all_sprites is used to update and draw all sprites together.
self.all_sprites = pygame.sprite.Group()
# You'll probably need a separate bullet_group
# later for collision detection with enemies.
self.bullet_group = pygame.sprite.Group()
self.ship = Ship()
self.all_sprites.add(self.ship)
bullet = Bullet(self.ship)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def handle_events(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.ship.rect.centerx -= 5
if keys[pygame.K_RIGHT]:
self.ship.rect.centerx += 5
if keys[pygame.K_UP]:
self.ship.rect.centery -= 5
if keys[pygame.K_DOWN]:
self.ship.rect.centery += 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.done = True
if event.key == pygame.K_SPACE:
bullet = Bullet(self.ship)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
def update(self):
# Calls `update` methods of all contained sprites.
self.all_sprites.update()
def draw(self):
self.screen.blit(self.image, (0, 0))
self.all_sprites.draw(self.screen) # Draw the contained sprites.
pygame.display.update()
class Ship(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((20, 30))
self.image.fill((50, 170, 230))
# A nicer way to set the start pos with `get_rect`.
self.rect = self.image.get_rect(center=(400, 500))
class Bullet(pygame.sprite.Sprite):
def __init__(self, ship):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((7, 7))
self.image.fill((230, 140, 30))
self.rect = self.image.get_rect()
self.rect.centerx = ship.rect.centerx
self.rect.centery = ship.rect.centery - 25
def update(self):
self.rect.y -= 5 # Move up 5 pixels per frame.
def main():
pygame.init()
pygame.display.set_caption('Space Game')
clock = pygame.time.Clock()
game = Game()
while not game.done:
game.handle_events()
game.update()
game.draw()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()
sys.exit()