I'll much appreciate it if you help me to resolve the issue with moving Rocket in my project.
I've started to code my first game, based on material that I've learned in the "Python crash course" book.
I need to make Rocket in the game to move all over the window and around its axis.
Everything worked (moving left, right, top, bottom), then I decided to make my Rocket to move around its axis. I spend a lot of time to know how to do it and it already works (left Shift).
But now I faced another problem. My Rocket now doesn't move on the screen, only around its axis. I understand that my question may seem very stupid, and somewhere in my code is obviously a lack of simple logic. But I tried to fix it by myself for several hours and didn't get a result.
Here is my code:
import pygame
import sys
class Settings():
"""A class to store all settings for Rocket."""
def __init__(self):
"""Initialize the game screen settings."""
# Screen settings.
self.screen_width = 900
self.screen_height = 700
self.bg_color = (21, 36, 110)
class Rocket():
"""A class that describes rocket."""
def __init__(self, screen):
"""Initialize rocket and its starting position."""
self.screen = screen
#Load the rocket image and get its rect.
self.image = pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket.png')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
#set starting position of a rocket.
self.rect.centerx = self.screen_rect.centerx
self.rect.centery = self.screen_rect.centery
#movement flag.
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
self.rotate_left = False
self.rocket_angle = 0
def update(self):
"""Update the rocket position based on the movement flag."""
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.centerx += 2
if self.moving_left and self.rect.left > 0:
self.rect.centerx -= 2
if self.moving_up and self.rect.top > 0:
self.rect.centery -= 2
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.rect.centery += 2
def rotated_center(self, image, rocket_angle):
"""Rotating rocket around its axis."""
self.center = self.image.get_rect().center
self.rotated_image = pygame.transform.rotate(self.image, self.rocket_angle)
self.new_rect = self.rotated_image.get_rect(center = self.center)
return self.rotated_image, self.new_rect
def blit_rocket(self, rect, rocket_angle):
"""Draw the rocket at its current location."""
if self.rotate_left:
self.rocket_angle = (self.rocket_angle + 1) % 360
self.screen.blit(self.rotated_image, self.new_rect)
else:
self.rocket_angle = (self.rocket_angle + 0) % 360
self.screen.blit(self.rotated_image, self.new_rect)
def check_events(self):
"""Respond to a key events."""
# Responses to the keydown events.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.moving_right = True
if event.key == pygame.K_LEFT:
self.moving_left = True
if event.key == pygame.K_DOWN:
self.moving_down = True
if event.key == pygame.K_UP:
self.moving_up = True
if event.key == pygame.K_LSHIFT:
self.rotate_left = True
# Responses to the keyup events.
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.moving_right = False
if event.key == pygame.K_LEFT:
self.moving_left = False
if event.key == pygame.K_DOWN:
self.moving_down = False
if event.key == pygame.K_UP:
self.moving_up = False
if event.key == pygame.K_LSHIFT:
self.rotate_left = False
pygame.init()
rocket_settings = Settings()
icon = pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket_icon.png')
pygame.display.set_icon(icon)
screen = pygame.display.set_mode((rocket_settings.screen_width, rocket_settings.screen_height))
pygame.display.set_caption("Rocket")
rocket = Rocket(screen)
"""The main game loop."""
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill((rocket_settings.bg_color))
rocket.update()
rocket.check_events()
rocket.rotated_center(rocket.image, rocket.rocket_angle)
rocket.blit_rocket(rocket.rect,rocket.rocket_angle)
pygame.display.flip()
event has to be an argument of the method Rocket.check_events:
class Rocket():
# [...]
def check_events(self, event): # event argument
"""Respond to a key events."""
# Responses to the keydown events.
# [...]
You have to call rocket.check_events() in the event loop and to pass the event to the method:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
rocket.check_events(event) # <--- ADD
screen.fill((rocket_settings.bg_color))
rocket.update()
# rocket.check_events() <--- DELETE
rocket.rotated_center(rocket.image, rocket.rocket_angle)
rocket.blit_rocket(rocket.rect,rocket.rocket_angle)
pygame.display.flip()
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0).
You must get the position of the the object from self.rect.center rather than self.image.get_rect().center:
class Rocket():
# [...]
def rotated_center(self, image, rocket_angle):
"""Rotating rocket around its axis."""
self.rotated_image = pygame.transform.rotate(self.image, self.rocket_angle)
self.new_rect = self.rotated_image.get_rect(center = self.rect.center)
return self.rotated_image, self.new_rect
Complete example:
import pygame
import sys
class Settings():
"""A class to store all settings for Rocket."""
def __init__(self):
"""Initialize the game screen settings."""
# Screen settings.
self.screen_width = 900
self.screen_height = 700
self.bg_color = (21, 36, 110)
class Rocket():
"""A class that describes rocket."""
def __init__(self, screen):
"""Initialize rocket and its starting position."""
self.screen = screen
#Load the rocket image and get its rect.
self.image = pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket.png')
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
#set starting position of a rocket.
self.rect.centerx = self.screen_rect.centerx
self.rect.centery = self.screen_rect.centery
#movement flag.
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
self.rotate_left = False
self.rocket_angle = 0
def update(self):
"""Update the rocket position based on the movement flag."""
if self.moving_right and self.rect.right < self.screen_rect.right:
self.rect.centerx += 2
if self.moving_left and self.rect.left > 0:
self.rect.centerx -= 2
if self.moving_up and self.rect.top > 0:
self.rect.centery -= 2
if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
self.rect.centery += 2
def rotated_center(self, image, rocket_angle):
"""Rotating rocket around its axis."""
self.rotated_image = pygame.transform.rotate(self.image, self.rocket_angle)
self.new_rect = self.rotated_image.get_rect(center = self.rect.center)
return self.rotated_image, self.new_rect
def blit_rocket(self, rect, rocket_angle):
"""Draw the rocket at its current location."""
if self.rotate_left:
self.rocket_angle = (self.rocket_angle + 1) % 360
self.screen.blit(self.rotated_image, self.new_rect)
else:
self.rocket_angle = (self.rocket_angle + 0) % 360
self.screen.blit(self.rotated_image, self.new_rect)
def check_events(self, event):
"""Respond to a key events."""
# Responses to the keydown events.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.moving_right = True
if event.key == pygame.K_LEFT:
self.moving_left = True
if event.key == pygame.K_DOWN:
self.moving_down = True
if event.key == pygame.K_UP:
self.moving_up = True
if event.key == pygame.K_LSHIFT:
self.rotate_left = True
# Responses to the keyup events.
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.moving_right = False
if event.key == pygame.K_LEFT:
self.moving_left = False
if event.key == pygame.K_DOWN:
self.moving_down = False
if event.key == pygame.K_UP:
self.moving_up = False
if event.key == pygame.K_LSHIFT:
self.rotate_left = False
pygame.init()
rocket_settings = Settings()
icon = pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket_icon.png')
pygame.display.set_icon(icon)
screen = pygame.display.set_mode((rocket_settings.screen_width, rocket_settings.screen_height))
pygame.display.set_caption("Rocket")
rocket = Rocket(screen)
"""The main game loop."""
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
rocket.check_events(event)
screen.fill((rocket_settings.bg_color))
rocket.update()
rocket.rotated_center(rocket.image, rocket.rocket_angle)
rocket.blit_rocket(rocket.rect,rocket.rocket_angle)
pygame.display.flip()
Related
I learned about the events settings through a very helpful user here on stackoverflow already. I think I understand how that function works. I currently spawn 1 enemy on the right side of the screen that moves towards the left. I am attempting to spawn them at certain time intervals.
However, I don't know where I am supposed to put the code in for the event. I've tried adding it into def _create_fleet(self): and def __init__(self): and def __init__(self, placeholder): under the "Alien.py" separate file, but none of them seem to work. Could someone please show me where the correct area to do so is?
I am trying to add this:
FIRE_EVENT = pygame.USEREVENT + 1 # This is just a integer.
pygame.time.set_timer(FIRE_EVENT, 1000) # 1000 milliseconds is 1 seconds.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == FIRE_EVENT: # Will appear once every second.
***whatever the right place to add will be put in here***
and the full code is here:
import sys
import pygame
from ship import Ship
from bullets import Bullet
from alien import Alien
from time import time, sleep
class Sideways:
"""Overall class to manage game assets"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.screen = pygame.display.set_mode((1200, 600))
self.screen_rect = self.screen.get_rect()
pygame.display.set_caption("Pew Pew")
self.bg_color = (204, 255, 255)
self.ship = Ship(self)
self.moving_up = False
self.moving_down = False
self.moving_left = False
self.moving_right = False
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
def _create_fleet(self):
""""Create a fleet of aliens"""
alien = Alien(self)
self.aliens.add(alien)
def _update_aliens(self):
self.aliens.update()
def run_game(self):
while True:
self._ship_movement_andfiring()
self.ship.blitme()
self._update_screen()
self._update_bullets()
self._update_aliens()
def _update_bullets(self):
self.bullets.update()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
for bullet in self.bullets.copy():
if bullet.rect.right > self.screen_rect.width:
self.bullets.remove(bullet)
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
def _update_screen(self):
pygame.display.flip()
self.screen.fill(self.bg_color)
self.aliens.draw(self.screen)
def _ship_movement_andfiring(self):
if self.moving_up == True:
self.ship.rect.y -= 1
if self.moving_down == True:
self.ship.rect.y += 1
if self.moving_right == True:
self.ship.rect.x += 1
if self.moving_left == True:
self.ship.rect.x -= 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.moving_up = True
elif event.key == pygame.K_DOWN:
self.moving_down = True
elif event.key == pygame.K_RIGHT:
self.moving_right = True
elif event.key == pygame.K_LEFT:
self.moving_left = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
elif event.key == pygame.K_ESCAPE:
sys.exit()
elif event.type == pygame.KEYUP:
if event.key != pygame.K_SPACE:
self.moving_up = False
self.moving_down = False
self.moving_left = False
self.moving_right = False
if self.ship.rect.midleft <= self.ship.screen_rect.midleft:
self.moving_left = False
if self.ship.rect.bottom > self.ship.screen_rect.bottom:
self.moving_down = False
if self.ship.rect.midright >= self.ship.screen_rect.midright:
self.moving_right = False
if self.ship.rect.top <= 0:
self.moving_up = False
def _fire_bullet(self):
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
if __name__ == '__main__':
pewpew = Sideways()
pewpew.run_game()
with a separate "alien.py" file to create the aliens here:
import pygame
from pygame.sprite import Sprite
from random import randint
class Alien(Sprite):
"""A class to represent a single alien in the fleet."""
def __init__(self, placeholder):
"""Initialize the alien and set its starting position"""
super().__init__()
self.screen = placeholder.screen
self.screen_rect = self.screen.get_rect()
#load the alien image and set its rect attribute
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
#Start each new alien randomly on right side of screen
random_number = randint(0, self.screen_rect.height-self.rect.height)
self.rect.x = self.screen_rect.width - self.rect.width
self.rect.y = random_number
#Store the alien's horizontal position
self.x = float(self.rect.x)
def update(self):
"""Move alien to the left"""
self.x -= 0.1
self.rect.x = self.x
Create the timer event in the constructor of the your class Sideways:
class Sideways:
def __init__(self):
# [...]
self.bullets = pygame.sprite.Group()
# self._create_fleet() <--- DELETE
# create timer event
self.FIRE_EVENT = pygame.USEREVENT + 1 # This is just a integer.
pygame.time.set_timer(self.FIRE_EVENT, 1000) # 1000 milliseconds is 1 seconds.
Handle the event tin the event loop and create a new enemy when the event occurs:
class Sideways:
# [...]
def _ship_movement_andfiring(self):
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == self.FIRE_EVENT:
self._create_fleet()
# [...]
I'm trying to write my game, based on material, that I've learned from the "Python crash course" book. Now I'm trying to make my ship shooting bullets. The code isn't ready yet, but when I've been testing it, I meet an error: name 'screen' is not defined.
Could someone look at my code and tell me, what I'm doing wrong and give some tips on how to solve it?
Here is the traceback that I get:
Traceback (most recent call last):
File "/media/philip/9074-45DF/Python/rocket/rocket.py", line 139, in <module>
bullet_settings = Bullet(rocket_settings, screen, rocket)
NameError: name 'screen' is not defined
Here is my code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import pygame
import sys
from pygame.sprite import Sprite
class Settings:
"""A class to store all settings for Rocket."""
def __init__(self):
"""Initialize the game screen settings."""
# Screen settings.
self.screen_width = 900
self.screen_height = 700
self.bg_color = (21, 36, 110)
class Rocket:
"""A class that describes rocket."""
def __init__(self, screen):
"""Initialize rocket and its starting position."""
self.screen = screen
# Load the rocket image and get its rect.
self.image = \
pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket.png'
)
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
# set starting position of a rocket.
self.rect.centerx = self.screen_rect.centerx
self.rect.centery = self.screen_rect.centery
# movement flag.
self.moving_right = False
self.moving_left = False
self.moving_up = False
self.moving_down = False
self.rotate_left = False
self.rotate_right = False
self.rocket_angle = 0
def update(self):
"""Update the rocket position based on the movement flag."""
if self.moving_right and self.rect.right \
< self.screen_rect.right:
self.rect.centerx += 2
if self.moving_left and self.rect.left > 0:
self.rect.centerx -= 2
if self.moving_up and self.rect.top > 0:
self.rect.centery -= 2
if self.moving_down and self.rect.bottom \
< self.screen_rect.bottom:
self.rect.centery += 2
def rotated_center(self, image, rocket_angle):
"""Rotating rocket around its axis."""
self.rotated_image = pygame.transform.rotate(self.image,
self.rocket_angle)
self.new_rect = \
self.rotated_image.get_rect(center=self.rect.center)
return (self.rotated_image, self.new_rect)
def blit_rocket(self, rect, rocket_angle):
"""Draw the rocket at its current location."""
if self.rotate_left:
self.rocket_angle = (self.rocket_angle + 1) % 360
self.screen.blit(self.rotated_image, self.new_rect)
else:
self.rocket_angle = (self.rocket_angle + 0) % 360
self.screen.blit(self.rotated_image, self.new_rect)
if self.rotate_right:
self.rocket_angle = (self.rocket_angle - 1) % 360
self.screen.blit(self.rotated_image, self.new_rect)
else:
self.rocket_angle = (self.rocket_angle + 0) % 360
self.screen.blit(self.rotated_image, self.new_rect)
def check_events(self, event):
"""Respond to a key events."""
# Responses to the keydown events.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.moving_right = True
if event.key == pygame.K_LEFT:
self.moving_left = True
if event.key == pygame.K_DOWN:
self.moving_down = True
if event.key == pygame.K_UP:
self.moving_up = True
if event.key == pygame.K_LSHIFT:
self.rotate_left = True
if event.key == pygame.K_RSHIFT:
self.rotate_right = True
# Responses to the keyup events.
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.moving_right = False
if event.key == pygame.K_LEFT:
self.moving_left = False
if event.key == pygame.K_DOWN:
self.moving_down = False
if event.key == pygame.K_UP:
self.moving_up = False
if event.key == pygame.K_LSHIFT:
self.rotate_left = False
if event.key == pygame.K_RSHIFT:
self.rotate_right = False
class Bullet(Sprite):
"""A class that stores bullet settings and manages bullets"""
def __init__(
self,
rocket_settings,
screen,
rocket,
):
"""Create a bullet object at the ship's current position."""
super(Bullet, self).__init__()
self.screen = screen
# Bullet settings.
self.bullet_width = 3
self.bullet_height = 5
self.bullet_color = (255, 204, 0)
self.bullets_allowed = 3
self.bullet_speed = 1
# Create a bullet object at the ship's current position.
self.rect = pygame.Rect(0, 0, bullet_settings.bullet_width,
bullet_settings.bullet_height)
self.rect.centerx = rocket.rect.top
self.color = bullet_settings.bullet_color
self.bullet_speed_factor = bullet_settings.bullet_speed
def update(self):
"""Moving bullet up the screen"""
# Update the bullet position.
self.y -= self.bullet_speed_factor
# Update the rect position.
self.rect.y = self.y
pygame.init()
rocket_settings = Settings()
bullet_settings = Bullet(rocket_settings, screen, rocket)
icon = \
pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket_icon.png'
)
pygame.display.set_icon(icon)
screen = pygame.display.set_mode((rocket_settings.screen_width,
rocket_settings.screen_height))
pygame.display.set_caption('Rocket')
rocket = Rocket(screen)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
rocket.check_events(event)
screen.fill(rocket_settings.bg_color)
rocket.update()
rocket.rotated_center(rocket.image, rocket.rocket_angle)
rocket.blit_rocket(rocket.rect, rocket.rocket_angle)
pygame.display.flip()
The screen variable has not been defined at runtime, so moving screen's definition above bullet_settings would work.
...
pygame.init()
pygame.display.set_icon(icon)
screen = pygame.display.set_mode((rocket_settings.screen_width, rocket_settings.screen_height))
pygame.display.set_caption("Rocket")
rocket = Rocket(screen)
rocket_settings = Settings()
bullet_settings = Bullet(rocket_settings, screen, rocket)
icon = pygame.image.load('/media/philip/9074-45DF/Python/rocket/images/rocket_icon.png')
...
I'm developing a basic game, where you can move around and shoot projectiles to kill randomly spawned enemies, but if I boot the game and I try to move before I do anything else, the game crashes. If I shoot a projectile first, however, the game runs perfectly and I can move around without any problems, but I can't seem to figure out why this is.
import pygame
import random
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
#Classes
class Player(pygame.sprite.Sprite):
def __init__(self, filename, x, y):
super().__init__()
self.image = pygame.image.load(filename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
self.change_x = 0
self.change_y = 0
self.walls = None
def changespeed(self, x, y):
self.change_x += x
self.change_y += y
def update(self):
self.rect.x += self.change_x
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.change_x > 0:
self.rect.right = block.rect.left
else:
self.rect.left = block.rect.right
self.rect.y += self.change_y
block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)
for block in block_hit_list:
if self.change_y > 0:
self.rect.bottom = block.rect.top
else:
self.rect.top = block.rect.bottom
class Wall(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
class Enemy(pygame.sprite.Sprite):
def __init__(self, filename):
super().__init__()
self.image = pygame.image.load(filename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
class ProjectileUp(pygame.sprite.Sprite):
def __init__(self, filename):
super().__init__()
self.image = pygame.image.load(filename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
def update(self):
self.rect.y -= 5
class ProjectileDown(pygame.sprite.Sprite):
def __init__(self, filename):
super().__init__()
self.image = pygame.image.load(filename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 5
class ProjectileLeft(pygame.sprite.Sprite):
def __init__(self, filename):
super().__init__()
self.image = pygame.image.load(filename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
def update(self):
self.rect.x -= 5
class ProjectileRight(pygame.sprite.Sprite):
def __init__(self, filename):
super().__init__()
self.image = pygame.image.load(filename).convert()
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
def update(self):
self.rect.x += 5
# --- Create the window
#Initialise pygame
pygame.init()
#Window dimensions
screen_width = 1080
screen_height = 607
screen = pygame.display.set_mode([screen_width, screen_height])
#Window display name
pygame.display.set_caption('Labyrinth')
#Sprite Lists
all_sprite_list = pygame.sprite.Group()
block_list = pygame.sprite.Group()
wall_list = pygame.sprite.Group()
projectile_list = pygame.sprite.Group()
#Define game borders
wall = Wall(-32, 0, 10, 607)
wall_list.add(wall)
all_sprite_list.add(wall)
wall = Wall(0 , -64, 1080, 10)
wall_list.add(wall)
all_sprite_list.add(wall)
wall = Wall(1100, 0, 10, 607)
wall_list.add(wall)
all_sprite_list.add(wall)
wall = Wall(0 , 600, 1080, 10)
wall_list.add(wall)
all_sprite_list.add(wall)
#Create player sprite
player = Player("Isaac.png", 420, 150)
all_sprite_list = pygame.sprite.Group()
all_sprite_list.add(player)
player.walls = wall_list
#Create enemies
for i in range(7):
block = Enemy("Judas.png")
block.rect.x = random.randrange(50, 950)
block.rect.y = random.randrange(50, 450)
block_list.add(block)
all_sprite_list.add(block)
#Manage screen updates
clock = pygame.time.Clock()
#Load background image
background_position = [0, 0]
background_image = pygame.image.load("Floor.png").convert()
#Loop until game_exit
done = False
#---------- MAIN PROGRAM LOOP ----------#
while not done:
# --- Event processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Projectile spawn
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
projectile = ProjectileUp("LightningUp.png")
projectile.rect.x = player.rect.x+65
projectile.rect.y = player.rect.y
elif event.key == pygame.K_DOWN:
projectile = ProjectileDown("LightningDown.png")
projectile.rect.x = player.rect.x+65
projectile.rect.y = player.rect.y+100
elif event.key == pygame.K_LEFT:
projectile = ProjectileLeft("LightningLeft.png")
projectile.rect.x = player.rect.x+35
projectile.rect.y = player.rect.y+100
elif event.key == pygame.K_RIGHT:
projectile = ProjectileRight("LightningRight.png")
projectile.rect.x = player.rect.x+115
projectile.rect.y = player.rect.y+100
all_sprite_list.add(projectile)
projectile_list.add(projectile)
#Movement controls
if event.type == pygame.KEYDOWN:
if event.key == ord('a'):
player.changespeed(-7, 0)
elif event.key == ord('d'):
player.changespeed(7, 0)
elif event.key == ord('w'):
player.changespeed(0, -7)
elif event.key == ord('s'):
player.changespeed(0, 7)
elif event.type == pygame.KEYUP:
if event.key == ord('a'):
player.changespeed(7, 0)
elif event.key == ord('d'):
player.changespeed(-7, 0)
elif event.key == ord('w'):
player.changespeed(0, 7)
elif event.key == ord('s'):
player.changespeed(0, -7)
# --- Game logic
#Update sprites
all_sprite_list.update()
#Projectile mechanics
for projectile in projectile_list:
block_hit_list = pygame.sprite.spritecollide(projectile, block_list, True)
for block in block_hit_list:
projectile_list.remove(projectile)
#Determine if player hits enemy
blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)
#Call on background image
screen.blit(background_image, background_position)
# --- Draw the window
all_sprite_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
It's because your if/elif/else logic is messed up a bit. You have two paths for the KEYDOWN event. If the first event happens to be a movement command, your code runs through the projectile code first (without finding a valid projectile command) and then tries to reference a projectile object that doesn't exist. Try this instead:
#---------- MAIN PROGRAM LOOP ----------#
while not done:
# --- Event processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
projectile = None # initialize
if event.key == pygame.K_UP:
projectile = ProjectileUp("LightningUp.png")
projectile.rect.x = player.rect.x+65
projectile.rect.y = player.rect.y
elif event.key == pygame.K_DOWN:
projectile = ProjectileDown("LightningDown.png")
projectile.rect.x = player.rect.x+65
projectile.rect.y = player.rect.y+100
elif event.key == pygame.K_LEFT:
projectile = ProjectileLeft("LightningLeft.png")
projectile.rect.x = player.rect.x+35
projectile.rect.y = player.rect.y+100
elif event.key == pygame.K_RIGHT:
projectile = ProjectileRight("LightningRight.png")
projectile.rect.x = player.rect.x+115
projectile.rect.y = player.rect.y+100
elif event.key == ord('a'):
player.changespeed(-7, 0)
elif event.key == ord('d'):
player.changespeed(7, 0)
elif event.key == ord('w'):
player.changespeed(0, -7)
elif event.key == ord('s'):
player.changespeed(0, 7)
if projectile: # did we create a valid projectile?
all_sprite_list.add(projectile)
projectile_list.add(projectile)
elif event.type == pygame.KEYUP:
if event.key == ord('a'):
player.changespeed(7, 0)
elif event.key == ord('d'):
player.changespeed(-7, 0)
elif event.key == ord('w'):
player.changespeed(0, 7)
elif event.key == ord('s'):
player.changespeed(0, -7)
My problem is very simple. The bullets I fire sticks to the screen if I shoot fast. If I shoot slowly, they don't stick. Anyone have an idea how this phenomenon occurs?
screenshot of the bullets sticking to the screen
Below I have entered the code. I follow this default game flowchart:
I am curious about the origin of the problem. Is it the code or hardware?
import sys
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group
# pygame initializing
pygame.init()
#create the screen surface
screen = pygame.display.set_mode((800, 700))
class Color():
def __init__(self):
self.black = (0, 0, 0)
self.white = (255, 255, 255)
self.red = (255, 0, 0)
self.green = (0, 255, 0)
self.green_lambda = (10, 255, 150)
self.blue = (0, 0, 255)
# set up the colors
color = Color() # make an instance of this class - this makes some colors available
class Spaceship(Sprite):
"""
This class represents the Spaceship.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self):
""" Constructor"""
# Call the parent class (Sprite) constructor
super().__init__()
width = 22
height = 32
self.screen = screen
self.image = pygame.Surface((width, height))
self.image.fill(color.black)
self.image.set_colorkey(color.black)
pygame.draw.polygon(self.image, color.green_lambda, [[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]],2)
self.rect = self.image.get_rect()
self.screen_rect = self.screen.get_rect()
self.rect.centerx = self.screen_rect.centerx
self.rect.bottom = self.screen_rect.bottom
# As the rect method only take integers we store a
# This value is only used at the beginning, i.e. before the game loop starts
self.center_x = self.rect.centerx
self.center_y = self.rect.centery
class Bullet(Sprite):
"""
This class represents the bullets.
It derives from the "Sprite" class in Pygame.
"""
def __init__(self):
# Call the parent class (Sprite) constructor
super().__init__()
self.image = pygame.Surface((8,10))
self.image.fill(color.red)
self.image.set_colorkey((color.red))
pygame.draw.ellipse(self.image, color.green, [1, 0, 5, 8], 2)
self.rect = self.image.get_rect()
self.rect.centerx = defender.rect.centerx
self.rect.bottom = defender.rect.top
# def function to move the bullets
def update_pos(self):
self.rect.y -= bullet_speed
# create spaceship instance
defender = Spaceship()
# create group to store sprites in
all_sprites_list = Group()
all_sprites_list.add(defender)
ship_speed = 0.5
bullet_speed = 3
def run_game():
m_right = False
m_left = False
m_up = False
m_down = False
new_bullet = False
while True:
"""This is the user interaction section"""
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
sys.exit()
elif event.key == pygame.K_RIGHT:
m_right = True
elif event.key == pygame.K_LEFT:
m_left = True
elif event.key == pygame.K_UP:
m_up = True
elif event.key == pygame.K_DOWN:
m_down = True
elif event.key == pygame.K_SPACE:
new_bullet = Bullet()
#print(dir(new_bullet))
all_sprites_list.add(new_bullet)
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
m_right = False
elif event.key == pygame.K_LEFT:
m_left = False
elif event.key == pygame.K_UP:
m_up = False
elif event.key == pygame.K_DOWN:
m_down = False
"""Below is the game logic, which gets input from the user interaction
section and more"""
# Movement of spaceship depending on the flag boolean value and on screen width and height
if m_right and defender.rect.right < defender.screen_rect.right:
defender.center_x += ship_speed
if m_left and defender.rect.left > defender.screen_rect.left:
defender.center_x -= ship_speed
if m_up and defender.rect.top > defender.screen_rect.top:
defender.center_y -= ship_speed
if m_down and defender.rect.bottom < defender.screen_rect.bottom:
defender.center_y += ship_speed
# The cumulative value (which is a float number) for the spaceships movement
# is given to the spaceship rect variable (which can only be integer) now.
# This enables fine adjusting of the speed
defender.rect.centerx = defender.center_x
defender.rect.centery = defender.center_y
all_sprites_list.update()
screen.fill(color.black)
if new_bullet:
new_bullet.update_pos()
# Below the bullets which leaves the screen display are deleted
if new_bullet.rect.bottom < defender.screen_rect.top:
all_sprites_list.remove(new_bullet)
all_sprites_list.draw(screen)
print(all_sprites_list)
pygame.display.flip()
run_game()
instead of just updating the position of new_bullet
# if new_bullet:
# new_bullet.update_pos()
# # Below the bullets which leaves the screen display are deleted
# if new_bullet.rect.bottom < defender.screen_rect.top:
# all_sprites_list.remove(new_bullet)
update the position of all bullets
for bullet in all_sprites_list:
if isinstance(bullet,Bullet):
bullet.update_pos()
if bullet.rect.bottom < defender.screen_rect.top:
all_sprites_list.remove(bullet)
del bullet
Joran Beasley's answer is correct. I'd just like to point out that you can also put the behavior of the sprites into their update methods which get called automatically when you call all_sprites_list.update(). You can actually move most of the code in the while loop to the update methods.
I've got an example with these changes and some more tips in the comments (a quick code review):
import pygame
from pygame.sprite import Sprite
from pygame.sprite import Group
# I'd just define some global constants for the colors.
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
GREEN_LAMBDA = (10, 255, 150)
class Spaceship(Sprite):
"""This class represents the Spaceship."""
def __init__(self, screen):
"""Constructor"""
super().__init__()
self.screen = screen
# pygame.SRCALPHA makes the surface transparent.
self.image = pygame.Surface((22, 32), pygame.SRCALPHA)
pygame.draw.polygon(
self.image, GREEN_LAMBDA,
[[10,0],[15,22],[20,30],[10,27],[0,30],[5,22]], 2
)
self.screen_rect = self.screen.get_rect()
# You can pass the position as the midbottom argument to `get_rect`.
self.rect = self.image.get_rect(midbottom=self.screen_rect.midbottom)
self.center_x = self.rect.centerx
self.center_y = self.rect.centery
# I've removed the `m_right`, etc. variables and just set the speed
# of the sprite in the event loop.
self.max_speed = 3.5
self.speed_x = 0
self.speed_y = 0
def update(self):
# Move the sprite.
self.center_x += self.speed_x
self.center_y += self.speed_y
self.rect.centerx = self.center_x
self.rect.centery = self.center_y
# Keep the sprite on the screen.
if not self.screen_rect.contains(self.rect):
self.rect.clamp_ip(self.screen_rect)
self.center_x, self.center_y = self.rect.center
class Bullet(Sprite):
"""This class represents the bullets."""
def __init__(self, pos):
super().__init__()
self.image = pygame.Surface((8, 10), pygame.SRCALPHA)
pygame.draw.ellipse(self.image, GREEN, [1, 0, 5, 8], 2)
self.rect = self.image.get_rect(midbottom=pos)
self.speed = 3 # The speed is now an attribute.
def update(self):
self.rect.y -= self.speed
if self.rect.top < 0:
self.kill() # Remove the sprite from all groups.
def run_game():
pygame.init()
screen = pygame.display.set_mode((800, 700))
clock = pygame.time.Clock() # Use a clock to limit the frame rate.
defender = Spaceship(screen)
all_sprites = Group() # Changed the name because groups are not lists.
all_sprites.add(defender)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
elif event.key == pygame.K_RIGHT:
defender.speed_x = defender.max_speed
elif event.key == pygame.K_LEFT:
defender.speed_x = -defender.max_speed
elif event.key == pygame.K_UP:
defender.speed_y = -defender.max_speed
elif event.key == pygame.K_DOWN:
defender.speed_y = defender.max_speed
elif event.key == pygame.K_SPACE:
new_bullet = Bullet(defender.rect.midtop) # Pass the pos.
all_sprites.add(new_bullet)
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT and defender.speed_x > 0:
defender.speed_x = 0
elif event.key == pygame.K_LEFT and defender.speed_x < 0:
defender.speed_x = 0
elif event.key == pygame.K_UP and defender.speed_y < 0:
defender.speed_y = 0
elif event.key == pygame.K_DOWN and defender.speed_y > 0:
defender.speed_y = 0
all_sprites.update() # Calls the update methods of all sprites.
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60) # Limit the frame rate to 60 FPS.
run_game()
When I try to move my character and press any other key at the same time, the movement suddenly stops. For example, the space key in my code is used to shoot a tiny orb out of the spaceship. Every time I press space and I am moving left and right quickly, the orb will shoot out but the player movement will be frozen for a second or two.
I have tried to switch to different ways of handling the way keys are input, but all of them seem to lead to this same problem. pygame.key.get_pressed() also has this problem when in my code.
I am not quite sure if this is a problem with my laptop keyboard or something in the code, so the code for the entire file is below.
import pygame, sys, decimal
# Screen Size
SCREEN_X = 400
SCREEN_Y = 400
# Loading Images
backgroundImg = pygame.image.load('StarBackground.png')
menuBar = pygame.image.load('Menu_Bar.png')
shipImg = pygame.image.load('PowerShip.png')
orb = pygame.image.load('Orb00.png')
class Ship(pygame.sprite.Sprite):
# Movement rate of change
change_x = 0
# Methods
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = shipImg.convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = SCREEN_X / 2 - 8
self.rect.y = SCREEN_Y - 40
def move(self, speed):
self.change_x = speed
def stop(self):
self.change_x = 0
def update(self, screen):
self.rect.x += self.change_x
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.right > SCREEN_X:
self.rect.x -= 1
screen.blit(self.image, self.rect)
class MenuBar(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = menuBar.convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = 10
self.rect.y = 0
def update(self, screen):
screen.blit(self.image,self.rect)
class Bullet1(pygame.sprite.Sprite):
def __init__(self,x,y):
pygame.sprite.Sprite.__init__(self)
self.image = orb.convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.alive = True
def update(self):
if self.alive == True:
self.rect.y -= 1
if self.alive == False:
self.rect.y = -10000
class HealthBar(pygame.sprite.Sprite):
pass
class EnergyBar(pygame.sprite.Sprite):
pass
class PointsBar(pygame.sprite.Sprite):
pass
class Background(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = backgroundImg.convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = 0
def update(self, screen):
if self.rect.top > 0:
self.rect.y = SCREEN_Y * -1
self.rect.y += 1
screen.blit(self.image, self.rect)
def main():
pygame.init()
size = [SCREEN_X, SCREEN_Y]
screen = pygame.display.set_mode(size, pygame.DOUBLEBUF) # Set the height and width of the screen
pygame.display.set_caption("Space Adventure") # Setting the game name in the title bar
background = Background() # Creating the game objects
menubar = MenuBar()
ship = Ship()
finished = False # Close button exit code
bullet1Enabled = True
bullet1Count = 1
spacePressed = False
clock = pygame.time.Clock() # Manages the frames per second
lastkey = None # Variable that stores the last key pressed
bulletlist = []
# Game loop
while not finished:
for event in pygame.event.get():
print(lastkey)
if event.type == pygame.QUIT:
finished = True
pygame.event.set_blocked(pygame.MOUSEMOTION)
if event.type == pygame.KEYDOWN:
if lastkey != pygame.K_SPACE:
lastkey = event.key
if event.key == pygame.K_SPACE:
spacePressed = True
if bullet1Enabled == True:
bullet1 = Bullet1(ship.rect.x, ship.rect.y)
bulletlist.append(bullet1)
bullet1Count = 1
else:
spacePressed = False
if event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT and lastkey != pygame.K_LEFT:
lastkey = None
ship.move(0)
if event.key == pygame.K_LEFT and lastkey != pygame.K_RIGHT:
lastkey = None
ship.move(0)
if event.key == pygame.K_RIGHT or lastkey == pygame.K_LEFT:
spacePressed = False
if event.key == pygame.K_LEFT or lastkey == pygame.K_RIGHT:
spacePressed = False
#Bullet Delay
if spacePressed == True:
bullet1Count = True
if spacePressed == False:
bullet1Count = False
if lastkey == pygame.K_RIGHT:
ship.move(1)
if lastkey == pygame.K_LEFT:
ship.move(-1)
clock.tick(240) # Frames per second
background.update(screen) # Background update
# Menu Bar update
ship.update(screen) # Ship update
for b in bulletlist:
if b.rect.bottom <= 0:
b.alive = False
b.update()
screen.blit(b.image, b.rect)
menubar.update(screen)
pygame.display.flip() # Updates the display for everything
pygame.quit() # Clean shutdown on IDLE
if __name__ == "__main__":
main()
The problem occurs because you don't reset lastkey to None after you release the space bar, so you have to press left or right twice.
if event.type == pygame.KEYUP:
if event.key == pygame.K_SPACE:
lastkey = None
I don't see why you need the lastkey variable at all. I'd remove these lines from the main loop,
if lastkey == pygame.K_RIGHT:
ship.move(1)
if lastkey == pygame.K_LEFT:
ship.move(-1)
insert them in the event loop and change lastkey to event.key:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
ship.move(1)
if event.key == pygame.K_LEFT:
ship.move(-1)
Now you should be able to remove the lastkey completely.