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)
Related
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)
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.
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.