Trying to move a sprite in pygame - python

I'm trying to make a bullet move in Pygame. Apologies if it has a simple fix, I just can't think at the moment.
This is what I run when I detect that the "1" button is pressed.
if pygame.event.get().type == KEYDOWN and e.key == K_1:
bullet = Bullet()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
entities.add(bullet)
bullet_list.add(bullet)
bullet.update()
...and this is the actual bullet class. The spacing is a bit off.
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super(Bullet, self).__init__()
self.image = pygame.Surface([4, 10])
self.image.fill(pygame.Color(255, 255, 255))
self.isMoving = False
self.rect = self.image.get_rect()
def update(self):
for i in range(20):
self.rect.x += 3
I understand that the update method is happening instantly rather than the slower movement I want. How am I supposed to make the bullet move slower?
All the answers that I've seen involve completely stopping the program instead of just stopping the one object. Is there a way around it?

You need to update all bullets on every game tick, not just when a player presses a button.
So, you'll want something like this as your event loop:
clock = pygame.time.Clock()
while True:
clock.tick(60)
for event in pygame.event.get():
if event == KEYDOWN and event.key == K_1:
bullet = Bullet()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
entities.add(bullet)
bullet_list.add(bullet)
for bullet in bullet_list:
bullet.update()
And, modify the Bullet class to do incremental moves, like so:
class Bullet(pygame.sprite.Sprite):
def __init__(self):
super(Bullet, self).__init__()
self.image = pygame.Surface([4, 10])
self.image.fill(pygame.Color(255, 255, 255))
self.isMoving = False
self.rect = self.image.get_rect()
def update(self):
self.rect.x += 3

Related

Pygame - Getting a bullet to fire up the screen on a MOUSEBUTTONDOWN event

Creating a space invaders style game using sprites as a pose to images - Trying to get the bullet to continually move up the screen until it detects that it's passed y = 0. I've got an function to fire that moves the bullet up, but only by 5 pixels per MOUSEBUTTONDOWN event detected. Ideally, it needs to continue moving to the top of the screen after only one event is fired.
Here is the relevant code concerning the bullet class and the event loop.
# bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self,path):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(path)
self.rect = self.image.get_rect()
def shoot(self,pixels):
self.rect.y -= pixels
def track(self):
bullet.rect.x = player.rect.x + 23
And the event loop:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.moveLeft(5)
if event.key == pygame.K_RIGHT:
player.moveRight(5)
if event.type == pygame.MOUSEBUTTONDOWN:
bullet.shoot(5)
bullet.track()
playerGroup.update()
gameDisplay.blit(backgroundImg,(0,0))
playerGroup.draw(gameDisplay)
bulletGroup.update()
bulletGroup.draw(gameDisplay)
pygame.display.update()
clock.tick(60)
Thanks to anyone who answers my question.
You can put an additional attribute shot in your bullet class. It
is initially set to 0 and updated when the shoot method is called.
Then the track method updates the y position of the bullet
accordingly.
class Bullet(pygame.sprite.Sprite):
def __init__(self,path):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(path)
self.rect = self.image.get_rect()
self.shot = 0 # bullet not shot initially
def shoot(self, pixels):
self.shot = pixels
def track(self):
self.rect.y -= self.shot
So, as soon as the button is clicked, the shot attribute is modified
and the bullet will keep moving.

I'm wondering why my player and my balloon which are both sprites is stuttering when I move the player

