I just finished chapter 12 in python crash course and i want to add a little more feature for my python pygame code. I had succesfully added a rotation feature to make my ship facing the mouse, but my ship seems kinda stuck and does not want move. Please help!
here's my code
alien invasion.py
def main():
pygame.init()
setting = Settings()
screen = pygame.display.set_mode((setting.screen_width, setting.screen_height))
pygame.display.set_caption("Alien Invasion")
bullets = Group()
while True:
mouse_position = pygame.mouse.get_pos()
ship = Ship(screen,mouse_position)
gf.check_events(screen, ship, bullets)
ship.ship_movement()
gf.update_bullet(bullets)
gf.update_screen(setting, screen, ship, bullets)
ship.py
import pygame
import math
class Ship():
def __init__(self, screen,mouse_position):
# initialize the ship and set its starting position
self.screen = screen
# Load the ship image
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect() # getting the image rectangle
self.screen_rect = screen.get_rect() # getting the screen rectangle
# start each new ship at the bottom center of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# if True then it'll move right/left
self.move_right = False
self.move_left = False
self.move_up = False
self.move_down = False
# set speed
self.set_speed = 1.5
# store the decimal value for the ship's center
self.center_x = float(self.rect.centerx)
self.center_y = float(self.rect.bottom)
# player rotation
self.mouse_position = mouse_position
self.angle = math.atan2(self.mouse_position[1] - (self.rect.centery), self.mouse_position[0] - (self.rect.centerx))
self.player_rotation = pygame.transform.rotate(self.image, -self.angle * 57.29 - 90)
self.new_playerpos = self.rect.centerx - self.player_rotation.get_rect().width / 2, self.rect.centery - self.player_rotation.get_rect().height / 2
def ship_movement(self):
if self.move_right and self.rect.right < self.screen_rect.right:
self.center_x += self.set_speed
if self.move_left and self.rect.left > 0:
self.center_x -= self.set_speed
if self.move_up and self.rect.top > self.screen_rect.top:
self.center_y -= self.set_speed
if self.move_down and self.rect.bottom < self.screen_rect.bottom:
self.center_y += self.set_speed
self.rect.centery = self.center_y - 30
self.rect.centerx = self.center_x
def blit_screen(self):
# Draw the ship at it's current location
self.screen.blit(self.player_rotation, self.new_playerpos)
# self.screen.blit(self.image, self.rect)
game functions.py
import pygame
from pygame.locals import *
import sys
from bullet import Bullet
def check_keydown_events(event,screen, ship, bullets):
if event.key == K_RIGHT or event.key == K_d:
ship.move_right = True
elif event.key == K_LEFT or event.key == K_a:
ship.move_left = True
elif event.key == K_UP or event.key == K_w:
ship.move_up = True
elif event.key == K_DOWN or event.key == K_s:
ship.move_down = True
elif event.key == K_SPACE:
fire_bullet(screen, ship, bullets)
def check_keyup_events(event, ship):
if event.key == K_RIGHT or event.key == K_d:
ship.move_right = False
elif event.key == K_LEFT or event.key == K_a:
ship.move_left = False
elif event.key == K_UP or event.key == K_w:
ship.move_up = False
elif event.key == K_DOWN or event.key == K_s:
ship.move_down = False
def fire_bullet(screen, ship, bullets):
# create new bullet and add it to the bullet group
new_bullet = Bullet(screen, ship)
if len(bullets) < new_bullet.bullet_limit:
bullets.add(new_bullet)
def check_events(screen, ship, bullets):
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == KEYDOWN:
check_keydown_events(event,screen, ship, bullets)
elif event.type == KEYUP:
check_keyup_events(event, ship)
def update_bullet(bullets):
# removing bullet that has already disapeared
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def update_screen(setting, screen, ship, bullets):
screen.fill(setting.bg_color)
for bullet in bullets.sprites():
bullet.draw_bullet()
ship.blit_screen()
pygame.display.flip()
The issue is that the Ship object is recreated in every frame at it's initial position. You have to create the instance of Ship before the main application loop:
def main():
# [...]
ship = Ship(screen,mouse_position) # <--- INSERT
while True:
# ship = Ship(screen,mouse_position) <--- DELETE
# [...]
The rotation of the ship has to be updated in the method ship_movement rather than the constructor:
def main():
# [...]
while True:
# [...]
mouse_position = pygame.mouse.get_pos()
ship.ship_movement(mouse_position)
# [...]
class Ship():
# [...]
def ship_movement(self, mouse_position):
if self.move_right and self.rect.right < self.screen_rect.right:
self.center_x += self.set_speed
if self.move_left and self.rect.left > 0:
self.center_x -= self.set_speed
if self.move_up and self.rect.top > self.screen_rect.top:
self.center_y -= self.set_speed
if self.move_down and self.rect.bottom < self.screen_rect.bottom:
self.center_y += self.set_speed
self.rect.centery = self.center_y - 30
self.rect.centerx = self.center_x
# player rotation
self.mouse_position = mouse_position
self.angle = math.atan2(self.mouse_position[1] - (self.rect.centery), self.mouse_position[0] - (self.rect.centerx))
self.player_rotation = pygame.transform.rotate(self.image, -self.angle * 57.29 - 90)
self.new_playerpos = self.rect.centerx - self.player_rotation.get_rect().width / 2, self.rect.centery - self.player_rotation.get_rect().height / 2
Related
This question already has answers here:
how to make image/images disappear in pygame?
(1 answer)
How do I delete rect object from screen once player collides with it?
(1 answer)
Closed 23 days ago.
I am new at using objects in python and brand new at Pygame. I am trying to make a game where a player moves around the screen eating objects. However, I am unable to get the consumed objects to disappear, the moving object is smudging and not transparent. The Downward and Rightward movement keys also produce no result.
This is the code for the movement:
def move(self):
if self.dir != '':
if self.dir == 'd' and self.rect.bottom < self.height:
self.rect.top += self.speed
if self.dir == 'u' and self.rect.top > 0:
self.rect.top -= self.speed
if self.dir == 'l' and self.rect.left > 0:
self.rect.left -= self.speed
if self.dir == 'r' and self.rect.right < self.width:
self.rect.right += self.speed
the code for image transparency:
self.surface = pygame.image.load("images/player.png").convert_alpha()
This is how I am drawing the player and the consumable:
def draw(self):
self.screen.blit(self.surface, self.rect)
and the main update code that is inside the game loop:
def update(self):
for f in self.foods[:]:
if self.player.rect.colliderect(f.rect):
self.foods.remove(f)
self.foods.append(Consumable(
self.screen, WIDTH*random.randint(1, 9)//10, HEIGHT*random.randint(1, 9)//10))
self.eaten += 1
for f in self.foods:
f.draw()
if self.eaten == 10:
self.won = True
self.player.move()
self.player.draw()
pygame.display.update()
pygame.display.flip()
This is the full game code:
import pygame
import random
import time
from pygame.locals import *
pwidth = 250
pheight = 250
class Consumable(pygame.sprite.Sprite):
def __init__(self, screen, centerx, centery):
super(Consumable, self).__init__()
self.surface = pygame.image.load("images/food.jpeg")
self.rect = self.surface.get_rect()
self.rect.centerx = centerx
self.rect.centery = centery
self.screen = screen
def draw(self):
self.screen.blit(self.surface, self.rect)
class player(pygame.sprite.Sprite):
def __init__(self, screen, centerx, centery):
super(player, self).__init__()
self.surface = pygame.image.load("images/player.png").convert_alpha()
self.screen = screen
self.speed = 10
self.dir = ''
self.rect = self.surface.get_rect()
self.rect.centerx = centerx
self.rect.centery = centery
self.width = self.rect.width
self.height = self.rect.height
def draw(self):
self.screen.blit(self.surface, self.rect)
def move(self):
if self.dir != '':
if self.dir == 'd' and self.rect.bottom < self.height:
self.rect.top += self.speed
if self.dir == 'u' and self.rect.top > 0:
self.rect.top -= self.speed
if self.dir == 'l' and self.rect.left > 0:
self.rect.left -= self.speed
if self.dir == 'r' and self.rect.right < self.width:
self.rect.right += self.speed
class Game:
def __init__(self, screen):
self.screen = screen
self.screen.fill((0, 0, 0))
self.icon = pygame.image.load("images/icon.jpeg")
pygame.display.set_icon(self.icon)
self.foods = []
self.eaten = 0
for i in range(4):
self.foods.append(Borgar(
self.screen, WIDTH*random.randint(1, 9)//10, HEIGHT*random.randint(1, 9)//10))
for f in self.foods:
f.draw()
self.player = player(screen, WIDTH//2, HEIGHT//2)
self.mainclock = pygame.time.Clock()
self.start_time = time.perf_counter()
self.end_time = -1
self.won = False
def update(self):
pickedUp = False
for f in self.foods[:]:
if self.player.rect.colliderect(f.rect):
self.foods.remove(f)
self.foods.append(Borgar(
self.screen, WIDTH*random.randint(1, 9)//10, HEIGHT*random.randint(1, 9)//10))
pickedUp = True
self.eaten += 1
print(self.eaten)
for f in self.foods:
f.draw()
if self.eaten == 10:
self.won = True
self.end_time = time.perf_counter()
print("You won")
print("time taken: {:.2f}".format(self.end_time-self.start_time))
self.player.move()
self.player.draw()
pygame.display.update()
pygame.display.flip()
def _handle_events(self):
for event in pygame.event.get():
if event.type == QUIT:
raise SystemExit
if event.type == KEYDOWN:
if event.key == K_BACKSPACE or event.key == K_ESCAPE or event.key == ord('q'):
raise SystemExit
if event.key == K_LEFT or event.key == ord('a'):
self.player.dir = 'l'
if event.key == K_RIGHT or event.key == ord('d'):
self.player.dir = 'r'
if event.key == K_UP or event.key == ord('w'):
self.player.dir = 'u'
if event.key == K_DOWN or event.key == ord('s'):
self.player.dir = 'd'
if event.type == KEYUP:
# End repetition.
if event.key == K_LEFT or event.key == ord('a'):
self.player.dir = ''
if event.key == K_RIGHT or event.key == ord('d'):
self.player.dir = ''
if event.key == K_UP or event.key == ord('w'):
self.player.dir = ''
if event.key == K_DOWN or event.key == ord('s'):
self.player.dir = ''
def run(self):
while True:
self.mainclock.tick(60)
self._handle_events()
self.update()
if self.won:
raise SystemExit
pygame.display.flip()
pygame.init()
display = pygame.display.set_mode((800, 600), pygame.RESIZABLE)
WIDTH, HEIGHT = pygame.display.get_window_size()
pygame.display.set_caption("Game")
game = Game(display)
game.run()
Here is what happens when I run the game:
game screenshot
I would really appreciate any help possible.
I'm creating a game on Pygame and faced with the problem that the objects are displayed incorrectly
I want the objects in a row but I wrote it diagonally
My game:
import pygame, controls
from gun import Gun
from pygame.sprite import Group
def run():
pygame.init()
screen = pygame.display.set_mode((700, 600))
pygame.display.set_caption('Game1')
bg_color = (0, 0, 0)
gun = Gun(screen)
bullets = Group()
inos = Group()
controls.create_army(screen, inos)
while True:
controls.events(screen, gun, bullets)
gun.update_gun()
controls.update(bg_color, screen, gun, inos, bullets)
controls.update_bullets(bullets)
controls.update_inos(inos)
run()
controls:
import pygame
import sys
from bullet import Bullet
from ino import Ino
def events(screen, gun, bullets):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
gun.mright = True
elif event.key == pygame.K_a:
gun.mleft = True
elif event.key == pygame.K_SPACE:
new_bullet = Bullet(screen, gun)
bullets.add(new_bullet)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_d:
gun.mright = False
elif event.key == pygame.K_a:
gun.mleft = False
def update(bg_color, screen, gun, inos, bullets):
screen.fill(bg_color)
for bullet in bullets.sprites():
bullet.draw_bullet()
gun.output()
inos.draw(screen)
pygame.display.flip()
def update_bullets(bullets):
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom <= 0:
bullets.remove(bullet)
def update_inos(inos):
inos.update()
def create_army(screen, inos):
ino = Ino(screen)
ino_width = ino.rect.width
number_ino_x = int((700 - 2 * ino_width) / ino_width)
ino_height = ino.rect.height
number_ino_y = int((800 - 100 - 2 * ino_height) / ino_height)
for row_number in range(number_ino_y - 1):
for ino_number in range(number_ino_x):
ino = Ino(screen)
ino.x = ino_width + (ino_width * ino_number)
ino.y = ino_height + (ino_height * ino_number)
ino.rect.x = ino.x
ino.rect.y = ino.rect.height + (ino.rect.height * row_number)
inos.add(ino)
soldiers:
import pygame
class Ino(pygame.sprite.Sprite):
def __init__(self, screen):
super(Ino, self).__init__()
self.screen = screen
self.image = pygame.image.load(r'C:\Users\ralph\Downloads\Python\Game\Image\r.soldier.png')
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.x = float(self.rect.x)
self.y = float(self.rect.y)
def draw(self):
self.screen.blit(self.image, self.rect)
def update(self):
self.y += 0.05
self.rect.y = self.y
I got this result
But I need another
How can I fix this?
In my humble opinion the problem is in "controls" or "soldiers" update
The images are diagonal because you calculate the y-coordinate depending on the ino_number, so that the y-coordinate increases with increasing ino_number. The y-coordinate must be the same for all objects. Only the coordinated x must increase with the number ino_number:
def create_army(screen, inos):
# [...]
for row_number in range(number_ino_y - 1):
for ino_number in range(number_ino_x):
ino = Ino(screen)
ino.x = ino_width + (ino_width * ino_number)
ino.y = ino_height
ino.rect.x = ino.x
ino.rect.y = ino.y
inos.add(ino)
In my program, when the keys are pressed, the image of the ship is flipped. I decided to bind the direction of the bullets to the flags of the ship's position. But I ran into the problem that all bullets take the direction of the ship when they are already fired. I need to keep the coordinate of the sprite when it is drawn.
import sys
import pygame
from settings import Settings
from rocket import Rocket
from bullet import Bullet
class Cosmos:
"""Главный класс игры Cosmos"""
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
pygame.display.set_caption('Cosmos')
self.fon_image = pygame.image.load('image/Fon.png')
self.fon = self.fon_image.get_rect()
self.settings = Settings()
self.rocket = Rocket(self.screen, self.settings)
self.bullets = pygame.sprite.Group()
def key_event(self):
"""Обрабатывает нажатие клавиш"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
sys.exit()
elif event.key == pygame.K_d:
self.rocket.move_RIGHT = True
elif event.key == pygame.K_a:
self.rocket.move_LEFT = True
elif event.key == pygame.K_w:
self.rocket.move_UP = True
elif event.key == pygame.K_s:
self.rocket.move_DOWN = True
elif event.key == pygame.K_SPACE:
self.fire_bullet()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_d:
self.rocket.move_RIGHT = False
elif event.key == pygame.K_a:
self.rocket.move_LEFT = False
elif event.key == pygame.K_w:
self.rocket.move_UP = False
elif event.key == pygame.K_s:
self.rocket.move_DOWN = False
def fire_bullet(self):
"""Создание нового снаряда и включение его в группу"""
new_bullet = Bullet(self.screen, self.settings, self.rocket)
self.bullets.add(new_bullet)
def run_game(self):
"""Обновляет события игры"""
while True:
self.screen.blit(self.fon_image, self.fon)
self.key_event()
self.rocket.update_rocket()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.bullets.update(bullet)
self.rocket.draw_rocket()
pygame.display.flip()
game = Cosmos()
game.run_game()
from pygame.sprite import Sprite
class Bullet(Sprite):
"""Класс для управления снарядами, выпущенными кораблем"""
def __init__(self, screen, settings, rocket):
super().__init__()
self.screen = screen
self.settings = settings
self.rocket = rocket
self.color = self.settings.bullets_color
self.rect = pygame.Rect(0, 0, self.settings.bullets_width, self.settings.bullets_height)
self.rect.midtop = rocket.rect.midtop
self.y = float(self.rect.y)
self.x = float(self.rect.x)
def update(self, bullet):
"""Перемещение снаряда"""
if self.rocket.image == self.rocket.image_right:
bullet.x += 2
if self.rocket.image == self.rocket.image_left:
bullet.x -= 2
if self.rocket.image == self.rocket.image_up:
bullet.y -= 2
if self.rocket.image == self.rocket.image_down:
bullet.y += 2
self.rect.x = self.x
self.rect.y = self.y
def draw_bullet(self):
"""Рисует снаряд"""
pygame.draw.rect(self.screen, self.color, self.rect)
import pygame
class Rocket:
"""Класс для управления кораблём"""
def __init__(self, screen, settings):
self.screen = screen
self.screen_rect = screen.get_rect()
self.settings = settings
self.image = pygame.image.load('image/pixil-frame-0.png')
self.rect = self.image.get_rect()
self.rect.center = self.screen_rect.center
self.x = float(self.rect.x)
self.y = float(self.rect.y)
self.move_RIGHT = False
self.move_LEFT = False
self.move_UP = False
self.move_DOWN = False
self.image_right = pygame.transform.rotate(self.image, -90)
self.image_left = pygame.transform.rotate(self.image, 90)
self.image_up = pygame.transform.rotate(self.image, 0)
self.image_down = pygame.transform.rotate(self.image, 180)
def update_rocket(self):
"""Флаги управления кораблём"""
if self.move_RIGHT and self.rect.right < self.screen_rect.right:
self.image = self.image_right
self.x += self.settings.rocket_speed
if self.move_LEFT and self.rect.left > self.screen_rect.left:
self.image = self.image_left
self.x -= self.settings.rocket_speed
if self.move_UP and self.rect.top > self.screen_rect.top:
self.image = self.image_up
self.y -= self.settings.rocket_speed
if self.move_DOWN and self.rect.bottom < self.screen_rect.bottom:
self.image = self.image_down
self.y += self.settings.rocket_speed
self.rect.x = self.x
self.rect.y = self.y
def draw_rocket(self):
"""Обновляет позицию корабля"""
self.screen.blit(self.image, self.rect)
The problem here is that in Bullet.update, you use the current image of the rocket to determine in which direction the bullet is going. This means that any change of this image results in a change of direction. Instead of doing this, you should save the current direction upon creating a new Bullet instance and then use this saved direction to determine the direction of the rocket.
A possible implementation:
class Bullet(Sprite):
"""Класс для управления снарядами, выпущенными кораблем"""
def __init__(self, screen, settings, rocket):
super().__init__()
self.screen = screen
self.settings = settings
self.rocket = rocket
self.color = self.settings.bullets_color
self.rect = pygame.Rect(0, 0, self.settings.bullets_width, self.settings.bullets_height)
self.rect.midtop = rocket.rect.midtop
self.y = float(self.rect.y)
self.x = float(self.rect.x)
if self.rocket.image == self.rocket.image_right:
self.direction = "right"
elif self.rocket.image == self.rocket.image_left:
self.direction = "left"
elif self.rocket.image == self.rocket.image_up:
self.direction = "up"
elif self.rocket.image == self.rocket.image_down:
self.direction = "down"
def update(self, bullet):
"""Перемещение снаряда"""
if self.direction == "right":
bullet.x += 2
if self.direction == "left":
bullet.x -= 2
if self.direction == "up":
bullet.y -= 2
if self.direction == "down":
bullet.y += 2
self.rect.x = self.x
self.rect.y = self.y
def draw_bullet(self):
"""Рисует снаряд"""
pygame.draw.rect(self.screen, self.color, self.rect)
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'm trying to make a ship for alien invaders move up and down but can't seem to make it properly work without messing something up.
With my code below what would I need to add?
alien_invasion.py:
import sys
import pygame
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")
#Draw the ship
ship = Ship(ai_settings, screen)
#Start the main loop for the game.
while True:
#Watch for keyboard and mouse events
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)
run_game()
ship.py:
import pygame
class Ship():
def __init__(self, ai_settings, screen):
"""Initialize teh ship and set its starting position"""
self.screen = screen
self.ai_settings = ai_settings
#Load teh ship image and get its rect
self.image = pygame.image.load('ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
#Start each new ship at the bottom center of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's center.
self.center = float(self.rect.centerx)
# Movement flag
self.moving_right = False
self.moving_left = False
def update(self):
"""Update the ship's postion based on the movement flag"""
# Update the ship's center value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
# Update rect object from self.center
self.rect.centerx = self.center
def blitme(self):
"""Draw the ship at its current location"""
self.screen.blit(self.image, self.rect)
game_fuction.py:
import sys
import pygame
def check_keydown_events(event, ship):
"""Responds to keypresses"""
if event.key == pygame.K_RIGHT:
ship.moving_right = True
elif event.key == pygame.K_LEFT:
ship.moving_left = True
def check_keyup_events(event, ship):
"""Respoinds 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(ship):
"""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, ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship):
"""Update images on the screen and flip to the new screen"""
#Redraw the screen during each pass through the loop
screen.fill(ai_settings.bg_color)
ship.blitme()
#Make the most recently drawn screen visible.
pygame.display.flip()
settings.py:
class Settings():
"""A Class to store all settings for ALein INvasion"""
def __init__(self):
"""Initialize the game's settings"""
#screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (255,255,255)
# Ship Settings
self.ship_speed_factor = 1.5
This is a project from Python Crash Course.
This certain project says
Make a game that begins with a rocket in the center of the screen.
Allow the player to move the rocket up, down, left, or right using the
four arrow keys. Make sure the rocket never moves beyond any edge or
the screen.
I haven't typed this in to check, but I have solved similar problems/projects in pygame. It looks like you need to modify the code in two places:
1) Catch the events of pressing the down key and the up keys.
This is probably something like:
# from your game_function.py
def check_keydown_events(event, ship):
"""Responds 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_DOWN:
ship.moving_down = True
elif event.key == pygame.K_UP:
ship.moving_up = True
def check_keyup_events(event, ship):
"""Respoinds to key releases"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
elif event.key == pygame.K_UP:
ship.moving_up = False
2) Your original ship class to process the actual movement.
This should look something like:
# Movement flag
self.moving_right = False
self.moving_left = False
self.moving_down = False
self.moving_up = False
def update(self):
"""Update the ship's postion based on the movement flag"""
# Update the ship's center value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.center += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.center -= self.ai_settings.ship_speed_factor
if self.moving_down and self.rect.bottom > self.screen_rect.bottom:
self.center -= self.ai_settings.ship_speed_factor
if self.moving_up and self.rect.top < self.screen_rect.top:
self.center -= self.ai_settings.ship_speed_factor
# Update rect object from self.center - might need to be changed now to handle
# movement in two directions properly.
if self.moving_up or self.moving_down:
self.rect.centery = self.center
if self.moving_left or self.moving_right:
self.rect.centerx = self.center
Ok, I've cracked it - this solution is now working:
settings.py
class Settings:
"""A Class to store all settings for ALien INvasion"""
def __init__(self):
"""Initialize the game's settings"""
#screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (255,255,255)
# Ship Settings
ship_speed_factor = 1.5
game_functions.py
import sys
import pygame
def check_keydown_events(event, ship):
"""Responds 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_DOWN:
ship.moving_down = True
elif event.key == pygame.K_UP:
ship.moving_up = True
def check_keyup_events(event, ship):
"""Responds to key releases"""
if event.key == pygame.K_RIGHT:
ship.moving_right = False
elif event.key == pygame.K_LEFT:
ship.moving_left = False
elif event.key == pygame.K_DOWN:
ship.moving_down = False
elif event.key == pygame.K_UP:
ship.moving_up = False
def check_events(ship):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.KEYDOWN:
check_keydown_events(event, ship)
elif event.type == pygame.KEYUP:
check_keyup_events(event, ship)
def update_screen(ai_settings, screen, ship):
"""Update images on the screen and flip to the new screen"""
#Redraw the screen during each pass through the loop
screen.fill(ai_settings.bg_color)
ship.blitme()
#Make the most recently drawn screen visible.
pygame.display.flip()
ship.py
import pygame
class Ship:
def __init__(self, ai_settings, screen):
"""Initialize teh ship and set its starting position"""
self.screen = screen
self.ai_settings = ai_settings
#Load the ship image and get its rect
self.image = pygame.image.load('ship.bmp')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
#Start each new ship at the bottom center of the screen
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# Store a decimal value for the ship's x and y center.
self.centerx = float(self.rect.centerx)
self.centery = float(self.rect.centery)
# Movement flag
self.moving_right = False
self.moving_left = False
self.moving_down = False
self.moving_up = False
def update(self):
"""Update the ship's postion based on the movement flag"""
# Update the ship's center value, not the rect.
if self.moving_right and self.rect.right < self.screen_rect.right:
self.centerx += self.ai_settings.ship_speed_factor
if self.moving_left and self.rect.left > 0:
self.centerx -= self.ai_settings.ship_speed_factor
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.centery += self.ai_settings.ship_speed_factor
if self.moving_up and self.rect.top > self.screen_rect.top:
self.centery -= self.ai_settings.ship_speed_factor
# Update rect object from self.center - might need to be changed now to handle
# movement in two directions properly.
if self.moving_up or self.moving_down:
self.rect.centery = self.centery
if self.moving_left or self.moving_right:
self.rect.centerx = self.centerx
def blitme(self):
"""Draw the ship at its current location"""
self.screen.blit(self.image, self.rect)
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
import game_functions as gf
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")
#Draw the ship
ship = Ship(ai_settings, screen)
#Start the main loop for the game.
while True:
#Watch for keyboard and mouse events
gf.check_events(ship)
ship.update()
gf.update_screen(ai_settings, screen, ship)
run_game()
Actually, all I did was to implement all of the changes that I described and eliminate a few basic errors in layout/syntax. Note that I added a better quit function to close the graphics canvas when you click the red button.
If this solution also works for you then please mark it as the answer so that I can get enough reputation here on stack flow.
I know this is an old question, but it was top result when I ran into the same problem. Just want to show how I ended up doing it.
alien_invasion.py keydown and keyup check events:
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_d:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_a:
self.ship.moving_left = True
elif event.key == pygame.K_UP:
self.ship.moving_up = True
elif event.key == pygame.K_DOWN:
self.ship.moving_down = True
elif event.key == pygame.K_q:
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_d:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
elif event.key == pygame.K_a:
self.ship.moving_left = False
elif event.key == pygame.K_UP:
self.ship.moving_up = False
elif event.key == pygame.K_DOWN:
self.ship.moving_down = False
ship.py
import pygame
class Ship:
"""A class to manage the ship"""
def __init__(self, ai_game):
"""initialize the ship and set its starting position"""
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_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
self.rect.center = self.screen_rect.center
# store a decimal value for the ship's horizontal position
self.x = float(self.rect.x)
self.y = float(self.rect.y)
# movement flag
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
def update(self):
"""update the ship's position on the movement flag"""
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
if self.moving_up and self.rect.top > 0:
self.y -= self.settings.ship_speed
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.y += self.settings.ship_speed
# update rect object from self.x
self.rect.x = self.x
self.rect.y = self.y
def blitme(self):
"""draw the ship at its current location"""
self.screen.blit(self.image, self.rect)
I added the moving_up and moving_down plus had to add the self.rect.y for the y axis.
The ship cannot move past the top of the screen or the bottom.
Hope this helps anyone who googled the same question.
I have written a runnable solution for 2nd edition of the book.
alien_invasion.py as below:
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
def __init__(self):
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption('Alien Invasion')
self.ship = Ship(self)
def run_game(self):
while True:
self._check_event()
self.ship.update()
self._update_screen()
pygame.display.flip()
def _check_event(self):
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_event(event)
def _check_keydown_events(self, event):
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_UP:
self.ship.moving_up = True
elif event.key == pygame.K_DOWN:
self.ship.moving_down = True
elif event.key == pygame.K_q:
sys.exit()
def _check_keyup_event(self, event):
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
elif event.key == pygame.K_DOWN:
self.ship.moving_down = False
elif event.key == pygame.K_UP:
self.ship.moving_up = False
def _update_screen(self):
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
settings.py
class Settings:
def __init__(self):
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
self.ship_speed = 1.5
ship.py
import pygame
class Ship:
def __init__(self, ai_game):
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_game.screen.get_rect()
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)
self.y = float(self.rect.y)
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = 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
if self.moving_up and self.rect.top > self.screen_rect.top:
self.y -= self.settings.ship_speed
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.y += self.settings.ship_speed
self.rect.x = self.x
self.rect.y = self.y
def blitme(self):
self.screen.blit(self.image, self.rect)