This question already has answers here:
Created multiple instances of the same image using a loop, can I move each instance of the image independently?
(1 answer)
Why do group lists in pygame have to have "update" functions, and not any other?
(1 answer)
Closed 2 years ago.
I am new to Python and got stuck with some Pygame code (see below traceback and complete code). I don't understand why the bullet object has no rect attribute when I iterate over the items stored in self.bullets. Any help or pointers would be hugely appreciated. Thanks!
Traceback
Traceback (most recent call last):
File "/Users/svengerlach/PycharmProjects/AlienInvasion/practice_12_6.py", line 82, in <module>
game.game_loop()
File "/Users/svengerlach/PycharmProjects/AlienInvasion/practice_12_6.py", line 22, in game_loop
self._bullet_update()
File "/Users/svengerlach/PycharmProjects/AlienInvasion/practice_12_6.py", line 54, in _bullet_update
if bullet.rect.left > self.screen_rect.right:
AttributeError: 'Bullet' object has no attribute 'rect'
Complete Code
import pygame
import sys
class Game:
def __init__(self):
[...]
self.bullets = pygame.sprite.Group()
def game_loop(self):
while True:
self._check_user_inputs()
self._ship_update()
self._bullet_update()
self._screen_update()
def _check_user_inputs(self):
for event in pygame.event.get():
elif event.type == pygame.KEYDOWN:
[...]
elif event.key == pygame.K_SPACE:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
elif event.type == pygame.KEYUP:
[...]
def _ship_update(self):
[...]
def _bullet_update(self):
self.bullets.update()
for bullet in self.bullets.copy():
if bullet.rect.left > self.screen_rect.right:
self.bullets.remove(bullet)
def _screen_update(self):
[...]
for bullet in self.bullets.sprites():
bullet.draw_bullet()
pygame.display.flip()
class Bullet(pygame.sprite.Sprite):
def __init__(self, ai_game):
super().__init__()
self.screen = ai_game.screen
self.ship_rect = ai_game.ship_rect
self.bullet_rect = pygame.Rect(0, 0, 15, 3)
self.bullet_rect.midleft = self.ship_rect.midright
def update(self):
self.bullet_rect.x += 5
def draw_bullet(self):
pygame.draw.rect(self.screen, (60, 60, 60), self.bullet_rect)
if __name__ == '__main__':
game = Game()
game.game_loop()
bullet has no attribute rect, but it has an attribute bullet_rect. I recommend to rename bullet_rect to rect. This way you can use pygame.sprite.Group.draw():
Draws the contained Sprites to the Surface argument. This uses the Sprite.image attribute for the source surface, and Sprite.rect for the position.
Furthermore use pygame.sprite.Sprite.kill():
The Sprite is removed from all the Groups that contain it. [...]
Example:
class Bullet(pygame.sprite.Sprite):
def __init__(self, ai_game):
super().__init__()
self.image = pygame.Surface((15, 3))
self.image.fill((60, 60, 60))
self.rect = self.image.get_rect(midleft = ai_game.ship_rect.midright)
def update(self):
self.rect.x += 5
class Game:
def __init__(self):
# [...]
self.bullets = pygame.sprite.Group()
def game_loop(self):
while True:
self._check_user_inputs()
self._ship_update()
self._bullet_update()
self._screen_update()
def _check_user_inputs(self):
for event in pygame.event.get():
elif event.type == pygame.KEYDOWN:
# [...]
elif event.key == pygame.K_SPACE:
self.bullets.add(Bullet(self))
elif event.type == pygame.KEYUP:
# [...]
def _bullet_update(self):
self.bullets.update()
for bullet in self.bullets:
if bullet.rect.left > self.screen_rect.right:
bullet.kill()
def _screen_update(self):
# [...]
self.bullets.draw(ai_game.screen)
pygame.display.flip()
Related
I have an error where python is not able to find my file. (if I understand correctly) I can't find what I have missed. If anyone could take the time to point it out to me, it would be much appreciated.
def _check_target_edges(self):
"""Respond appropriately if the target has reached an edge."""
if sideways_target.check_edges():
self._change_target_direction()
this code is giving error messages
pygame 2.1.2 (SDL 2.0.18, Python 3.10.5)
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
File "C:\Users\2\Desktop\python_work\Chapter 11 - 15\Chapter 14\14.2 Target Practice.py", line 218, in <module>
ss.run_game()
File "C:\Users\2\Desktop\python_work\Chapter 11 - 15\Chapter 14\14.2 Target Practice.py", line 53, in run_game
self._update_target()
File "C:\Users\2\Desktop\python_work\Chapter 11 - 15\Chapter 14\14.2 Target Practice.py", line 157, in _update_target
self._check_target_edges()
File "C:\Users\2\Desktop\python_work\Chapter 11 - 15\Chapter 14\14.2 Target Practice.py", line 207, in _check_target_edges
if sideways_target.check_edges():
NameError: name 'sideways_target' is not defined
[Finished in 2.7s]
def check_edges(self):
"""Return True if target is at edge of screen."""
screen_rect = self.screen.get_rect()
if self.rect.bottom >= screen_rect.bottom or self.rect.top <= 0:
return True
This is what I want the code to find. So it updates any changes in True/False.
I would like to know where can I define 'sideways_target' so the error clears up and I can run the project.
If you need more detail... the files are down below
File of the 1st piece of code
# ship spawns on the left side
# bullets travel to the right
# target moves up and down
# shoot the target
#
import sys, pygame
from sideways_settings import Settings
from sideways_ship import Ship
from sideways_bullet import Bullet
from sideways_game_stats import GameStats
from sideways_target import Target
from sideways_button import Button
class ShipShooter:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.sideways_settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.sideways_settings.screen_width = self.screen.get_rect().width
self.sideways_settings.screen_width = self.screen.get_rect().height
pygame.display.set_caption("Sideways Shooter")
# Create an instance to store game statistics.
self.sideways_stats = GameStats(self)
self.sideways_ship = Ship(self)
self.bullets = pygame.sprite.Group()
# Make the button.
self.sideways_play_button = Button(self, "Play")
# Make the target
self.sideways_target = Target(self)
# Set the background color.
self.bg_color = (230, 230, 230)
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
if self.sideways_stats.game_active:
self.sideways_ship.update()
self._update_bullets()
self._update_target()
self._update_screen()
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self, mouse_pos):
"""Start a new game when the player clicks Play."""
button_clicked = self.sideways_play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.sideways_stats.game_active:
# Reset the game statistics.
self.sideways_stats.reset_stats()
self.sideways_stats.game_active = True
# Get rid of any remaining bullets.
self.bullets.empty()
# Center the ship
self.sideways_ship.center_ship()
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
def _check_play_button2(self):
"""Start a new game when the player clicks Play."""
if self.key_p == True:
# Reset the game statistics.
self.sideways_stats.reset_stats()
self.sideways_stats.game_active = True
# Get rid of any remaining bullets.
self.bullets.empty()
# Center the ship
self.sideways_ship.center_ship()
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
def _check_keydown_events(self, event):
"""Respond to keypresses."""
if event.key == pygame.K_UP:
self.sideways_ship.moving_up = True
elif event.key == pygame.K_DOWN:
self.sideways_ship.moving_down = True
elif event.key == pygame.K_q:
sys.exit()
elif event.key == pygame.K_SPACE:
self._fire_bullet()
elif event.key == pygame.K_p:
self.key_p = True
self._check_play_button2()
def _check_keyup_events(self, event):
"""Respond to key releases."""
if event.key == pygame.K_UP:
self.sideways_ship.moving_up = False
elif event.key == pygame.K_DOWN:
self.sideways_ship.moving_down = False
def _fire_bullet(self):
"""Create a new bullet and add it to the bullets group."""
if len(self.bullets) < self.sideways_settings.bullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update position of bullets and get rid of old bullets."""
# Update bullet positions.
self.bullets.update()
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.right >= self.sideways_ship.screen_rect.right :
self.bullets.remove(bullet)
# print(len(self.bullets))
# Look for bullets hitting the side of the screen.
self._check_bullets_bottom()
def _check_bullet_target_collisions(self):
"""Respond to bullet-target collisions."""
# Remove any bullets that have collided with the target
collisions = pygame.sprite.groupcollide(
self.bullets, self.sideways_target, True, True)
def _update_target(self):
"""
Check if the target is at the edge,
then update the position of the target
"""
self._check_target_edges()
self.sideways_target.update()
# Look for bullet-target collisions.
if pygame.sprite.spritecollideany(self.bullets, self.sideways_target):
self._target_hit()
def _target_hit(self):
"""Respond to the target being hit by a bullet."""
if self.sideways_stats.targets_left > 0:
# Decrement targets_left.
self.sideways_stats.targets_left -= 1
# Get rid of any remaining bullets.
self.bullets.empty()
# Center the ship.
self.sideways_ship.center_ship()
# Pause.
sleep(1.5)
else:
self.sideways_stats.game_active = False
pygame.mouse.set_visible(True)
def _check_bullets_bottom(self):
"""Check if any bullets have reached the side of your screen."""
screen_rect = self.screen.get_rect()
for bullet in self.bullets.sprites():
if bullet.rect.right >= screen_rect.right:
# Treat this the same as if the target got hit.
self.target_hit()
break
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
self.screen.fill(self.sideways_settings.bg_color)
self.sideways_ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.sideways_target.draw_target()
# Draw the play button if the game is inactive.
if not self.sideways_stats.game_active:
self.sideways_play_button.draw_button()
pygame.display.flip()
def _check_target_edges(self):
"""Respond appropriately if the target has reached an edge."""
if sideways_target.check_edges():
self._change_target_direction()
def _change_target_direction(self):
"""Change the target's direction."""
self.sideways_settings.target_direction *= -1
if __name__ == '__main__':
# Make a game instance, and run the game.
ss = ShipShooter()
ss.run_game()
File of the 2nd piece of code
import pygame
from pygame.sprite import Sprite
class Target:
def __init__(self, ai_game):
"""Initialize target attributes."""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
# Set the dimensions and properties of the target
self.width, self.height = 70, 300
self.target_color = (0, 0, 0)
# Build the target's rect object and center it.
self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.midright = self.screen_rect.midright
# Store the target's exact vertical position.
self.y = float(self.rect.y)
def check_edges(self):
"""Return True if target is at edge of screen."""
screen_rect = self.screen.get_rect()
if self.rect.bottom >= screen_rect.bottom or self.rect.top <= 0:
return True
def update(self):
"""Move the target up or down."""
self.y += (self.sideways_settings.target_speed *
self.sideways_settings.target_direction)
self.rect.y = self.y
def draw_target(self):
# Draw the target to the screen
pygame.draw.rect(self.screen, self.target_color, self.rect)
I want to access my player position, I tried following Moving a Sprite Class from outside of the original Class in pygame but then I got "AttributeError: 'GroupSingle' object has no attribute 'rect'" as my error. I need to access player position inside my obstacle class.
This is my code:
import pygame
import os
import random
from sys import exit
pygame.init()
os.system("cls")
WIDTH = 288
HEIGHT = 512
FPS = 60
JUMP_POWER = 60
GRAVITY = 0.15
GAME_ACTIVE = 1
OBSTACLE_INTERVAL = 1000
AWAY_FROM_BIRD = 100
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("assets/images/player/bird.png").convert_alpha()
self.rect = self.image.get_rect(center = (WIDTH/4, HEIGHT/2))
self.gravity_store = 0
self.jump_sfx = pygame.mixer.Sound("assets/audio/jump.wav")
self.jump_sfx.set_volume(0.1)
def player_input(self):
for event in event_list:
if event.type == pygame.KEYDOWN:
if self.rect.bottom <= 0:
pass
else:
if event.key == pygame.K_SPACE:
self.gravity_store = 0
self.rect.y -= JUMP_POWER
self.jump_sfx.play()
def gravity(self):
self.gravity_store += GRAVITY
self.rect.y += self.gravity_store
print(int(self.gravity_store), int(clock.get_fps()))
def collision(self):
if self.rect.colliderect(ground_rect):
GAME_ACTIVE = 0
def update(self):
self.player_input()
self.gravity()
self.collision()
class Obstacles(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("assets/images/obstacles/pipe-green.png")
def obstacle_spawn(self):
#I want to access player position here
print(player.rect.x)
self.rect = self.image.get_rect(midleft = (0, 0))
def update(self):
self.obstacle_spawn()
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mama Bird")
clock = pygame.time.Clock()
background_surf = pygame.image.load("assets/images/background/background-day.png")
ground_surf = pygame.image.load("assets/images/background/base.png")
ground_rect = ground_surf.get_rect(topleft = (0, HEIGHT-112))
player = pygame.sprite.GroupSingle()
player.add(Player())
obstacle = pygame.sprite.Group()
obstacle.add(Obstacles())
OBSTACLESPAWN = pygame.USEREVENT + 1
pygame.time.set_timer(OBSTACLESPAWN, OBSTACLE_INTERVAL)
while True:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
pygame.quit()
exit()
SCREEN.blit(background_surf, (0,0))
player.draw(SCREEN)
player.update()
for event in event_list:
if event.type == OBSTACLESPAWN and GAME_ACTIVE == 1:
obstacle.update()
obstacle.draw(SCREEN)
SCREEN.blit(ground_surf, ground_rect)
pygame.display.update()
clock.tick(FPS)
I tried to see if it would access the player position using "print(player.rect.x)". And got this error:
Traceback (most recent call last):
File "c:\Users\46722\Documents\Programmering\Python\yesman\Pygame\Mama Bird\main.py", line 98, in <module>
obstacle.update()
File "C:\Users\46722\AppData\Local\Programs\Python\Python310\lib\site-packages\pygame\sprite.py", line 539, in update
sprite.update(*args, **kwargs)
File "c:\Users\46722\Documents\Programmering\Python\yesman\Pygame\Mama Bird\main.py", line 65, in update
self.obstacle_spawn()
File "c:\Users\46722\Documents\Programmering\Python\yesman\Pygame\Mama Bird\main.py", line 61, in obstacle_spawn
print(player.rect.x)
AttributeError: 'GroupSingle' object has no attribute 'rect'
PS C:\Users\46722\Documents\Programmering\Python\yesman\Pygame\Mama Bird>
player is an instance of pygame.sprite.Group():
player = pygame.sprite.GroupSingle()
player.add(Player())
Use the sprite property to access the pygame.sprite.Sprite in a pygame.sprite.GroupSingle:
print(player.rect.x)
print(player.sprite.rect.x)
I am learning Python from the book, "PYTHON CRASH COURSE: A Hands-On, Project-Based Introduction to Programming", 2E.
In that while making game, I am getting the error
Traceback (most recent call last):
File "d:\BOOKS\python\my-projects\AlienInvasion\tempCodeRunnerFile.py", line 69, in <module>
alinv = AlienInvasion()
File "d:\BOOKS\python\my-projects\AlienInvasion\tempCodeRunnerFile.py", line 22, in __init__
self.ship = Ship(self.screen)
File "d:\BOOKS\python\my-projects\AlienInvasion\ship.py", line 10, in __init__
self.screen = alinv_game.screen
AttributeError: 'pygame.Surface' object has no attribute 'screen'
My Code is:
AlienInvasion.py
# Importing modules
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
'''Class to manage game assets and behavior'''
def __init__(self):
'''Constructor to initialize the game and its assets'''
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion")
self.ship = Ship(self.screen)
def run_game(self):
'''Starts the main loop for the game'''
while True:
self._check_events()
self.ship.update()
self._update_screen()
def _check_events(self):
'''Watch for the keyboard and mouse events'''
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_ESCAPE:
sys.exit()
def _check_keyup_events(self, event):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
'''Redraw the screen during each pass of the loop'''
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
'''Make the most recently drawn screen visible'''
pygame.display.flip()
if __name__ == '__main__':
'''Make a game instance, and run the game'''
alinv = AlienInvasion()
alinv.run_game()
ship.py
import pygame
class Ship:
'''A class to manage the ship'''
def __init__(self, alinv_game):
'''Initialize the ship and set its starting position'''
self.screen = alinv_game.screen
self.settings = alinv_game.settings
self.screen_rect = alinv_game.screen.get_rect()
'''Load the ship image and get its rect'''
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
'''Start each new ship at the bottom-center of the screen'''
self.rect.midbottom = self.screen_rect.midbottom
'''Store a decimal value for ship's horizontal position'''
self.x = float(self.rect.x)
'''Movement 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.x += self.settings.ship_speed
if self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
'''Update rect obj from self.x'''
self.rect.x = self.x
def blitme(self):
'''Draw the ship at its current location.'''
self.screen.blit(self.image, self.rect)
In the update method of class Ship, I Implemented two separate blocks to allow ship.rect.x value
to be increased and then decreased when both left and right
arrow keys are pressed. Otherwise, the self.moving_right will
have more priority if 'elif' is implemented.
settings.py
class Settings:
'''A class to store all settings for Alien Invasion Game.'''
def __init__(self):
'''Initialize the game's settings'''
# Screen Settings:
self.screen_width = 1280
self.screen_height = 720
self.bg_color = (230, 230, 230)
# Ship Settings:
self.ship_speed = 1.5
I think the error should be with the screen declaration because I think Pygame.Surface do not have 'screen' attribute but we can though get it by get_rect method. Kindly help me finding the bug. I am not able to solve the issue.
I believe you meant to make the line self.ship = Ship(self.screen) say self.ship = Ship(self). That way the Ship object can access all of the attributes of the AlienInvasion object, instead of just it's screen attribute (and that attribute doesn't have a screen attribute, thus the error).
As a beginner, I was trying the "Alien invasion' project in the python crash course a hands-on project-based introduction to programming and got stuck, I was just trying to copy the code but it always get the same feedback:
Traceback (most recent call last):
File "D:/snake/venv/Alien_Invasion/main_game.py", line 29, in <module>
run_game()
File "D:/snake/venv/Alien_Invasion/main_game.py", line 26, in run_game
bullets.update_bullets()
AttributeError: 'Group' object has no attribute 'update_bullets'
and here's my code:
main_game.py
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
import bullet
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 = pygame.sprite.Group()
#游戏主循环
while True:
gf.check_events(ai_settings, screen, ship, bullets)
ship.update_ship()
bullets.update_bullets()
gf.update_screen(ai_settings, screen, ship, bullets)
run_game()
bullet.py
import pygame
class Bullet(pygame.sprite.Sprite):
"""一个对飞船发射的子弹管理的类"""
def __init__(self, ai_settings, screen, ship):
"""在飞船所在位置创建一个子弹对象"""
self.screen = screen
super(Bullet, self).__init__()
#在(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_bullets(self):
"""向上移动子弹"""
#更新浮点数的子弹纵坐标
self.y -= self.speed_factor
#更新子弹纵坐标位置
self.rect.y = self.y
def draw_bullet(self):
"""在屏幕上绘制子弹"""
pygame.draw.rect(screen, self.color, self.rect)
game_functions.py
import sys
import pygame
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
#创建一个子弹,并加入bullets编组
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_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_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.sprites():
bullet.draw_bullet()
ship.blitme()
#最近绘制屏幕可见
pygame.display.flip()
there are two other files, but I don't think they are related to this problem.
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 = 1.5
#子弹设置
self.bullet_speed_factor = 1
self.bullet_width = 3
self.bullet_heght = 15
self.bullet_color = 60, 60, 60
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
def update_ship(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
#根据center更新rect.centerx值
self.rect.centerx = self.center
def blitme(self):
"""在指定位置绘制飞船"""
self.screen.blit(self.image, self.rect)
Can someone please tell me what I did wrong here? It would be much appreciated!
In your traceback error it says:
Traceback (most recent call last):
File "D:/snake/venv/Alien_Invasion/main_game.py", line 2, in <module>
from pygame import Group
importError: cannot import name 'Group' from 'pygame' (D:\snake\venv\lib\site-packages\pygame\__init__.py)
Which says there should be this line:
from pygame import Group
on line 2 of your main_game.py module. However in the code you have included, that is not there:
main_game.py
import pygame
from settings import Settings
from ship import Ship
Are you sure you are showing he correct code?
Ignoring the code and just looking at the error message it shows that you had this line:
from pygame import Group
There is no pygame.Group which is why you are getting the error telling you that. I think you are looking for pygame.sprite.Group
Edit
Now that we have the right code and the right question :-)
Sprite groups have some built-in methods which can be seen in the docs here. One of these is update() which will then call the update() function in all the sprites in the group. Your problem is that you named your sprite function update_bullets() not just update() and then you tried to call update_bullets() on the group and the group does not have that method.
So to fix your code you have to rename your Bullet method to just update() and then call the sprite.Group method update() to have it called on all the bullets in the group.
You have the same issue with your draw_bullet() method but you worked around it by iterating over the group and calling your method. That works, but you should correct it to take advantage of the builtin group method. You need to change it to just draw() and then you can call it on all the bullets by just calling bullets.draw(). As I said, in your code now, because you are not using the name that the sprite.Group knows how to find, you are iterating over the group. If you rename it, you can then replace this:
for bullet in bullets.sprites():
bullet.draw_bullet()
with just:
bullets.draw()
This question already has an answer here:
Faster version of 'pygame.event.get()'. Why are events being missed and why are the events delayed?
(1 answer)
Closed 2 years ago.
With the help of some person now I can press key to shoot bullet in Pygame but I have met another new issue. I define this in class Bullet:
def move(self):
self.y -= self.speed
self.bullet.y=self.y
def draw(self,setting):
p.draw.rect(setting.screen,self.color,self.bullet)
def bullet_blit(self,bullets,setting):
for bullet in bullets.sprites():
p.draw.rect(setting.screen,self.color,self.bullet)
self.y -= self.speed
self.bullet.y=self.y
Finally when I just use the code bullet.bullet_blit(bullets,setting) it will only one bullet when I press the key.
Instead I use
for bullet in bullets.sprites():
bullet.draw(setting)
bullet.move()
It worked correctly. I don't know why. They are the same, but their effect is not the same.
Here's the code:
#! /usr/bin/python
import pygame as p
import sys
class Setting():
def __init__(self,width,height):
self.w=width
self.h=height
self.flag=p.RESIZABLE
self.color=(255,255,255)
self.speed=1
self.screen=p.display.set_mode((self.w,self.h),self.flag)
p.display.set_caption("Bullet")
self.bullet_s=1
self.bullet_w=5
self.bullet_h=30
self.bullet_c=(0,0,0)
class Bullet(p.sprite.Sprite):
def __init__(self,setting):
super().__init__()
self.screen_rect=setting.screen.get_rect()
self.screen_center=self.screen_rect.center
self.bullet=p.Rect((0,0),(setting.bullet_w,setting.bullet_h))
self.bullet.center=self.screen_center
self.bullet.bottom=self.screen_rect.bottom
self.color=setting.bullet_c
self.speed=setting.bullet_s
self.y=float(self.bullet.centery)
def bullet_check(self,bullets,setting):
for event in p.event.get():
if event.type == p.QUIT:
sys.exit()
elif event.type == p.KEYDOWN:
if event.key ==p.K_SPACE:
bullets.add(Bullet(setting))
def move(self):
self.y -= self.speed
self.bullet.y=self.y
def draw(self,setting):
p.draw.rect(setting.screen,self.color,self.bullet)
def bullet_blit(self,bullets,setting):
for bullet in bullets.sprites():
p.draw.rect(setting.screen,self.color,self.bullet)
self.y -= self.speed
self.bullet.y=self.y
def game():
p.init()
setting=Setting(1200,800)
bullet=Bullet(setting)
bullets=p.sprite.Group()
while True:
bullet.bullet_check(bullets,setting)
setting.screen.fill((255,0,0))
bullet.bullet_blit(bullets,setting) <—--this cant create many bullets,only one bullet
# for bullet in bullets.sprites():
# bullet.draw(setting)
# bullet.move()
p.display.flip()
game()
The issue is the instance method
class Bullet(p.sprite.Sprite):
# [...]
def bullet_blit(self,bullets,setting):
for bullet in bullets.sprites():
p.draw.rect(setting.screen,self.color,self.bullet)
self.y -= self.speed
self.bullet.y=self.y
You want to iterate through all bullets and to move and blit each single bullet, but you just use the attributes of the instance (self) rather than object from the list bullet.
Turn bullet_blit to a class method:
class Bullet(p.sprite.Sprite):
def bullet_blit(bullets,setting):
for bullet in bullets.sprites():
p.draw.rect(setting.screen,bullet.color,bullet.bullet)
bullet.y -= bullet.speed
bullet.bullet.y=bullet.y
and call it:
while True:
# [...]
Bullet.bullet_blit(bullets,setting)
pygame.event.get() get all the messages and remove them from the queue. So this function should be called once per frame, in the main application loop.
Turn bullet_check to a class method and pass the list of events to the method:
class Bullet(p.sprite.Sprite):
# [...]
def bullet_check(events, bullets, setting):
for event in events:
if event.type == p.KEYDOWN:
if event.key ==p.K_SPACE:
bullets.add(Bullet(setting))
Get the events in the main application loop and pass it to the function:
def game():
p.init()
setting=Setting(1200,800)
bullets=p.sprite.Group()
while True:
events = p.event.get()
for event in events:
if event.type == p.QUIT:
sys.exit()
Bullet.bullet_check(events, bullets, setting)
setting.screen.fill((255,0,0))
Bullet.bullet_blit(bullets,setting)
p.display.flip()
Another option is to create a BulletsGroup class, derived from pygame.sprite.Group:
import pygame as p
import sys
class Setting():
def __init__(self,width,height):
self.w=width
self.h=height
self.flag=p.RESIZABLE
self.color=(255,255,255)
self.speed=1
self.screen=p.display.set_mode((self.w,self.h),self.flag)
p.display.set_caption("Bullet")
self.bullet_s=1
self.bullet_w=5
self.bullet_h=30
self.bullet_c=(0,0,0)
class BulletsGroup(p.sprite.Group):
def __init__(self):
super().__init__()
def move_bullets(self):
for bullet in self:
bullet.move()
def draw_bullets(self, setting):
for bullet in self:
bullet.draw(setting)
def bullet_check(self, events, setting):
for event in events:
if event.type == p.KEYDOWN:
if event.key ==p.K_SPACE:
self.add(Bullet(setting))
class Bullet(p.sprite.Sprite):
def __init__(self,setting):
super().__init__()
self.screen_rect=setting.screen.get_rect()
self.screen_center=self.screen_rect.center
self.bullet=p.Rect((0,0),(setting.bullet_w,setting.bullet_h))
self.bullet.center=self.screen_center
self.bullet.bottom=self.screen_rect.bottom
self.color=setting.bullet_c
self.speed=setting.bullet_s
self.y=float(self.bullet.centery)
def move(self):
self.y -= self.speed
self.bullet.y=self.y
def draw(self, setting):
p.draw.rect(setting.screen,self.color,self.bullet)
def game():
p.init()
setting = Setting(1200,800)
bullets = BulletsGroup()
while True:
events = p.event.get()
for event in events:
if event.type == p.QUIT:
sys.exit()
bullets.bullet_check(events, setting)
bullets.move_bullets()
setting.screen.fill((255,0,0))
bullets.draw_bullets(setting)
p.display.flip()
game()