My frames drop and the game basically stops whenever I use the WASD keys, which are my movement controls. Its supposed to make the water balloon move but when I stop moving so does the water balloon. They're both sprites.
I might have got them mixed up since there are two sprite families.
main.py:
import pygame
from player import Player
from projectile import WaterBaloon
# Start the game
pygame.init()
game_width = 1000
game_height = 650
screen = pygame.display.set_mode((game_width, game_height))
clock = pygame.time.Clock()
running = True
bg_image = pygame.image.load("../assets/BG_Sand.png")
#make all the sprite groups
playerGroup = pygame.sprite.Group()
projectilesGroup = pygame.sprite.Group()
#put every sprite class in a group
Player.containers = playerGroup
WaterBaloon.containers = projectilesGroup
#tell the sprites where they are gonna be drawn at
mr_player = Player(screen, game_width/2, game_height/2)
# ***************** Loop Land Below *****************
# Everything under 'while running' will be repeated over and over again
while running:
# Makes the game stop if the player clicks the X or presses esc
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
#player input that detects keys (to move)
keys = pygame.key.get_pressed()
if keys[pygame.K_d]:
mr_player.move(1, 0)
if keys[pygame.K_a]:
mr_player.move(-1, 0)
if keys[pygame.K_w]:
mr_player.move(0, -1)
if keys[pygame.K_s]:
mr_player.move(0, 1)
if pygame.mouse.get_pressed()[0]:
mr_player.shoot()
#blit the bg image
screen.blit((bg_image),(0, 0))
#update the player
mr_player.update()
#update all the projectiles
for projectile in projectilesGroup:
projectile.update()
# Tell pygame to update the screen
pygame.display.flip()
clock.tick(60)
pygame.display.set_caption("ATTACK OF THE ROBOTS fps: " + str(clock.get_fps()))
player.py:
import pygame
import toolbox
import projectile
#Players Brain
class Player(pygame.sprite.Sprite):
#player constructor function
def __init__(self, screen, x, y):
pygame.sprite.Sprite.__init__(self, self.containers)
self.screen = screen
self.x = x
self.y = y
self.image = pygame.image.load("../assets/player_03.png")
#rectangle for player sprite
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
#end of rectangle for player sprite
self.speed = 8
self.angle = 0
#player update function
def update(self):
self.rect.center = (self.x, self.y)
mouse_x, mouse_y = pygame.mouse.get_pos()
self.angle = toolbox.angleBetweenPoints(self.x, self.y, mouse_x, mouse_y)
#get the rotated version of player
image_to_draw, image_rect = toolbox.getRotatedImage(self.image, self.rect, self.angle)
self.screen.blit(image_to_draw, image_rect)
#tells the computer how to make the player move
def move(self, x_movement, y_movement):
self.x += self.speed * x_movement
self.y += self.speed * y_movement
#shoot function to make a WaterBaloon
def shoot(self):
projectile.WaterBaloon(self.screen, self.x, self.y, self.angle)
You're only processing one event per frame. That's because you've put everything you do inside the frame in the event handling loop. That is likely to break down when you start getting events (such as key press and release events) at a higher rate. If events are not all being processed, your program may not update the screen properly.
You probably want your main loop to only do event processing stuff in the for event in pygame.event.get(): loop. Everything else should be unindented by one level, so that it runs within the outer, while running: loop, after all pending events have been handled.
Try something like this:
while running:
# Makes the game stop if the player clicks the X or presses esc
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
#player input that detects keys (to move) # unindent here (and all the lines below)
keys = pygame.key.get_pressed()
...

Pygame - Creating a "Enemy" class, and then importing it into game

