so I need to remove one sprite from the group every time I press the - (minus) key. However, I am confused as to how to do it. Another issue I've run into is when I press the + key it is adding more than one sprite at a time. I'm guessing I need to also check for KEYUP ? I also have a bug where some of the sprites will be created on top of each and will stay stuck bouncing off one another. How can I check for existing sprites where the new will be generated? Here is my code
dragon.py
import pygame
from pygame.locals import *
class Dragon(pygame.sprite.Sprite):
def __init__(self, x,y, vx, vy):
super().__init__();
self.image = pygame.image.load("images/dragon.png").convert()
self.image.set_colorkey(pygame.Color(255,0,255))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.vx = vx
self.vy = vy
def draw(self, SCREEN):
SCREEN.blit(self.image, (self.rect.x, self.rect.y))
def move(self, SCREEN):
r_collide = self.rect.x + self.image.get_width() + self.vx >= SCREEN.get_width()
l_collide = self.rect.x + self.vx <= 0
t_collide = self.rect.y + self.vy <= 0
b_collide = self.rect.y + self.image.get_height() + self.vy >= SCREEN.get_height()
# Check collision on right and left sides of screen
if l_collide or r_collide:
self.vx *= -1
# Check collision on top and bottom sides of screen
if t_collide or b_collide:
self.vy *= -1
self.rect.x += self.vx
self.rect.y += self.vy
def bounce(self, SCREEN):
self.vy *= -1
self.vx *= -1
dragon_animation.py
import pygame
import sys
from random import *
from pygame.locals import *
from flying_dragon.dragon import Dragon
def main():
pygame.init()
FPS = 30
FPS_CLOCK = pygame.time.Clock()
''' COLOR LIST '''
WHITE = pygame.Color(255,255,255)
''' create initial window '''
window_size = (500, 500)
SCREEN = pygame.display.set_mode(window_size)
''' set the title '''
pygame.display.set_caption("Flying Dragons")
''' fill background color in white '''
SCREEN.fill(WHITE)
''' group to store dragons '''
dragons = pygame.sprite.Group()
d1 = Dragon(0,0,3,2)
d1.draw(SCREEN)
dragons.add(d1)
''' main game loop '''
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
SCREEN.fill(WHITE)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_PLUS or pygame.K_KP_PLUS:
d = Dragon(randint(1, SCREEN.get_width() - dragon.rect.x), randint(1, SCREEN.get_height()- dragon.rect.y), randint(1, 5), randint(1, 5))
dragons.add(d)
for dragon in dragons:
dragons.remove(dragon)
collisions = pygame.sprite.spritecollide(dragon, dragons, False)
dragons.add(dragon)
for dragon in collisions:
dragon.bounce(SCREEN)
for dragon in dragons:
dragon.move(SCREEN)
dragons.update(SCREEN)
dragons.draw(SCREEN)
pygame.display.update()
FPS_CLOCK.tick(FPS)
if __name__ == "__main__":
main()
if event.key == pygame.K_MINUS or pygame.K_KP_MINUS:
dragons.remove(dragons[0])
Should work for removing a sprite. Sprites aren't ordered in the group so will just delete a random one.
As another pointer, you should look at redefining your Dragon.move() function as dragon.update() then the Group.update() call will move all your dragons.
you don't need to draw the dragon immediately after it's creation before the while loop. The draw inside the while loop is sufficient.
When you press a key the event list will hold which key you are holding down and as long as this is down a dragon will be created. As the loop runs pretty fast several dragons will be created before you remove your finger from the keyboard.
also good to do
pygame.event.pump()
before the for event in pygame.event.get(): so that you clear the event list beofre the next run.
read about the pygame event pump here.
Use key = pygame.key.get_pressed() instead of event.get as it will read a key press ONCE.
can't run your code because of this error.
ImportError: No module named flying_dragon.dragon
Related
I am making a space invaders game where the enemy hits the left or right side of the screen it will go down. However, I am struggling to figure out how to do the same thing but in reverse. So when it hits the end of the screen it will move left/right. Here is the code for the enemy.
import pygame
import random
# Initialize Pygame
pygame.init()
# Creates the screen for pygame
screen = pygame.display.set_mode((1000, 800))
#Enemy1
enemy1image = pygame.image.load('Enemy1.png')
enemy1image = pygame.transform.scale(enemy1image, (80, 80))
#Make it appear in a random cordinate
enemy1X = random.randint (0,1000)
enemy1y = random.randint(40,300)
enemy1X_change = 3
enemy1Y_change = 30
enemy1y_change_reverse = -30
def enemy1(x,y):
#Draws Enemy1 on screen
screen.blit(enemy1image,(x,y))
#Enemy1 Movement/boundaries
enemy1X += enemy1X_change
enemy1(enemy1X, enemy1y)
#Every time the enemy hits the boundary, it moves down
if enemy1X <= 0:
enemy1X_change = 4
enemy1y += enemy1Y_change
elif enemy1X >= 917:
enemy1X_change = -4
enemy1y += enemy1Y_change
It is difficult to answer what is specifically wrong with your code without a Minimal, Reproducible Example. Your concept appears valid, when you hit the boundary, change direction, move down and increase speed.
Here is an example that creates sprites that exhibit space-invader style movement. It it a little more effort to create sprites, but they make handling multiple game entities much easier.
import pygame
import random
screen = pygame.display.set_mode((800, 800))
pygame.init()
sprite_list = pygame.sprite.Group()
class Block(pygame.sprite.Sprite):
"""A block that moves like a space invader"""
def __init__(self, size, pos):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = pygame.Surface([size[0], size[1]])
self.image.fill(pygame.color.Color("blueviolet"))
self.rect = self.image.get_rect()
self.rect.x = pos[0]
self.rect.y = pos[1]
self.speedx = random.randint(-5, 5) # note: includes zero
self.speedy = size[1] # this only changes on edge collission
def update(self):
"""move across the screen, skip down a row when at the edge"""
width, height = screen.get_size()
if not 0 < self.rect.x < (width - self.size[0]):
self.speedx *= -1 # reverse direction
self.rect.y += self.speedy
self.rect.x += self.speedx
if self.rect.y > (height - self.size[1]):
self.kill()
# Create some random blocks in random positions
for _ in range(5):
invader = Block(
(random.randint(80, 100), random.randint(80, 100)), # size
(random.randint(0, 800), random.randint(0, 800)), # position
)
sprite_list.add(invader)
run = True
clock = pygame.time.Clock()
while run:
## Handle Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.MOUSEBUTTONUP:
# create block on mouse click
invader = Block((random.randint(80, 100), random.randint(80, 100)), event.pos)
sprite_list.add(invader)
## Clear background
screen.fill("white")
## Update Sprites
sprite_list.update()
## Draw Sprites
sprite_list.draw(screen)
## Update Screen
pygame.display.update()
clock.tick(60) # limit to 60 FPS
pygame.quit()
This question already has answers here:
How to move a sprite according to an angle in Pygame
(3 answers)
calculating direction of the player to shoot pygame
(1 answer)
Moving forward after angle change. Pygame
(1 answer)
Closed 1 year ago.
I wanna know how to shoot a projectile towards the direction the player is looking. I would also like to know how to shoot that projectile from the middle of the player. I have got my bullet class sort of ready but don't know my next step.
here is my code and replit...
https://replit.com/#TahaSSS/game-1#main.py
import pygame
from sys import exit
from random import randint
import math
from pygame.constants import K_LSHIFT, K_SPACE, MOUSEBUTTONDOWN
pygame.init()
pygame.mouse.set_visible(True)
class Player(pygame.sprite.Sprite):
def __init__(self, x , y):
super().__init__()
self.x = x
self.y = y
self.image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
self.rect = self.image.get_rect()
self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
self.rotate_vel = 1
self.cross_image = pygame.image.load('graphics/crosshair049.png')
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, self.rect)
dir_vec = pygame.math.Vector2()
dir_vec.from_polar((180, -self.rotate_vel))
cross_pos = dir_vec + self.rect.center
cross_x, cross_y = round(cross_pos.x), round(cross_pos.y)
surface.blit(self.cross_image, self.cross_image.get_rect(center = (cross_x, cross_y)))
def movement(self):
key = pygame.key.get_pressed()
self.rect = self.image.get_rect(center = (self.x, self.y))
dist = 3 # distance moved in 1 frame, try changing it to 5
if key[pygame.K_DOWN] or key[pygame.K_s]: # down key
self.y += dist # move down
elif key[pygame.K_UP] or key[pygame.K_w]: # up key
self.y -= dist # move up
if key[pygame.K_RIGHT] or key[pygame.K_d]: # right key
self.x += dist # move right
elif key[pygame.K_LEFT] or key[pygame.K_a]: # left key
self.x -= dist # move left
def rotate(self, surface):
keys = pygame.key.get_pressed()
if keys[K_LSHIFT]:
self.rotate_vel += 5
self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
self.rect = self.image.get_rect(center=self.rect.center)
surface.blit(self.image, self.rect)
if keys[K_SPACE]:
self.rotate_vel += -5
self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
self.rect = self.image.get_rect(center=self.rect.center)
surface.blit(self.image, self.rect)
def update(self):
self.movement()
self.draw(screen)
self.rotate(screen)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x , y):
super().__init__()
self.bullet_image = pygame.image.load('graphics/weapons/bullets/default_bullet.png')
self.bullet_rect = self.bullet_image.get_rect(center = (x,y))
self.bullet_speed = 15
#screen
clock = pygame.time.Clock()
FPS = 60
screen = pygame.display.set_mode((800, 400))
#player
player_sprite = Player(600, 300)
player = pygame.sprite.GroupSingle()
player.add(player_sprite)
#bullet
bullet_group = pygame.sprite.Group()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
#screen
screen.fill('grey')
#player sprite funtions
player.update()
clock.tick(FPS)
pygame.display.update()
pygame.sprite.Group.draw() and pygame.sprite.Group.update() are methods which are provided by pygame.sprite.Group.
The former delegates the to the update method of the contained pygame.sprite.Sprites - you have to implement the method. See pygame.sprite.Group.update():
Calls the update() method on all Sprites in the Group [...]
The later uses the image and rect attributes of the contained pygame.sprite.Sprites to draw the objects - you have to ensure that the pygame.sprite.Sprites have the required attributes. See 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. [...]
A Sprite kan be removed from all Groups and thus delted by calling kill.
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If this is done every frame, the position error will accumulate over time.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .center) of the rectangle.
Write a Sprite class with an update method that moves the bullet in a certain direction. Destroy the bullet when it goes out of the display:
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, angle):
super().__init__()
self.image = pygame.image.load('graphics/weapons/bullets/default_bullet.png')
self.image = pygame.transform.rotate(self.image, angle)
self.rect = self.image.get_rect(center = pos)
self.speed = 15
self.pos = pos
self.dir_vec = pygame.math.Vector2()
self.dir_vec.from_polar((self.speed, -angle))
def update(self, screen):
self.pos += self.dir_vec
self.rect.center = round(self.pos.x), round(self.pos.y)
if not screen.get_rect().colliderect(self.rect):
self.kill()
pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is True, otherwise False. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement.
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action.
Use an keyboard event to generate a new Bullet object (e.g. TAB). Call bullet_group.update(screen) and bullet_group.draw(screen) in the application loop:
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_TAB:
pos = player_sprite.rect.center
dir_vec = pygame.math.Vector2()
new_bullet = Bullet(pos, player_sprite.rotate_vel)
bullet_group.add(new_bullet)
bullet_group.update(screen)
screen.fill('grey')
player.update()
bullet_group.draw(screen)
pygame.display.update()
clock.tick(FPS)
I'm trying to build a game something like, balls are coming down randomly and the boy in the bottom catch the ball. I made a row of balls on top in a random manner, but I'm not sure how to make them appear randomly and fall down individually.
baseball_game.py
import sys
import pygame
from pygame.sprite import Group
from settings import Settings
from boy import Boy
from ball import Ball
import game_functions as gf
def run_game():
# Initialize pygame, settings, and screen object.
pygame.init()
bg_settings = Settings()
screen = pygame.display.set_mode(
(bg_settings.screen_width, bg_settings.screen_height))
pygame.display.set_caption("Catch the Baseball!")
# Make a boy
boy = Boy(bg_settings, screen)
balls = Group()
# Create the fleet of aliens.
gf.create_fleet(bg_settings, screen, boy, balls)
# Make a ball
ball = Ball(bg_settings, screen)
# Set the background color.
bg_color = (217, 208, 187)
# Start the main loop for the game.
while True:
gf.check_events(boy)
boy.update()
gf.update_balls(balls)
gf.update_screen(bg_settings, screen, boy, balls)
run_game()
ball.py
import pygame
from pygame.sprite import Sprite
import random
class Ball(Sprite):
"""A class to represent a single ball."""
def __init__(self, bg_settings, screen):
"""Initalize the ball and set its starting position."""
super(Ball, self).__init__()
self.screen = screen
self.bg_settings = bg_settings
# Load the ball image and set its rect attribute.
self.image = pygame.image.load('images/ball.png')
self.rect = self.image.get_rect()
# Start each new ball.
self.rect.x = random.randint(-10, 40)
self.rect.y = random.randint(-10, 40)
# Store the ball's exact position.
self.y = float(self.rect.y)
def update(self):
"""Move the ball down."""
self.y += self.bg_settings.ball_speed_factor
self.rect.y = self.y
def blitme(self):
"""Draw the ball at its current location."""
self.screen.blit(self.image, self.rect)
game_functions.py
import sys
import pygame
from ball import Ball
from random import randint
random_number = randint(-15, 39)
def check_events(boy):
"""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, boy)
elif event.type == pygame.KEYUP:
check_keyup_events(event, boy)
def check_keydown_events(event, boy):
"""Respond to keypresses."""
if event.key == pygame.K_RIGHT:
boy.moving_right = True
elif event.key == pygame.K_LEFT:
boy.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def check_keyup_events(event, boy):
"""Respond to key releases."""
if event.key == pygame.K_RIGHT:
boy.moving_right = False
elif event.key == pygame.K_LEFT:
boy.moving_left = False
def update_screen(bg_settings, screen, boy, balls):
"""Update images on the screen and flip to the new screen."""
# Redraw the screen during each pass through the loop.
screen.fill(bg_settings.bg_color)
boy.blitme()
balls.draw(screen)
# Make the most recently drawn screen visible.
pygame.display.flip()
def get_number_balls_x(bg_settings, ball_width):
"""Determine the number of aliens that fit in a row."""
available_space_x = bg_settings.screen_width - 2 * ball_width
number_balls_x = int(available_space_x / (2 * ball_width))
return number_balls_x
def create_ball(bg_settings, screen, balls, ball_number):
"""Create a ball and place it in the row."""
ball = Ball(bg_settings, screen)
ball_width = ball.rect.width
ball.x = ball_width + 2 * ball_width * ball_number
ball.rect.x = ball.x
balls.add(ball)
def create_fleet(bg_settings, screen, boy, balls):
ball = Ball(bg_settings, screen)
number_balls_x = get_number_balls_x(bg_settings, ball.rect.width)
for ball_number in range(number_balls_x):
create_ball(bg_settings, screen, balls, ball_number)
def update_balls(balls):
"""Update the positions of all balls in the fleet."""
balls.update()
So the first row of balls are randomly placed on top, but how do I make it to appear not all at once, and falling down separately?
Since you mention that you want the balls to fall down individually, we can think of each ball having two states: "falling" and "not falling". We can adjust the update and __init__ functions for ball.py accordingly:
class Ball(Sprite):
def __init__(self, bg_settings, screen)
# ... all your other properties
self.is_falling = False # the ball is not falling when first created
# ...
def update(self):
if self.is_falling:
self.y += self.bg_settings.ball_speed_factor
self.rect.y = self.y
Now we just need some way of triggering the falling property in each ball randomly. In game_functions.py, you have a method that updates the balls. We could add some function there to randomly decide whether we want a ball to drop, and which ball we want to drop:
from random import random
# ...
def update_balls(self):
if random() < 0.4: # 40% percent chance of making a ball drop
ball_index = randint(0, len(balls)-1)
balls[ball_index].is_falling = True
balls.update()
Make a variable called RNG and set it to generate random numbers between any range let's say between -50 and 50. Then you can make an array called RNG_CASES[] and give them any random constant value in the range that you set for the RNG. Finally, make a conditional structure:
if(RNG in RNG_CASES[]):
fall();
else:
pass; #(pass not needed, only for pseudocode).
You could just add new Ball instances to the group if some condition is True (if random.randrange(100) < 2: in the example below). If there should be a maximum number of balls, add and len(balls) < max_balls:.
Also, don't forget to kill the sprites when they leave the screen.
import random
import pygame
from pygame.sprite import Sprite
class Ball(Sprite):
"""A class to represent a single ball."""
def __init__(self, bg_settings, screen):
"""Initalize the ball and set its starting position."""
super(Ball, self).__init__()
self.screen = screen
self.bg_settings = bg_settings
self.image = pygame.Surface((30, 30))
self.image.fill((100, 200, 0))
self.rect = self.image.get_rect()
# Start each new ball.
self.rect.x = random.randint(-10, self.bg_settings.screen_width)
self.rect.y = random.randint(-100, -40)
# Store the ball's exact position.
self.y = float(self.rect.y)
def update(self):
"""Move the ball down."""
self.y += 1
self.rect.y = self.y
if self.rect.top > self.bg_settings.screen_height:
self.kill()
class Settings:
screen_width = 640
screen_height = 480
bg_color = pygame.Color('gray12')
def run_game():
pygame.init()
clock = pygame.time.Clock()
bg_settings = Settings()
screen = pygame.display.set_mode(
(bg_settings.screen_width, bg_settings.screen_height))
balls = pygame.sprite.Group()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
balls.update()
if random.randrange(100) < 2:
balls.add(Ball(bg_settings, screen))
screen.fill(bg_settings.bg_color)
balls.draw(screen)
pygame.display.flip()
clock.tick(60)
run_game()
I'm working on my first project in Python / Pygame, which is a shooter-style game. However, when I create multiple instances of my Bullet sprite and add them to the sprite group, only the most recent instance is shown on the screen. That is, only one bullet is showing at any given time.
I think Lines 175-180 or within the Bullet class are causing the problem.
My code:
import pygame, random , sys , time
from pygame.locals import *
# Screen dimensions
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
# Global constants
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
LIGHTBLUE = ( 0, 0, 155)
FPS = 60
class Player(pygame.sprite.Sprite):
# set speed vector of the player
change_x = 0
change_y = 0
moverate = 5
# Constructor. Pass in x and y position
def __init__(self, x, y):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create player image
self.image = pygame.image.load('player.png')
self.image.set_colorkey(WHITE)
# Set a referance to the image rect.
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.y = y
def changespeed(self, x, y):
""" Change the speed of the player"""
self.change_x += x
self.change_y += y
def update(self):
""" Move the player. """
# Move left/right
self.rect.x += self.change_x
# Move up/down
self.rect.y += self.change_y
def stop(self):
""" Called when the user lets off the keyboard."""
self.change_x = 0
self.change_y = 0
self.image = pygame.image.load('player.png')
self.image.set_colorkey(WHITE)
class Enemy(pygame.sprite.Sprite):
""" This class represents the enemy sprites."""
minmoverate = 1
maxmoverate = 8
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('enemyShip.png')
self.image = pygame.transform.scale(self.image, (50, 50))
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
def reset_pos(self):
""" Reset position to the top of the screen, at a random x location.
Called by update() or the main program loop if there is a collision."""
self.rect.y = - ( SCREEN_HEIGHT / 4)
self.rect.x = random.randrange(SCREEN_WIDTH)
def update(self):
""" Move the enemies. """
# Move down, at some speed
self.rect.y += 2
# Move left and right, at some speed
self.rect.x += 0
# If enemy is too far down, reset to top of screen
if self.rect.y > SCREEN_HEIGHT:
self.reset_pos()
class Bullet(pygame.sprite.Sprite):
""" This class represents the bullet. """
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([8, 20])
self.image.fill(LIGHTBLUE)
self.rect = self.image.get_rect()
def update(self):
""" Move the bullet. """
self.rect.y -= 10
class Game(object):
""" This class represents an instance of the game. If we need to
rest the game we'd just need to create a new instance of this class."""
# --- Class attributes.
# Sprite lists
enemy_list = None
bullet_list = None
all_sprites_list = None
# --- Class methods
# Set up the game
def __init__(self):
self.score = 0
self.game_over = False
# Create sprite lists
self.enemy_list = pygame.sprite.Group()
self.bullet_list = pygame.sprite.Group()
self.all_sprites_list = pygame.sprite.Group()
# Create the starting enemy ships
for i in range(15):
enemy = Enemy()
enemy.rect.x = random.randrange(SCREEN_WIDTH)
enemy.rect.y = random.randrange(-300, 20)
self.enemy_list.add(enemy)
self.all_sprites_list.add(enemy)
# Create the player
self.player = Player(SCREEN_WIDTH / 2, SCREEN_HEIGHT - (SCREEN_HEIGHT / 6))
self.all_sprites_list.add(self.player)
def process_events(self):
""" Process all of the events. Return "True" if we need to close the window."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
return True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
return True
elif event.key == K_RETURN:
if self.game_over:
self.__init__()
elif event.key in (K_RIGHT ,K_d):
self.player.changespeed( self.moverate ,0)
elif event.key in (K_LEFT ,K_a):
self.player.changespeed( -self.moverate ,0)
elif event.key in (K_UP , K_w):
self.player.changespeed(0, -self.moverate)
elif event.key in (K_DOWN , K_s):
self.player.changespeed(0, self.moverate)
elif event.key == K_SPACE: # Fire bullet
bullet = Bullet(0
# Set bullet so it is where the player is
bullet.rect.centerx = self.player.rect.centerx
bullet.rect.y = self.player.rect.y
# Add bullet to lists
self.all_sprites_list.add(bullet)
self.bullet_list.add(bullet)
elif event.type == KEYUP:
if event.key in (K_RIGHT ,K_d):
self.player.changespeed( -self.moverate ,0)
elif event.key in (K_LEFT ,K_a):
self.player.changespeed( self.moverate ,0)
elif event.key in (K_UP , K_w):
self.player.changespeed(0, self.moverate)
elif event.key in (K_DOWN , K_s):
self.player.changespeed(0, -self.moverate)
def run_logic(self):
""" This method is run each time through the frame.
It updates positions and checks for collisions."""
enemy = Enemy()
if not self.game_over:
# Move all the sprites
self.all_sprites_list.update()
if len(self.all_sprites_list) < 17:
self.enemy_list.add(enemy)
self.all_sprites_list.add(enemy)
enemy.rect.x = random.randrange(SCREEN_WIDTH)
enemy.rect.y = random.randrange(-100, -50)
# Bullet Mechanics
for bullet in self.bullet_list:
# See if the bullets has collided with anything.
self.enemy_hit_list = pygame.sprite.spritecollide(bullet, self.enemy_list, True)
# For each enemy hit, remove bullet and enemy and add to score
for enemy in self.enemy_hit_list:
self.bullet_list.remove(bullet)
self.all_sprites_list.remove(bullet)
self.score += 1
# Remove the bullet if it flies up off the screen
if bullet.rect.y < -10:
self.bullet_list.remove(bullet)
self.all_sprites_list.remove(bullet)
# Player Mechanics
for enemy in self.enemy_list:
# See if player has collided with anything.
self.player_hit_list = pygame.sprite.spritecollide(self.player, self.enemy_list, True)
if len(self.player_hit_list) == 1:
# If player is hit, show game over.
self.game_over = True
def display_frame(self, screen):
""" Display everything to the screen for the game. """
screen.fill(BLACK)
if self.game_over:
# font = pygame.font.Font("Serif:, 25)
font = pygame.font.SysFont("serif", 25)
text = font.render("Game Over! You scored " + str(self.score) +" points, press Enter to restart", True, WHITE)
center_x = (SCREEN_WIDTH // 2) - (text.get_width() // 2)
center_y = (SCREEN_HEIGHT // 2) - (text.get_height() // 2)
screen.blit(text, [center_x, center_y])
if not self.game_over:
self.all_sprites_list.draw(screen)
pygame.display.flip()
def main():
""" Main program function. """
# Initialize Pygame and set up the window
pygame.init()
size = [SCREEN_WIDTH, SCREEN_HEIGHT]
screen = pygame.display.set_mode(size)
screen_rect = screen.get_rect()
pygame.display.set_caption("My Game")
pygame.mouse.set_visible(False)
# Create our objects and set the data
done = False
clock = pygame.time.Clock()
# Create an instance of the Game class
game = Game()
# Main game loop
while not done:
# Process events (keystrokes, mouse clicks, etc)
done = game.process_events()
# Update object positions, check for collisions
game.run_logic()
# Draw the current frame
game.display_frame(screen)
# Pause for the next frame
clock.tick(FPS)
# Close window and exit
pygame.quit()
# Call the main function, start up the game
if __name__ == "__main__":
main()
The problem is that you're only ever creating a single Bullet instance, which you're storing in the Game.bullet class variable. Whenever you shoot, the code moves that single bullet to the player's position, and it updates from there.
You probably want to create a new bullet for every shot. Use something like this in process_events:
elif event.key == K_SPACE: # Fire bullet
bullet = Bullet()
bullet.rect.centerx = self.player.rect.centerx
bullet.rect.y = self.player.rect.y
# Add bullet to lists
all_sprites_list.add(self.bullet)
bullet_list.add(self.bullet)
That creates a new bullet every time the space bar is pressed. If your game design specifies a limit to the number of bullets that can be "active" at once, you may need some more complex logic (or if the performance cost of creating and destroying lots of instances is too much you could write some object caching code), but this should get you on the right track.
Now, the code above doesn't do anything to remove the bullets later, so you'll probably need to handle that too or your game will bog down from the calculations involving off-screen bullets. I'd suggest putting some logic in Bullet.update() that calls self.kill() if the bullet is off the screen (or whenever makes sense for your game). The instance will be garbage collected automatically when there are no more references to it.
I'd also suggest getting rid of all of the class variables of you Game class, which are either unnecessary (they get shadowed by instance variables that are created in __init__), or broken (like bullet).
I am building a football game (american) that has a Player class move an image by the keyboard in one program:
import pygame
import os
import random
black = (0,0,0)
white = (255,255,255)
red = (255, 0, 0)
green = (0, 100, 0)
# This class represents the bar at the bottom that the player controls
class Player(object):
def __init__(self):
self.image = pygame.image.load("player_one.png").convert()
self.image.set_colorkey(white)
self.width = 15
self.height = 15
self.x = 940
self.y = 240
def handle_keys(self):
key = pygame.key.get_pressed()
if key[pygame.K_DOWN]:
if self.y < 470:
self.y += self.height
elif key[pygame.K_UP]:
if self.y > 0:
self.y -= self.height
if key[pygame.K_RIGHT]:
if self.x < 940:
self.x += self.width
elif key[pygame.K_LEFT]:
if self.x > 0:
self.x -= self.width
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
pygame.init()
screen = pygame.display.set_mode((1000, 500))
player = Player()
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
player.handle_keys()
screen.fill(green)
for x in range(60,940,35):
pygame.draw.line(screen, white, [x, 0], [x, 500], 1)
player.draw(screen)
pygame.display.update()
clock.tick(20)
And another program that displays the enemy randomly on a background image when any key is pressed they change position:
import random
import pygame
WHITE = (255,255,255)
BLACK = (0 ,0 ,0 )
#----------------------------------------------------------------------
class Enemy():
def __init__(self, image, x=0, y=0):
self.image = pygame.image.load(image).convert()
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
#------------
def draw(self, screen):
screen.blit(self.image, self.rect)
#------------
def update(self):
# here change randomly positon
self.rect.topleft = random.randint(60,220+1), random.randint( 0, 475+1)
#----------------------------------------------------------------------
class Game():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1400,500))
self.background = pygame.image.load("GameField1.png").convert()
self.multi_enemies = []
self.players = []
# create 3 enemies 0...2
for i in range(0,3):
enemy = Enemy("enemy_"+str(i)+".png")
enemy.update() # set random position on start
self.multi_enemies.append(enemy)
#------------
def run(self):
clock = pygame.time.Clock()
RUNNING = True
while RUNNING:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
RUNNING = False
# changes position when key is pressed
for enemy in self.multi_enemies:
enemy.update()
for player in self.players:
player.handle_keys()
# --- updates ----
# place for updates
# --- draws ---
self.screen.fill(BLACK)
self.screen.blit(self.background, self.background.get_rect())
for enemy in self.multi_enemies:
enemy.draw(self.screen)
pygame.display.update()
pygame.display.flip()
# --- FPS ---
clock.tick(20)
# --- quit ---
pygame.quit()
#----------------------------------------------------------------------
Game().run()
First - Thank you for the people who helped me get this far. Second - I need to combine the Player class to the second program. I need to add collision detection so that if the player makes it to the left end zone his Score increases +7 and he goes back to the start. Also, if the player runs into an Enemy then he goes back to the start. I want the game to be on a 2 min timer so the goal is to score as much within the timeframe before the game ends.
I know a lot of people are going to recommend Sprites and I expect that but could you please provide code/explanation. Attached are my images.
I split code into 3 files Player.py, Enemy.py and Game.py
Player.py
I add restart() to set start position at new game and game restart
In handle_event I use event (to check keys) so I could check mouse events and other events if I have to - it is more universal.
handle_event return True/False if player was moved or not.
.
import pygame
#----------------------------------------------------------------------
class Player(object):
def __init__(self, surface_rect):
self.surface_rect = surface_rect
self.image = pygame.image.load("player_one.png").convert()
self.image.set_colorkey( (255,255,255) )
self.rect = self.image.get_rect() # you get image width, height
self.move_x = 15
self.move_y = 15
self.restart()
#------------
def restart(self):
self.rect.x = 940
self.rect.y = 240
#------------
def handle_events(self, event):
player_moves = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
if self.rect.bottom < self.surface_rect.bottom: #470
self.rect.y += self.move_y
player_moves = True
elif event.key == pygame.K_UP:
if self.rect.top > self.surface_rect.top:
self.rect.y -= self.move_y
player_moves = True
elif event.key == pygame.K_RIGHT:
if self.rect.right < self.surface_rect.right:
self.rect.x += self.move_x
player_moves = True
elif event.key == pygame.K_LEFT:
if self.rect.left > self.surface_rect.left:
self.rect.x -= self.move_x
player_moves = True
print "(debug): player: x, y:", self.rect.x, self.rect.y
return player_moves
#------------
def draw(self, surface):
surface.blit(self.image, self.rect)
#----------------------------------------------------------------------
Enemy.py
nothing is changed
import pygame
import random
#----------------------------------------------------------------------
class Enemy():
def __init__(self, image, x=0, y=0):
self.image = pygame.image.load(image).convert()
self.image.set_colorkey( (255,255,255) )
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
#------------
def draw(self, screen):
screen.blit(self.image, self.rect)
#------------
def update(self):
# here change randomly positon
self.rect.topleft = random.randint(60, 220+1), random.randint(0, 475+1)
#----------------------------------------------------------------------
Game.py
best part :) try to figure out what is going on in code ;)
collision detect - it could be use pygame.sprite.Sprite, pygame.sprite.Group, etc.
score for player and enemies
time counting
game over - backspace restart game after game over
.
import random
import pygame
from Player import *
from Enemy import *
WHITE = (255,255,255)
BLACK = (0 ,0 ,0 )
RED = (255,0 ,0 )
#----------------------------------------------------------------------
class Game():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1400,500))
self.background = pygame.image.load("GameField1.png").convert()
self.enemies = []
#self.players = []
self.player = Player(self.screen.get_rect())
# create 3 enemies 0...2
for i in range(3):
enemy = Enemy("enemy_"+str(i)+".png")
enemy.update() # set random position on start
self.enemies.append(enemy)
self.font = pygame.font.SysFont("", 32)
self.gameover_text = self.font.render("GAME OVER", -1, RED)
self.gameover_rect = self.gameover_text.get_rect(center=self.screen.get_rect().center)
self.restart()
#------------
def restart(self):
self.player_score = 0
self.enemies_score = 0
#self.play_time = 2*60 # 2minutes * 60 seconds
self.play_time = 30 # 30 seconds for fast test
self.change_time = pygame.time.get_ticks() + 1000 # 1s
self.player.restart()
#------------
def update_time(self):
print "(debug): time:", self.change_time, pygame.time.get_ticks()
if pygame.time.get_ticks() >= self.change_time:
self.change_time += 1000 # 1s
self.play_time -= 1
return self.play_time <= 0 # GAME OVER ?
#------------
def draw_score(self, surface):
surface_rect = surface.get_rect()
self.player_score_text = self.font.render(str(self.player_score) + " :Player", -1, WHITE)
self.player_score_rect = self.player_score_text.get_rect(right=surface_rect.right-10, top=10)
surface.blit(self.player_score_text, self.player_score_rect)
self.enemies_score_text = self.font.render("Enemy: " + str(self.enemies_score), -1, WHITE)
self.enemies_score_rect = self.enemies_score_text.get_rect(left=surface_rect.left+10, top=10)
surface.blit(self.enemies_score_text, self.enemies_score_rect)
print "(debug): render scores:", self.player_score, self.player_score_rect, self.enemies_score, self.enemies_score_rect
#------------
def draw_time(self, surface):
surface_rect = surface.get_rect()
time_str = "%02d:%02d" % (self.play_time/60, self.play_time%60)
self.time_text = self.font.render(time_str, -1, RED )
self.time_rect = self.time_text.get_rect(centerx=surface_rect.centerx, top=10)
surface.blit(self.time_text, self.time_rect)
print "(debug): render time:", self.play_time, self.time_rect, (self.play_time/60, self.play_time%60), time_str
#------------
def run(self):
clock = pygame.time.Clock()
RUNNING = True
GAME_OVER = False
while RUNNING:
# --- events ---
PLAYER_MOVES = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
RUNNING = False
if event.key == pygame.K_BACKSPACE:
if GAME_OVER:
GAME_OVER = False
self.restart()
# player moves
if not GAME_OVER:
PLAYER_MOVES = self.player.handle_events(event)
# --- updates ----
if PLAYER_MOVES and not GAME_OVER:
# changes position when key is pressed
for enemy in self.enemies:
enemy.update()
# check collisions
collision = False
for enemy in self.enemies:
if pygame.sprite.collide_rect(self.player, enemy):
collision = True
break # first collision and I don't check rest enemies
if collision:
self.enemies_score += 7
print "(debug): game: collision:", self.player_score, self.enemies_score
self.player.restart()
# check touchdown
if self.player.rect.left <= 100:
self.player_score += 7
print "(debug): game: touchdown:", self.player_score, self.enemies_score
self.player.restart()
if not GAME_OVER:
GAME_OVER = self.update_time()
# --- draws ---
self.screen.fill(BLACK)
self.screen.blit(self.background, self.background.get_rect())
self.player.draw(self.screen)
for enemy in self.enemies:
enemy.draw(self.screen)
self.draw_time(self.screen)
self.draw_score(self.screen)
if GAME_OVER:
self.screen.blit(self.gameover_text, self.gameover_rect)
pygame.display.update()
# --- FPS ---
clock.tick(20)
#----------------------------------------------------------------------
Game().run()
I'm a little confused as to what you are asking, I hope this will answer all your questions:
First of all wouldn't the enemies "teleport" to random locations each time they update? I'm not sure what you are trying to achieve but it should be better if you randomize their location on init (where you set their x and y to 0 ) and on the update you should create some Artificial Intelligence (like following the hero? )
class Enemy():
def __init__(self, image):
self.image = pygame.image.load(image).convert()
self.image.set_colorkey(WHITE)
self.rect = self.image.get_rect()
self.rect.centerx = random.randint(60,220+1)
self.rect.centery = random.randint( 0, 475+1)
#------------
def draw(self, screen):
screen.blit(self.image, self.rect)
#------------
def update(self, heroX, heroY):
#Make enemies follow hero (for example)
if heroX > self.rect.centerx
self.rect.centerx += 10; #10 is example value, set the one you like!
else if heroX < self.rect.centerx
self.rect.centerx -= 10;
#Do the same for y
#------------
def reset(self): #This is called when hero scores, or enemies touch hero (and you want to reset hero to starting point and enemies)
self.rect.centerx = random.randint(60,220+1)
self.rect.centery = random.randint( 0, 475+1)
Just make sure to pass player's x and y when you update the enemy.
About collision, there is a simple algorithm that goes like that: If you have objectA and objectB they only collide if objectA.right > objectB.left && objectA.left < objectB.right , combine top and bottoms the same way and you are done
if (player.right > enemy.left && player.left < enemy.right && player.bottom > enemy.top && player.top < enemy.bottom)
player.reset()
enemy.reset()
apply this algorithm once for each enemy (and hero, if there are more than one)
About the timer, you already have a timer to limit frames, you can use that to count seconds inside the game and create limits (use your imagination!)
I would recommend sprites! I'm not going to tell you everything because figuring game programming out is half the fun!
First of all, you could set pygame groups up, like
player_list = pygame.sprite.Group()
player_list.add(player)
for player and enemy, and test a collision between them both using pygame.sprite.spritecollide and maybe something similar for your left zone of the pitch..
Then as furas mentioned, just copy or import player class. :)
Also, I would suggest pygame.time.get_ticks() for your timer, although you can do it other ways..
The rest is down to you! I hope this helped in some way, ask any questions if you don't understand!
I have done this on a phone so if someone wants to edit it for all the codey bits that'd be great :)