I am a learner of the Python Crash Course and now I am working on the alien invasion program. I just finished the chapter about adding bullets to my spaceship and when I press the space button, python threw "AttributeError: 'Ship' object has no attribute 'bullet_width'". I had tried to debug it and run it step by step but I couldn't find the mistake I've made. Can anybody help me to deal with it? Here are my codes:
alien_invasion(main code):
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():
# 初始化游戏并创建一个屏幕对象
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# 创建一艘飞船
ship = Ship(ai_settings, screen)
# 创建一个用于存储子弹的编组
bullets = Group()
bg_color = (230, 230, 230)
# 开始游戏的主循环
while True:
gf.check_events(ship, ai_settings, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
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
# 在(0, 0)处创建一个表示子弹的矩形,再设置正确的位置
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
# 更新表示子弹的rect的位置
self.rect.y = self.y
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(self.screen, self.color, self.rect)
game_function.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:
#创建一颗子弹,并将其加入到编组bullet中
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
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.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, bullets):
"""更新屏幕上的图像,并切换到新屏幕"""
# 每次循环时都重绘屏幕
screen.fill(ai_settings.bg_color)
# 在飞船和外星人后面重绘所有子弹
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
#让最近绘制的屏幕可见
pygame.display.flip()
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('images/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
# 在飞船的属性center中存储最小数值
self.center = float(self.rect.centerx)
#移动标志
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
def update(self):
"""根据移动标志调整飞船的位置"""
# 更新飞船的center值,而不是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
#根据self.center更新rect对象
self.rect.centerx = self.center
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
settings.py:
class Settings():
"""存储《外星人入侵》的所有设置的类"""
def __init__(self):
"""初始化游戏的设置"""
#屏幕设置
self.screen_width = 1300
self.screen_height = 680
self.bg_color = (230, 230, 230)
#飞船的设置
self.ship_speed_factor = 1.5
#子弹设置
self.bullet_speed_factor = 1.5
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
Thanks a lot, man!
See the signature of the check_events function:
def check_events(ai_settings, screen, ship, bullets):
However, when you call check_events, the ship is passed twice, but the screen is missing:
gf.check_events(ship, ai_settings, ship, bullets)
gf.check_events(ai_settings, screen, ship, bullets)
Related
please help me solve the error.
I have already double-checked everything and nothing works, the same traceback
Here is the full Traceback:
File "E:\Python\Alien_Invasion\alien_invasion.py", line 29, in <module>
run_game()
File "E:\Python\Alien_Invasion\alien_invasion.py", line 23, in run_game
gf.check_events(ai_settings, ship, screen, bullets)
File "E:\Python\Alien_Invasion\game_functions.py", line 29, in check_events
check_keydown_events(event, ship, ai_settings, screen, bullets)
File "E:\Python\Alien_Invasion\game_functions.py", line 13, in check_keydown_events
new_bullet = Bullet(ai_settings, screen, ship)
File "E:\Python\Alien_Invasion\bullet.py", line 12, in __init__
self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)
AttributeError: 'pygame.Surface' object has no attribute 'bullet_width'
Here is the code for the "bullet.py" file that has the problem:
class Bullet(Sprite):
def __init__(self, ai_settings, ship, screen):
#Создаєм обєкт пулі
super(Bullet, self).__init__()
self.screen = screen
#Создаем пулю в позиции (0,0)
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):
#Перемещает пулю по y
self.y -= self.speed_factor
#Обновлює позицію пулі в float
self.rect.y = self.y
def draw_bullet(self):
#Выводит пули на экран
pygame.draw.rect(self.screen, self.color, self.rect)
This is a class setting with instantiate Bullet object
class Settings():
def __init__(self):
self.screen_width = 1200
self.screen_hight = 800
self.bg_color = (20,228,255)
self.ship_speed_factor = 1
#Параметры пуль
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 253,251,255
This is the main module in which the while loop happens.
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
from pygame.sprite import Group
def run_game():
#Ініціалізує pygame, setting і створює екран
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width,
ai_settings.screen_hight))
pygame.display.set_caption("Alien Invasion")
#Создаєм корабль
ship = Ship(screen, ai_settings)
#Створюєм группу для хранения пуль
bullets = Group()
#Запуск основного циклу гри
while True:
gf.check_events(ai_settings, ship, screen, bullets)
ship.update()
ship.blitme()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
This is a module with a functional game program
import sys
import pygame
from bullet import Bullet
def check_keydown_events(event, ship, screen, ai_settings, 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:
#Создание новой пули и занесение её в группу bullets
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
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(ship, ai_settings, screen, bullets):
#Обработка нажатия клавиш и мышки
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship, ai_settings, screen, bullets)
def update_screen(ai_settings, screen, ship, bullets):
#Обновляет экран, и последнюю картинку
for bullet in bullets.sprites():
bullet.draw_bullet()
screen.fill(ai_settings.bg_color)
ship.blitme()
#Отображает последний экран
pygame.display.flip()
The signature of is check_keydown_events
def check_keydown_events(event, ship, screen, ai_settings, bullets):
When calling the function in check_events you accidentally swapped the ship and ai_settings arguments
check_keydown_events(event, ship, ai_settings, screen, bullets)
The correct call is:
check_keydown_events(event, screen, ship, ai_settings, bullets)
I am doing Python Crash Course Alien invasion and when I test this code, an error pops out saying that "Bullet" has no object "speed_factor"
I've been trying for so long to find out why, checked multiple times the code and even compared it to other codes, but it all seems fine.
The game code (alien_invasion.py):
import sys
import game_functions as gf
import pygame
from settings import Settings
from ship import Ship
from pygame.sprite import Group
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")
screen.fill(ai_settings.bg_color)
# Make a ship.
ship = Ship(screen, ai_settings)
bullets = Group()
# Background color
bg_color = (230, 230, 230)
while True:
gf.check_events(ai_settings, screen, ship, bullets)
gf.update_screen(ai_settings, bullets, screen, ship)
gf.check_events(ship, screen, ship, bullets)
ship.update(ai_settings)
bullets.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(ai_settings.bg_color)
ship.blitme()
pygame.display.flip()
run_game()
Settings code (settings.py):
class Settings():
def __init__(self):
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
self.ship_speed_factor = 2
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 12
self.bullet_color = (60, 60, 60)
self.bullet_limit = 5
Ship code (ship.py):
import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
def __init__(self, screen, ai_settings):
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load(r'C:\Users\user\Desktop\alien invasion\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)
self.center += self.ai_settings.ship_speed_factor
def update(self, ai_settings):
self.ai_settings = ai_settings
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.centerx += 1
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 -= 1
def blitme(self):
self.screen.blit(self.image, self.rect)
Bullet code (bullet.py):
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, ai_settings, screen, ship):
super().__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.ship_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 pygame
from bullet import Bullet
def check_keydown_events(event, ship, ai_settings, screen, 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:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
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.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event,ship, ai_settings, screen, bullets)
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def update_screen(ai_settings, bullets, screen, ship):
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blitme()
Error log:
File "C:\Users\user\Desktop\alien invasion\bullet.py", line 17, in update
self.y -= self.speed_factor
AttributeError: 'Bullet' object has no attribute 'speed_factor'
I would really appreciate any kind of help as this is driving me crazy.
In the __init__ of your Bullet class, you're setting self.ship_speed_factor but in its update method you're trying to do self.y -= self.speed_factor.
I guess you're just using a wrong field name, in which case you should do either of:
replace self.y -= self.speed_factor with self.y -= self.ship_speed_factor in update
replace self.ship_speed_factor = ... with self.speed_factor = ... in __init__
I've been trying to solve this issue where all images are just completely messed up and don't show properly.
It doesn't pop up any error so it is hard to traceback the cause for this: Here is a more detailed image of the issue
The code is as follows:
main code (alien_invasion.py):
import sys
import game_functions as gf
import pygame
from settings import Settings
from ship import Ship
from pygame.sprite import Group
from alien import Alien
from game_stats import GameStats
from button import Button
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")
play_button = Button(ai_settings, screen, "Play")
stats = GameStats(ai_settings)
alien = Alien(ai_settings, screen)
screen.fill(ai_settings.bg_color)
# Make a ship.
ship = Ship(ai_settings, screen)
aliens = Group()
bullets = Group()
# Background color
bg_color = (230, 230, 230)
gf.create_fleet(ai_settings, screen, aliens, ship)
while True:
gf.check_events(ai_settings, screen, ship, bullets, stats, play_button)
if stats.game_active:
ship.update(ai_settings)
gf.update_aliens(ai_settings, aliens, ship, stats, screen, bullets)
gf.update_bullets(bullets, aliens, ai_settings, screen, ship)
gf.update_screen(ai_settings, bullets, screen, ship, aliens, stats, play_button)
if not stats.game_active:
play_button.draw_button()
screen.fill(ai_settings.bg_color)
ship.blitme(screen)
pygame.display.flip()
run_game()
Alien code (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(r'C:\Users\user\Desktop\alien invasion\alien.bmp')
#Load the alien image and set its rect attribute
self.rect = self.image.get_rect()
#Start each new alienn near the top left of the screen
self.rect.x = self.rect.width
self.rect.y = self.rect.height
#Store the alien's exact position
self.x = float(self.rect.x)
def blitme(self):
#Draw the alien at its current location
self.screen.blit(self.image, self.rect)
def update(self):
self.x += (self.ai_settings.alien_speed_factor * self.ai_settings.fleet_direction)
self.rect.x = self.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
Bullet code (bullet.py):
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, ai_settings, screen, ship):
super().__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.ship_speed_factor = ai_settings.bullet_speed_factor
def update(self):
self.y -= self.ship_speed_factor
self.rect.y = self.y
def draw_bullet(self):
pygame.draw.rect(self.screen, self.color, self.rect)
Button code (button.py):
import pygame.font
class Button():
def __init__(self, ai_settings, screen, msg):
self.screen = screen
self.screen_rect = screen.get_rect()
self.width, self.height = 200, 50
self.button_color = (0, 250, 0)
self.text_color = (255, 255, 255)
self.font = pygame.font.SysFont(None, 48)
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = (self.screen_rect.center)
self.prep_msg(msg)
def prep_msg(self, msg):
self.msg_image = self.font.render(msg, True, self.text_color, self.button_color)
self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
def draw_button(self):
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
Game function code (game_functions.py):
import sys
import pygame
from bullet import Bullet
from alien import Alien
from time import sleep
def check_events(ai_settings, screen, ship, bullets, stats, play_button):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
check_play_button(stats, play_button, mouse_x, mouse_y)
elif event.type == pygame.KEYDOWN:
check_keydown_events(event,ship, ai_settings, screen, bullets)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, bullets, screen, ship, aliens, stats, play_button):
for bullet in bullets.sprites():
bullet.draw_bullet()
aliens.draw(screen)
ship.blitme(screen)
pygame.display.flip()
def check_keydown_events(event, ship, ai_settings, screen, 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 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 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 update_bullets(bullets, aliens, ai_settings, screen, ship):
bullets.update()
collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
if len(aliens) == 0:
bullets.empty()
create_fleet(ai_settings, screen, aliens, ship)
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def create_fleet(ai_settings, screen, aliens, ship):
alien = Alien(ai_settings, screen)
alien_width = alien.rect.width
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 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 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 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 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 update_aliens(ai_settings, aliens, ship, stats, screen, bullets):
check_fleet_edges(ai_settings, aliens)
aliens.update()
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)
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 ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
if stats.ships_left > 0:
stats.ships_left -= 1
aliens.empty()
bullets.empty()
create_fleet(ai_settings, screen, aliens, ship)
ship.center_ship
sleep(0.5)
else:
stats.game_active = False
def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
break
def check_play_button(stats, play_button, mouse_x, mouse_y):
if play_button.rect.collidepoint(mouse_x, mouse_y):
stats.game_active = True
Game stats (game_stats.py):
class GameStats():
def __init__(self, ai_settings):
self.ai_settings = ai_settings
self.reset_stats()
self.game_active = True
def reset_stats(self):
self.ships_left = self.ai_settings.ship_limit
Settings code (settings.py):
class Settings():
def __init__(self):
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
self.ship_speed_factor = 10
self.ship_limit = 3
self.bullet_speed_factor = 3
self.bullet_width = 3
self.bullet_height = 12
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 3
self.alien_speed_factor = 1
self.fleet_drop_speed = 10
self.fleet_x_margin = 70
self.fleet_y_margin = 70
self.fleet_direction = -1
Ship code (ship.py):
import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
def __init__(self, ai_settings, screen):
super(Ship, self).__init__()
self.screen = screen
self.ai_settings = ai_settings
self.image = pygame.image.load(r'C:\Users\user\Desktop\alien invasion\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)
self.center += self.ai_settings.ship_speed_factor
def update(self, ai_settings):
self.ai_settings = ai_settings
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.centerx += 1
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 -= 1
def blitme(self, screen):
self.screen.blit(self.image, self.rect)
def center_ship(self):
self.center = self.screen_rect.centerx
Any help would really appreciate it!
Simple change needed. Clear the screen before drawing the aliens.
while True:
screen.fill(ai_settings.bg_color) # <<<<<<< clear screen first
gf.check_events(ai_settings, screen, ship, bullets, stats, play_button)
if stats.game_active:
ship.update(ai_settings)
gf.update_aliens(ai_settings, aliens, ship, stats, screen, bullets)
gf.update_bullets(bullets, aliens, ai_settings, screen, ship)
gf.update_screen(ai_settings, bullets, screen, ship, aliens, stats, play_button)
if not stats.game_active:
play_button.draw_button()
Following "Python Crash Course" and I'm stuck while trying to group all the bullets on the screen, I Get an AttributeError:
Traceback (most recent call last):
File "C:/Users/esmai/PycharmProjects/AllienInvasion1/allien_invasion.py", line 27, in <module>
run_game()
File "C:/Users/esmai/PycharmProjects/AllienInvasion1/allien_invasion.py", line 24, in run_game
gf.update_screen(ai_settings, screen, ship, bullets)
File "C:\Users\esmai\PycharmProjects\AllienInvasion1\game_functions.py", line 36, in update_screen
for bullet in bullets.sprite():
AttributeError: 'Group' object has no attribute 'sprite'
Process finished with exit code 1
Here are the files (as the error only occurred in alien_invasion.py and game_functions.py, those two will be the first two folders you see followed by bullet.py...):
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():
pygame.init()
# the .display tells python to set display somehow, duh
ai_settings = Settings()
screen = pygame.display.set_mode(
(ai_settings.screen_width, ai_settings.screen_height)
)
pygame.display.set_caption("Alien Invasion")
ship = Ship(ai_settings, screen)
bullets = Group()
while True:
gf.check_event(ai_settings, screen, ship, bullets)
ship.update()
bullets.update()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
game_functions.py:
import pygame
import sys
from bullet import Bullet
def check_keydown_event(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:
new_bullet = Bullet(ai_settings, screen, ship)
bullets.add(new_bullet)
def check_keyup_event(event, ship):
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
def check_event(ai_settings, screen, ship, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_event(event, ai_settings, screen, ship, bullets)
elif event.type == pygame.KEYUP:
check_keyup_event(event, ship)
def update_screen(ai_settings, screen, ship, bullets):
screen.fill(ai_settings.bg_color)
for bullet in bullets.sprite():
bullet.draw_bullet()
'the Group AttributeError occurs here as grouping bullets do not have attribute sprite, apparently.'
ship.blitme()
pygame.display.flip()
bullet.py:
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
def __init__(self, ai_settings, ship, screen):
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)
ship.py:
import pygame
class Ship:
def __init__(self, ai_settings, screen):
self.screen = screen
self.image = pygame.image.load('ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.ai_settings = ai_settings
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
self.center = float(self.rect.centerx)
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.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)
settings.py:
class Settings:
def __init__(self):
self.screen_width = 1200
self.screen_height = 700
self.bg_color = (230, 230, 230)
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 225, 69, 0
# For ship Speed
self.ship_speed_factor = 1.5
This post here has a couple of problems.
the biggest problem is that the code was generated from an old edition of the book, as the author of the book stated in the comments.
next, the problems in this post have stated from the perspective of a newcomer (I). so the main problem occurred from naming the variables wrong or different from a file to another, again stated by the author. So when I redo the project, I nailed it in the head >>boom<<, of course, by changing to the second edition of the Python Crash course.
so what to learn from this.
name variables correctly.
listen to the author's podcasts, in a podcast he once said that "new programmers buy early editions..." and he was right (check the edition of the book, K?)
don't follow the code blindly, change aspects of the code to your tunning.
download the resources, it is free but the knowledge in it boils the ocean.
thanks for the help, each comment put me on pins and needles. in short thanks.
Trying to make a game where my catcher catches a baseball. If the baseball is caught, then a new baseball appears randomly at the top of the screen. I can't seem to move the catcher after the changes I've made. The game is made through python and pygame.
catch.py
import sys
import pygame
from pygame.sprite import Group
from settings import Settings
from catcher import Catcher
from baseball import Baseball
import game_functions as gf
def run_game():
"""Initializes the game, screen, and settings."""
pygame.init()
ai_settings = Settings()
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
pygame.display.set_caption("Catch")
catcher = Group()
baseball = Group()
while True:
gf.check_events(catcher)
gf.update_catcher(ai_settings, screen, catcher)
gf.update_baseball(ai_settings, screen, catcher, baseball)
gf.update_screen(ai_settings, screen, catcher, baseball)
run_game()
baseball.py
import pygame
from pygame.sprite import Sprite
class Baseball(Sprite):
"""A class to represent a baseball."""
def __init__(self, ai_settings, screen):
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the baseball image and set its rect attribute.
self.image = pygame.image.load('images/baseball.bmp')
self.rect = self.image.get_rect()
# Start each new baseball at a random position at the top of the screen.
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# Store the alien's exact position.
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def blitme(self):
"""Draw the baseball at tis current location."""
self.screen.blit(self.image, self.rect)
def update(self):
"""Dropping the baseball."""
self.y += self.ai_settings.baseball_drop_speed
self.rect.y = self.y
catcher.py
import pygame
from pygame.sprite import Sprite
class Catcher(Sprite):
def __init__(self, ai_settings, screen):
"""Initialize the catcher and set its starting position."""
super().__init__()
self.screen = screen
self.ai_settings = ai_settings
# Load the catcher image and get its rect.
self.image = pygame.image.load('images/catcher.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# Start each new catcher at the bottom center of the screen.
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the catcher's center.
self.center = float(self.rect.centerx)
# Movement flags
self.moving_right = False
self.moving_left = False
def update(self, ai_settings, screen):
"""Update the catcher's position baed on the movement flag."""
# Update the catcher's center value, not the rect.
if self.moving_right and self.rect.right <= self.screen_rect.right:
self.center += self.ai_settings.catcher_speed_factor
if self.moving_left and self.rect.left >= 0:
self.center -= self.ai_settings.catcher_speed_factor
# Update rect object from self.center.
self.rect.centerx = self.center
def blitme(self):
"""Draw the catcher at its current location."""
self.screen.blit(self.image, self.rect)
game_functions.py
import sys
import pygame
from baseball import Baseball
from catcher import Catcher
from random import randint
def check_keydown_events(event, catcher):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
catcher.moving_right = True
elif event.key == pygame.K_LEFT:
catcher.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, catcher):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
catcher.moving_right = False
elif event.key == pygame.K_LEFT:
catcher.moving_left = False
def check_events(catcher):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, catcher)
elif event.type == pygame.KEYUP:
check_keyup_events(event, catcher)
def update_screen(ai_settings, screen, catcher, baseball):
"""Update images on the screen and flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(ai_settings.bg_color)
catcher.draw(screen)
baseball.draw(screen)
# Make the most recently drawn screen visible.
pygame.display.flip()
def update_baseball(ai_settings, screen, catcher, baseball):
"""Update the positions of the baseball and detect collisions."""
screen_rect = screen.get_rect()
baseball.update()
if len(baseball) == 0:
new_ball = Baseball(ai_settings, screen)
new_ball.x = randint(new_ball.rect.width, screen_rect.right - new_ball.rect.width)
new_ball.rect.x = new_ball.x
new_ball.y = new_ball.rect.height
new_ball.rect.y = new_ball.y
baseball.add(new_ball)
collisions = pygame.sprite.groupcollide(baseball, catcher, True, False)
def update_catcher(ai_settings, screen, catcher):
screen_rect = screen.get_rect()
catcher.update(ai_settings, screen)
if len(catcher) == 0:
new_catcher = Catcher(ai_settings, screen)
new_catcher.center = screen_rect.centerx
new_catcher.rect.centerx = new_catcher.center
new_catcher.rect.bottom = screen_rect.bottom
catcher.add(new_catcher)
settings.py
import pygame
class Settings():
"""Class to store settings."""
def __init__(self):
"""Initializes the game's settings."""
# Screen settings.
self.screen_width = 800
self.screen_height = 600
self.bg_color = (255, 255, 255)
# Ship settings.
self.catcher_speed_factor = 1.5
# Baseball settings.
self.baseball_drop_speed = float(1 / 2)
The issue is that catcher is an instance of Group rather than Catcher(Sprite):
catcher = Group()
while True:
check_events(catcher)
That causes that the attributes moving_right and moving_left are set to the Group object in check_keydown_events respectively check_keyup_events.
You have to change the attributes of the objects in the Group:
def check_keydown_events(event, catcher):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
for c in catcher:
c.moving_right = True
elif event.key == pygame.K_LEFT:
for c in catcher:
c.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, catcher):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
for c in catcher:
c.moving_right = False
elif event.key == pygame.K_LEFT:
for c in catcher:
c.moving_left = False