Okay, so basically what I'm trying to do is keep the main file a little cleaner and I'm starting with the "Zombie" enemy by making it's own file which will most likely contain all enemies, and importing it in.
So I'm confused on how I'd set-up the Class for a sprite, you don't have to tell me how to get it to move or anything like that I just want it to simply appear. The game doensn't break when I run it as is, I just wanted to ask this question before I goto sleep so I can hopefully get a lot done with the project done tomorrow (School related)
Code is unfinished like I said just wanted to ask while I get some actual sleep just a few google searches and attempts.
Eventually I'll take from the advice given here to make a "Hero" class as well, and as well as working with importing other factors if we have the time.
Zombie code:
import pygame
from pygame.locals import *
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
# self.images.append(img)
# self.image = self.images[0]
self.rect = self.image.get_rect()
zombieX = 100
zombieY = 340
zombieX_change = 0
Main Code:
import pygame
from pygame.locals import *
import Zombie
# Intialize the pygame
pygame.init()
# Create the screen
screen = pygame.display.set_mode((900, 567))
#Title and Icon
pygame.display.set_caption("Fighting Game")
# Add's logo to the window
# icon = pygame.image.load('')
# pygame.display.set_icon(icon)
# Player
playerImg = pygame.image.load('images/character.png')
playerX = 100
playerY = 340
playerX_change = 0
def player(x,y):
screen.blit(playerImg,(x,y))
Zombie.ZombieEnemy()
def zombie(x,y):
screen.blit()
# Background
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load('images/background.png')
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
BackGround = Background('background.png', [0,0])
# Game Loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# If keystroke is pressed check right, left.
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#playerX_change = -2.0
BackGround.rect.left = BackGround.rect.left + 2.5
if event.key == pygame.K_RIGHT:
#playerX_change = 2.0
BackGround.rect.left = BackGround.rect.left - 2.5
# if event.type == pygame.KEYUP:
# if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
# BackGround.rect.left = 0
screen.blit(BackGround.image, BackGround.rect)
playerX += playerX_change
player(playerX,playerY)
pygame.display.flip()
Your sprite code is basically mostly there already. But as you say, it needs an update() function to move the sprites somehow.
The idea with Sprites in PyGame is to add them to a SpriteGroup, then use the group functionality for handling the sprites together.
You might want to modify the Zombie class to take an initial co-ordinate location:
class ZombieEnemy(pygame.sprite.Sprite):
def __init__( self, x=0, y=0 ):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.center = ( x, y ) # NOTE: centred on the co-ords
Which allows the game to create a Zombie at a particular starting point (perhaps even a random one).
So to have a sprite group, first you need to create the container:
all_zombies = pygame.sprite.Group()
Then when you create a new zombie, add it to the group. Say you wanted to start with 3 randomly-positioned zombies:
for i in range( 3 ):
new_x = random.randrange( 0, WINDOW_WIDTH ) # random x-position
new_y = random.randrange( 0, WINDOW_HEIGHT ) # random y-position
all_zombies.add( Zombie( new_x, new_y ) ) # create, and add to group
Then in the main loop, call .update() and .draw() on the sprite group. This will move and paint all sprites added to the group. In this way, you may have separate groups of enemies, bullets, background-items, etc. The sprite groups allow easy drawing and collision detection between other groups. Think of colliding a hundred bullets against a thousand enemies!
while running:
for event in pygame.event.get():
# ... handle events
# Move anything that needs to
all_zombies.update() # call the update() of all zombie sprites
playerX += playerX_change
# Draw everything
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
all_zombies.draw( screen ) # paint every sprite in the group
pygame.display.flip()
EDIT: Added screen parameter to all_zombies.draw()
It's probably worthwhile defining your player as a sprite too, and having a single-entry group for it as well.
First you could keep position in Rect() in class. And you would add method which draws/blits it.
import pygame
class ZombieEnemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('images/zombie.png')
self.rect = self.image.get_rect()
self.rect.x = 100
self.rect.y = 340
self.x_change = 0
def draw(self, screen):
screen.blit(self.image, self.rect)
Next you have to assign to variable when you create it
zombie = Zombie.ZombieEnemy()
And then you can use it to draw it
zombie.draw(screen)
in
screen.blit(BackGround.image, BackGround.rect)
player(playerX,playerY)
zombie.draw(screen)
pygame.display.flip()
The same way you can create class Player and add method draw() to class Background and then use .
background.draw(screen)
player.draw(screen)
zombie.draw(screen)
pygame.display.flip()
Later you can use pygame.sprite.Group() to keep all objects in one group and draw all of them using one command - group.draw()

pygame sprite not moving automatically with each sprite update

I'm trying to make a cut scene for a pygame that I'm building, and in this pygame I'm trying to get the main player sprite to spawn at the rightmost side of the screen, before slowly moving towards the center. For some reason, rather than moving, it just stays completely still, despite the fact that the same logics that I applied in the cutscene works completely fine in the main game when I'm using keypresses to move the player instead of an automatic update.
I basically just followed the same logic that I did in my main game except instead of moving as a result of a keypress, it moves as a result of a sprite update. This is my code for the sprite:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 60))
self.image = pygame.image.load("soldier.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.x = WIDTH - 30
self.rect.y = HEIGHT - 60
self.speedx = -5
self.speedy = 0
self.count = 0
tru = False
def update(self):
self.count += 1
if self.count > 10:
self.speedx -= 5
self.count = 0
else:
self.speedx = 0
if self.rect.x < WIDTH / 2:
self.speedx = 0
self.rect.x += self.speedx
And here is my main game loop
game_over = True
running = True
CurrentLevel = 1
while running:
all_sprites = pygame.sprite.Group()
curtime = pygame.time.get_ticks()
platforms = pygame.sprite.Group()
bullets = pygame.sprite.Group()
grounds = pygame.sprite.Group()
thentime = pygame.time.get_ticks()
for plat in PLATFORMS:
p = Platform(*plat)
all_sprites.add(p)
platforms.add(p)
for gro in GROUNDS:
g = Ground(*gro)
all_sprites.add(g)
grounds.add(g)
player = Player()
all_sprites.add(player)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
running = False
all_sprites.update()
screen.fill(WHITE)
screen.blit(bg, [0, 0])
all_sprites.draw(screen)
pygame.display.flip()
This is what the outcome becomes:
Output
That player sprite at the far right side of the screen just remains there and doesn't move like it should.
Does anyone know whats going on?
Inside your while running loop, you have the line player = Player() and all_sprites.add(player). That means that each frame, your game is creating a new player object and instantiating it on the right side of the screen, so it never has a chance to move.

FIRE. Checking if a list of rectangles collides with a player rectangle

I know i need a loop to check if a list of bullets hits a player.
I've tried researching online for 2 hours for reference code but all use sprites and classes.
#Bullet Collision with players
for i in range(len(bullets_2)):
#player is a rectangle style object
if bullets_2[i].colliderect(player):
player_health -= 10
Sadly enough my computer science teacher hasn't taught the class about sprites or classes, so let's avoid that.
I tried having the code above check if the list collides with the rectangle player.
The point of the above code is for the game to to take health away from the health bar if the enemy player's bullets hit the player.
EDIT:
I only have about 8 days to finish this game
TLDR:
How do I check to see if a list collides with a rectangle.
As in the comment, here's the code.
Here's the images I am using
import pygame, time, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
class playerClass(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png").convert()
self.image.set_alpha(196)
self.rect = self.image.get_rect()
self.rect.y = 260
self.rect.x = 500
def update(self):
pass
class bulletClass(pygame.sprite.Sprite):
def __init__(self, speed, starty):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("bullet.png").convert()
self.rect = self.image.get_rect()
self.rect.y = starty
self.rect.x = 0
self.speed = speed
def update(self):
self.rect.x += self.speed
gameExit = False
player = playerClass()
bullet1 = bulletClass(7,265)
bullet2 = bulletClass(10,295)
bullet3 = bulletClass(7,325)
bulletSprites = pygame.sprite.RenderPlain((bullet1, bullet2, bullet3))
playerSprites = pygame.sprite.RenderPlain((player))
bulletRectList = [bullet1.rect, bullet2.rect, bullet3.rect]
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
screen.fill((255,255,255))
bulletSprites.update()
bulletSprites.draw(screen)
playerSprites.update()
playerSprites.draw(screen)
collidedBulletList = player.rect.collidelistall(bulletRectList)
if len(collidedBulletList) > 0:
for i in collidedBulletList:
print(i)
pygame.display.update()
clock.tick(10)
In this code, if you want to add a bullet just declare a new object
bullet4 = bulletClass(speed, yCoordinateToBeginWith)
and append it to bulletSprites and the bulletRectList and you are done. Using sprites simplifies this. Making your code sprite friendly would be difficult at the beginning but appending it is definitely easy afterwards.

Categories

Resources