Not able to make my cannon fire bullets in pygame - python

In my game I can place cannons anywhere on the screen. I want my cannons to be able to fire bullets and the bullets should be reset after they travel 100 pixels. Given below is my cannon class, I'm still new to OOP so I require some help however I wasn't able to accomplish this task using lists either. Thank you for your help.
class Cannon():
global cannon_list
global bullet_list
def __init__(self, x, y, track, old_x):
self.x = x
self.y = y
self.track = track
self.old_x = old_x
def spawnBullet(self):
for j in bullet_list:
self.old_x = self.x
self.track = j[2]
screen.blit(bullet, (j[0], j[1]))
def moveBullet(self):
if self.x <= self.track:
self.x += 3
def resetBullet(self):
if self.x >= self.track:
self.x = self.old_x
def spawnCannon(self):
for i in cannon_list:
screen.blit(cannon, i)
Using the cannon class. this is under redrawGamewindow.
for j in bullet_list:
cannonsAndBullets = Cannon(j[0], j[1], j[2], j[0])
cannonsAndBullets.spawnCannon()
cannonsAndBullets.spawnBullet()
cannonsAndBullets.moveBullet()
cannonsAndBullets.resetBullet()
Given below is what I have appended into bullet_list and cannon_list. x an y are the position of my player
cannon_list.append([x,y])
bullet_list.append([x,(y+25),100, x])
Edits in my class
class Cannon():
global cannon_list
global bullet_list
def __init__(self, x, y, track, old_x):
self.x = x
self.y = y
self.track = track
self.old_x = old_x
def spawnBullet(self):
# for j in bullet_list:
# self.x, self.y, self.track, self.old_x = j
screen.blit(bullet, (self.x, self.y))
def moveBullet(self):
# for j in bullet_list:
# self.x, self.y, self.track, self.old_x = j
if self.track <= 100:
print(self.track)
self.track += 3
self.x += 3
def resetBullet(self):
# for j in bullet_list:
# self.x, self.y, self.track, self.old_x = j
if self.track >= 100:
print(self.track)
self.x = self.old_x
def spawnCannon(self):
for i in cannon_list:
screen.blit(cannon, i)

Let's discard everything and start from scratch and make use of pygame features like sprites and vector math.
We begin with a basic skeleton of a pygame game, a simple window:
import pygame
def main():
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill(pygame.Color('grey'))
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Now, we want to place some Sprites. Let's create a Sprite class that represents the cannons, and let's place them with the mouse:
import pygame
class Cannon(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((32, 32))
self.image.fill(pygame.Color('darkred'))
self.rect = self.image.get_rect(center=pos)
def main():
all_sprites = pygame.sprite.Group()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.MOUSEBUTTONDOWN:
Cannon(e.pos, all_sprites)
all_sprites.update()
screen.fill(pygame.Color('grey'))
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
Now we want the cannons to shoot bullets. To do that, we're making use of some OOP features, like polymorphism. Bullets and cannons are different types, but they provide the same interface: update and draw. And that's it. Note how our mainloop does not need to know what types exactly our sprites are.
We also make sure that all the logic for bullets is in the Bullet class, and all the logic for the cannon is in the Cannon class:
import pygame
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((4, 4))
self.image.fill(pygame.Color('black'))
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
self.travelled = pygame.Vector2(0, 0)
direction = pygame.Vector2(pygame.mouse.get_pos()) - self.pos
if direction.length() > 0:
self.direction = direction.normalize()
else:
self.kill()
def update(self, dt):
v = self.direction * dt / 5
self.pos += v
self.travelled += v
self.rect.center = self.pos
if self.travelled.length() > 100:
self.kill()
class Cannon(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((32, 32))
self.image.fill(pygame.Color('darkred'))
self.rect = self.image.get_rect(center=pos)
self.timer = 200
def update(self, dt):
self.timer = max(self.timer - dt, 0)
if self.timer == 0:
self.timer = 200
Bullet(self.rect.center, self.groups()[0])
def main():
all_sprites = pygame.sprite.Group()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
dt = 1
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == pygame.MOUSEBUTTONDOWN:
Cannon(e.pos, all_sprites)
all_sprites.update(dt)
screen.fill(pygame.Color('grey'))
all_sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(60)
if __name__ == '__main__':
main()
Using a vector and simply adding the distance travelled each frame makes it easy to check for each Bullet if it already travelled 100 pixels. If true, simply calling kill will remove it.

Related

Pygame 2D tiled game, moving camera. Character moving faster than camera

I had a problem with creating camera in pygame, I assumed code below should work but our player is moving faster than camera and is going out of the window. Somebody know what's the issue?
import pygame, sys
class Player(pygame.sprite.Sprite):
def __init__(self, pos, group):
super().__init__(group)
self.image = pygame.image.load('./chatacters/players/player_one.png').convert_alpha()
self.rect = self.image.get_rect(center=(640, 360))
self.direction = pygame.math.Vector2()
self.speed = 5
# key inputs
def input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
self.direction.y = -1
elif keys[pygame.K_DOWN]:
self.direction.y = 1
else:
self.direction.y = 0
if keys[pygame.K_LEFT]:
self.direction.x = -1
elif keys[pygame.K_RIGHT]:
self.direction.x = 1
else:
self.direction.x = 0
# Moving using inputs
def move(self, speed):
if self.direction.magnitude() != 0:
self.direction = self.direction.normalize()
self.rect.center += self.direction * speed
# updating drawing
def update(self):
self.input()
self.move(self.speed)
class Camera(pygame.sprite.Group):
def __init__(self):
super().__init__()
self.display_surface = pygame.display.get_surface()
self.offset = pygame.math.Vector2()
self.half_w = self.display_surface.get_size()[0] // 2
self.half_h = self.display_surface.get_size()[1] // 2
self.map = pygame.image.load('./map/level_data/level.png').convert_alpha()
self.rect = self.map.get_rect(topleft=(0, 0))
def custom_draw(self, player):
self.offset.x = player.rect.centerx - self.half_w
self.offset.y = player.rect.centery - self.half_h
ground_offset = self.rect.topleft - self.offset
self.display_surface.blit(self.map, ground_offset)
class Game():
def __init__(self):
# Settings
self.WIDTH = 1280
self.HEIGHT = 720
self.FPS = 60
pygame.init()
pygame.display.set_caption('BetterFortinite')
self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
self.clock = pygame.time.Clock()
self.camera_group = Camera()
self.player = Player((100, 200), self.camera_group)
def game(self):
while True:
self.clock.tick(self.FPS)
self.screen.fill('black')
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# self.screen.fill("WHITE")
self.camera_group.custom_draw(self.player)
self.player.move(5)
self.player.update()
self.camera_group.draw(self.screen)
# self.camera_group.update()
pygame.display.update()
if __name__ in '__main__':
game = Game()
game.game()
I'm taking the center position of player rect minus half of the width size. Same with height and setting with it my offset. Then I'm setting my ground_offset as cords of topleft screen rect minus offset. What is wrong with this formula?
The problem is not with your formula, but with the code itsself. In the main game loop, you have:
self.player.move(5)
self.player.update()
While Player.update contains:
def update(self):
self.input()
self.move(self.speed)
As you can see, player.move is called twice. This means that the player is moved twice as much as intended and thus twice as fast as the camera, causing both to move at a different speed.
The solution to this problem would be to remove one of the calls of Player.move. I would remove the one in the main game loop as it uses a hardcoded value rather than the Player.speed constant, but it doesn't really matter which one you remove.

Pygame Delttime Function to go to the Right is 10 times slower than when I go to the left [duplicate]

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
Closed 1 year ago.
Hello Stackoverflow Community,
could someone help me with my problem.
When I move to the left I am quite fast but when I want to go to the right it takes way to lang. I deleted the dt from my move function and the went the same speed. I changed the buttons but the problem seems only to occure when I use the +=. Does one of you dealt with the problem before and could help me?
import pygame
import time
import random
#0 = Menu, 1 = Game, 2 = Quit
game_state = 1
WHITE = (255, 255, 255)
class Player(pygame.sprite.Sprite):
speed = 100
def __init__(self, WIDTH):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png")
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, 480)
def move(self, keys, dt):
if(keys[0]):
self.rect.x += self.speed * dt
print(self.speed)
print(dt)
print(self.rect.x)
if(keys[1]):
print(self.speed)
print(dt)
self.rect.x -= self.speed * dt
print(self.rect.x)
class Main():
prev_time = time.time()
dt = 0
#initalizing menu or game depending on variables
def __init__(self):
global game_state
while game_state !=2:
WIDTH, HEIGHT = 800, 600
screen = self.initialize_pygame("Alien Slaughter", WIDTH, HEIGHT)
self.all_sprites = pygame.sprite.Group()
self.player = Player(WIDTH)
self.all_sprites.add(self.player)
if(game_state == 1):
self.prev_time = time.time()
self.game(screen, WIDTH, HEIGHT)
#calculating deltatime
def calc_deltatime(self):
self.now = time.time()
self.dt = self.now - self.prev_time
self.prev_time = self.now
#initializing pygame and create window
def initialize_pygame(self, title, width, height):
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(title)
icon = pygame.image.load("icon.png")
pygame.display.set_icon(icon)
pygame.init()
return screen
def handle_inputs(self, keys, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_d:
keys[0] = True
if event.key == pygame.K_a:
keys[1] = True
if event.type == pygame.KEYUP:
if event.key == pygame.K_d:
keys[0] = False
if event.key == pygame.K_a:
keys[1] = False
def game(self, screen, width, height):
global game_state
BLACK = (100, 100, 100)
keys = [False, False]
#Game loop
while game_state == 1:
#Process input (events)
for event in pygame.event.get():
#Check for Closing windows
if event.type == pygame.QUIT:
game_state = 2
self.handle_inputs(keys, event)
self.calc_deltatime()
self.player.move(keys, self.dt)
#Update
self.all_sprites.update()
#Draw / render
screen.fill(BLACK)
self.all_sprites.draw(screen)
pygame.display.update()
game = Main()
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 movement is added to the coordinates of 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 coordinate and assign it to the location of the rectangle:
class Player(pygame.sprite.Sprite):
speed = 100
def __init__(self, WIDTH):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("player.png")
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, 480)
self.x = self.rect.x
def move(self, keys, dt):
if keys[0]:
self.x += self.speed * dt
if keys[1]:
self.x -= self.speed * dt
self.rect.x = round(self.x)

Pygame problem falling objects doesnt show up

Hey can someone tell me why I dont see the objects falling from the top? In this case The "Münzen, Scheine, and Sack". I am new to pygame and maybe there is an easy way. My game should be like this:
The walking man should catch money falling down and get points for it.
https://gyazo.com/d2ce52cb54e8658e92ae9e5f3d1e7cca
import random
from pygame.sprite import Sprite
pygame.init()
#Display
win = pygame.display.set_mode((900,780))
pygame.display.set_caption("The Collector")
#Laufanimation
walkRight = [pygame.image.load('Assets/R1.png'), pygame.image.load('Assets/R2.png'), pygame.image.load('Assets/R3.png'), pygame.image.load('Assets/R4.png'), pygame.image.load('Assets/R5.png'), pygame.image.load('Assets/R6.png'), pygame.image.load('Assets/R7.png'), pygame.image.load('Assets/R8.png'), pygame.image.load('Assets/R9.png')]
walkLeft = [pygame.image.load('Assets/L1.png'), pygame.image.load('Assets/L2.png'), pygame.image.load('Assets/L3.png'), pygame.image.load('Assets/L4.png'), pygame.image.load('Assets/L5.png'), pygame.image.load('Assets/L6.png'), pygame.image.load('Assets/L7.png'), pygame.image.load('Assets/L8.png'), pygame.image.load('Assets/L9.png')]
#Hintergrund
bg = pygame.image.load('City.jpg')
bg = pygame.transform.scale(bg, (900,780))
#Charakter
char = pygame.image.load('Assets/R1.png')
# Geld
Münzen2 = pygame.image.load("Assets/coin.png")
Schein2 = pygame.image.load("Assets/schein.png")
Sack2 = pygame.image.load("Assets/sack.png")
# Geld
Münzen = pygame.image.load("Assets/coin.png")
Schein = pygame.image.load("Assets/schein.png")
Sack = pygame.image.load("Assets/sack.png")
clock = pygame.time.Clock()
class player():
def __init__(self,x,y,width,height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 20
self.left = False
self.right = False
self.walkCount = 0
self.hitbox = (self.x + 20, self.y, 28, 60)
def draw(self, win):
if self.walkCount + 1 >= 27:
self.walkCount = 0
if self.left:
win.blit(walkLeft[self.walkCount//3], (self.x,self.y))
self.walkCount += 1
elif self.right:
win.blit(walkRight[self.walkCount//3], (self.x,self.y))
self.walkCount +=1
else:
win.blit(char, (self.x,self.y))
self.hitbox = (self.x + 215, self.y + 230, 220, 70) # NEW
pygame.draw.rect(win, (255,0,0), self.hitbox,2)
class Laser:
def __init__(self, x, y, img):
self.x = x
self.y = y
self.img = img
def draw(self, window):
window.blit(self.img, (self.x, self.y))
def move(self, vel):
self.y += vel
def off_screen(self, height):
return not(self.y <= height and self.y >= 0)
def collision(self, obj):
return collide(self, obj)
def redrawGameWindow():
win.blit(bg, (0,0))
collector.draw(win)
pygame.display.update()
class Ship:
COOLDOWN = 30
def __init__(self, x, y, health=100):
self.x = x
self.y = y
self.health = health
self.ship_img = None
self.laser_img = None
self.lasers = []
self.cool_down_counter = 0
def draw(self, window):
window.blit(self.ship_img, (self.x, self.y))
for laser in self.lasers:
laser.draw(window)
def move_lasers(self, vel, obj):
self.cooldown()
for laser in self.lasers:
laser.move(vel)
if laser.off_screen(HEIGHT):
self.lasers.remove(laser)
elif laser.collision(obj):
obj.health -= 10
self.lasers.remove(laser)
def cooldown(self):
if self.cool_down_counter >= self.COOLDOWN:
self.cool_down_counter = 0
elif self.cool_down_counter > 0:
self.cool_down_counter += 1
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def get_width(self):
return self.ship_img.get_width()
def get_height(self):
return self.ship_img.get_height()
class Enemy(Ship):
COLOR_MAP = {
"red": (Münzen, Münzen2),
"green": (Schein, Schein2),
"blue": (Sack, Sack2)
}
def __init__(self, x, y, color, health=100):
super().__init__(x, y, health)
self.ship_img, self.laser_img = self.COLOR_MAP[color]
self.mask = pygame.mask.from_surface(self.ship_img)
def move(self, vel):
self.y += vel
def shoot(self):
if self.cool_down_counter == 0:
laser = Laser(self.x-20, self.y, self.laser_img)
self.lasers.append(laser)
self.cool_down_counter = 1
def collide(obj1, obj2):
offset_x = obj2.x - obj1.x
offset_y = obj2.y - obj1.y
return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
#mainloop
collector = player(200, 410, 64,64)
run = True
while run:
clock.tick(27)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and collector.x > -180 -collector.width -collector.vel:
collector.x -= collector.vel
collector.left = True
collector.right = False
elif keys[pygame.K_RIGHT] and collector.x < 550 - collector.width - collector.vel:
collector.x += collector.vel
collector.right = True
collector.left = False
else:
collector.right = False
collector.left = False
collector.walkCount = 0
redrawGameWindow()
pygame.quit()````
[1]: https://i.stack.imgur.com/FYFkB.png
It seems like you copy/pasted your code together, so let's drop it and start from scratch. Feel free to look some stuff up the documentation as we go along.
First, we need a mainloop to keep our game running. I usually start with something like this:
import pygame
RESOLUTION = 800, 600
FPS = 60
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill('black')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
This will create a window and manage our sprites that we will create later on and add to the sprites container. Not much to see here currently:
Now, we want a player that can move left and right. Here's how I would do it, making use of some pygame features, like the Rect and Sprite class. There's no magic involved. A Sprite is basically just a class that has an image attribute (you already guessed that this is, well, the image to use), and a rect attribute, with contains the position of where we want to draw the image.
Here's the Player class:
TILESIZE = 32
PLAYER_SPEED = 600
class Player(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill('dodgerblue')
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]: d -= 1
if pressed[pygame.K_d]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
and now let's add a Player to our game:
...
sprites = pygame.sprite.Group()
Player((300, 500), sprites)
while True:
...
Note that you don't have to draw/blit anything manually. Give our class an image and rect attribute, subclass Sprite, add the instance to a Group, and call draw on this group.
Now for the falling coins and other stuff. We should create a class for them, something like this:
from random import choice
FALLING_SPEED = 400
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(choice(['red', 'yellow', 'green']))
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
In your final game, you can easily replace the mono coloured image with an image you loaded from a file. Here, I just randomly choose red, yellow or green.
As you have already guessed, all the behaviour of our game objects is inside the update method of the sprite classes. The FallingStuff just moves its rect downward and kills itself when it goes out of screen. 'killing' as sprite just means it removes itself from all of its Group-containers. Another reason to use these instead of plain lists.
Of course, we want to generate multiple falling objects. There are dozen ways to do this but let's use pygame's time module to create an event because why not:
from random import randint
...
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), sprites)
screen.fill('black')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
The only thing left is to check if we could catch one of the falling objects.
Full code below:
import pygame
import pygame.freetype
from random import choice, randint
RESOLUTION = 800, 600
FPS = 60
TILESIZE = 32
PLAYER_SPEED = 600
FALLING_SPEED = 400
class Player(pygame.sprite.Sprite):
def __init__(self, pos, falling_stuff, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill('dodgerblue')
self.rect = self.image.get_rect(topleft=pos)
self.falling_stuff = falling_stuff
self.score = 0
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]: d -= 1
if pressed[pygame.K_d]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
for stuff in pygame.sprite.spritecollide(self, self.falling_stuff, True):
self.score += 1
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(choice(['red', 'yellow', 'green']))
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
falling_stuff = pygame.sprite.Group()
player = Player((300, 500), falling_stuff, sprites)
font = pygame.freetype.SysFont('Arial', 54)
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), falling_stuff, sprites)
screen.fill('black')
font.render_to(screen, (20, 20), f'Score: {player.score}', 'white')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
And here's the same with animations and a hitbox:
import pygame
import pygame.freetype
import os
from random import choice, randint
from itertools import cycle
RESOLUTION = 800, 600
FPS = 60
TILESIZE = 32
PLAYER_SPEED = 300
FALLING_SPEED = 400
ANIM_THRESHOLD = 0.05
def load_images(dir):
path = os.path.dirname(os.path.realpath(__file__))
path = os.path.join(path, dir)
for f in os.listdir(path):
yield pygame.image.load(os.path.join(path, f)).convert_alpha()
class Player(pygame.sprite.Sprite):
def __init__(self, pos, falling_stuff, *grps):
super().__init__(*grps)
idle_images = list(load_images('1-Idle'))
run_images = list(load_images('2-Run'))
self.images = {
'IDLE': {
'RIGHT': cycle(idle_images),
'LEFT': cycle(pygame.transform.flip(s, True, False) for s in idle_images)
},
'RUN': {
'RIGHT': cycle(run_images),
'LEFT': cycle(pygame.transform.flip(s, True, False) for s in run_images)
}
}
self.state = 'IDLE'
self.direction = 'RIGHT'
self.image = next(self.images['IDLE']['RIGHT'])
self.animation_counter = 0
self.rect = self.image.get_rect(topleft=pos)
self.falling_stuff = falling_stuff
self.score = 0
self.hitbox = pygame.Rect(0, 0, 30, 20)
self.hitbox.center = self.rect.center
self.hitbox.move_ip(-10, -10)
def update_image(self, dt, new_state):
self.animation_counter += dt
if self.animation_counter > ANIM_THRESHOLD or self.state != new_state:
self.image = next(self.images[new_state][self.direction])
self.animation_counter = 0
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_a]: d -= 1
if pressed[pygame.K_d]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
if d == 1:
new_state = 'RUN'
self.direction = 'RIGHT'
if d == -1:
new_state = 'RUN'
self.direction = 'LEFT'
if d == 0:
new_state = 'IDLE'
for stuff in self.falling_stuff:
if self.hitbox.colliderect(stuff.rect):
stuff.kill()
self.score += 1
self.update_image(dt, new_state)
self.state = new_state
self.hitbox.center = self.rect.center
self.hitbox.move_ip(10 if self.direction == 'LEFT' else -10, -10)
pygame.draw.rect(pygame.display.get_surface(), 'red', self.hitbox, 2)
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(choice(['red', 'yellow', 'green']))
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
falling_stuff = pygame.sprite.Group()
player = Player((300, 500), falling_stuff, sprites)
font = pygame.freetype.SysFont('Arial', 54)
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), falling_stuff, sprites)
screen.fill('black')
font.render_to(screen, (20, 20), f'Score: {player.score}', 'white')
sprites.draw(screen)
sprites.update(dt, events)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()
Sprites by Pixel Frog
First, you should consider to use "ae, oe, ue" or just the English translations instead of "ä, ö, ü", this can cause a few problems. Also, why don't you have included pygame?
Second of all, try not to use fixed values for window size, if you want to change it later on, you have to change a few values and easily run into problems when you forget one. I would recommend using a variable for width and height you can change later on.
But to your specific problem:
I think the reason you don't see any objects falling is that there are none. The part of your code that actually runs is in the while-loop and as long as I didn't miss anything, neither is there anything about your dropping objects, nor about them falling (any kind of acceleration or velocity).
Also I would highly recommend (as the comment of "oskros" already said) to test your code before asking. This is also an important part of programming and can be done like this for example:
You go in every function and write a "print" statement there, so you know that this function has run (by the output on the console). If one doesn't run, try to look at the point where it was supposed to be run and go on fixing from this point.
You can apply this technique to basically any debugging, stackoverflow is mostly for asking more specific questions and not "why does this not work, can you fix my code?" (at least for a whole program)
import pygame.freetype
from random import choice, randint
pygame.init()
RESOLUTION = 900, 780
FPS = 60
TILESIZE = 32
PLAYER_SPEED = 600
FALLING_SPEED = 400
coin = pygame.image.load('coin.png')
coin = pygame.transform.scale(coin, (60, 60))
sack = pygame.image.load('sack.png')
sack = pygame.transform.scale(sack, (60, 60))
money = pygame.image.load('schein.png')
money = pygame.transform.scale(money, (60, 60))
char = pygame.image.load ("L1.png")
walkRight = [pygame.image.load('Assets/R1.png'), pygame.image.load('Assets/R2.png'), pygame.image.load('Assets/R3.png')]
bg = pygame.image.load('City.jpg')
bg = pygame.transform.scale(bg, (900,780))
pygame.mixer.music.load('Music/Titel.mp3')
pygame.mixer.music.play(0)
class Player(pygame.sprite.Sprite):
def __init__(self, pos, falling_stuff, *grps):
super().__init__(*grps)
self.image = char
self.rect = self.image.get_rect(topleft=pos)
self.falling_stuff = falling_stuff
self.score = 0
def update(self, dt, events):
d = 0
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT]: d -= 1
if pressed[pygame.K_RIGHT]: d += 1
self.rect.move_ip(d * dt * PLAYER_SPEED, 0)
display_rect = pygame.display.get_surface().get_rect()
self.rect.clamp_ip(display_rect)
for stuff in pygame.sprite.spritecollide(self, self.falling_stuff, True):
self.score += 1
class FallingStuff(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = choice([coin,sack,money])
self.rect = self.image.get_rect(topleft=pos)
def update(self, dt, events):
self.rect.move_ip(0, FALLING_SPEED * dt)
display_rect = pygame.display.get_surface().get_rect()
if self.rect.top > display_rect.bottom:
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode(RESOLUTION)
dt, clock = 0, pygame.time.Clock()
sprites = pygame.sprite.Group()
falling_stuff = pygame.sprite.Group()
player = Player((400, 700), falling_stuff, sprites)
font = pygame.freetype.SysFont('ComicSans', 70)
CREATE_STUFF = pygame.USEREVENT + 1
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
if e.type == CREATE_STUFF:
pygame.time.set_timer(CREATE_STUFF, randint(1000, 2000), True)
FallingStuff((randint(50, 550), -TILESIZE), falling_stuff, sprites)
screen.blit(bg, (0,0))
font.render_to(screen, (30, 30), f'Score: {player.score}', 'white')
sprites.update(dt, events)
sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(FPS) / 1000
if __name__ == "__main__":
main()

My pygame player sprite is not moving and/or updating

I am trying to build a simple "flappy bird" like game. I am trying to sort all the code into classes and methods. How do I fix this problem? Is it the code not working because of calling some method too early or is it because there's something missing? I would really love it if someone would try to explain to me.
sprites.py:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = (700 / 2)
self.movex = 0
self.movey = 0
def control(self, x, y):
self.movex += x
self.movey += y
def update(self):
self.rect.x += self.movex
self.rect.y += self.movey
def animate(self):
pass
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.width = width
self.height = height
main.py:
from sprites import *
import pygame
WIDTH = 700
HEIGHT = 700
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.score = 0
self.running = True
def new(self):
pass
def events(self):
self.game_on = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.game_on = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.type == pygame.K_UP:
self.croc.control(0, -20)
def update(self):
self.croc = Player()
self.all_sprites = pygame.sprite.Group()
self.all_sprites.add(self.croc)
self.all_sprites.update()
def draw(self):
self.screen.fill((0, 0, 0))
self.all_sprites.draw(self.screen)
pygame.display.flip()
game = Game()
while game.running:
game.clock.tick(60)
game.new()
game.events()
game.update()
game.draw()
Thank you
There are 2 mistakes. The Player object is recreated in every frame. Create the player in the constructor of Game rather than in the method update:
class Game:
def __init__(self):
# [...]
self.croc = Player() # <--- ADD
self.all_sprites = pygame.sprite.Group() # <--- ADD
self.all_sprites.add(self.croc) # <--- ADD
def update(self):
# self.croc = Player() # <--- DELETE
# self.all_sprites = pygame.sprite.Group() # <--- DELETE
# self.all_sprites.add(self.croc) # <--- DELETE
self.all_sprites.update()
There is a type in the event loop. You've to get the key from the .key attribute rather than the .type attriburte:
if event.type == pygame.K_UP:
if event.key == pygame.K_UP:
Complete code:
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image.fill((255, 255, 0))
self.rect = self.image.get_rect()
self.rect.x = 0
self.rect.y = (700 / 2)
self.movex = 0
self.movey = 0
def control(self, x, y):
self.movex += x
self.movey += y
def update(self):
self.rect.x += self.movex
self.rect.y += self.movey
def animate(self):
pass
class Obstacle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.width = width
self.height = height
WIDTH = 700
HEIGHT = 700
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
self.clock = pygame.time.Clock()
self.score = 0
self.running = True
self.croc = Player()
self.all_sprites = pygame.sprite.Group()
self.all_sprites.add(self.croc)
def new(self):
pass
def events(self):
self.game_on = True
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.game_on = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.croc.control(0, -20)
def update(self):
self.all_sprites.update()
def draw(self):
self.screen.fill((0, 0, 0))
self.all_sprites.draw(self.screen)
pygame.display.flip()
game = Game()
while game.running:
game.clock.tick(60)
game.new()
game.events()
game.update()
game.draw()

A surface shown unexpectedly with pygame

I'm in the process of making a snake game, it mostly went well so far, but it displayed a block of the snake at the top left that I can't get rid of.
I checked that I didn't draw the surface there(0,0). I'm stuck. Please help me out, thanks!!
BTW it's my first time asking a question so any suggestion on that is also appreciated.
Edit: I found that using a regular class instead of sprite solved the problem, but I need the collide and other functions in sprite.
import pygame
class snake(pygame.sprite.Sprite):
speed=5
init_length=10
direction=0
x=[]
y=[]
updateCountMax = 2
updateCount = 0
length=10
# image=pygame.Surface((11,11)).convert().fill((0,128,255))
def __init__(self,init_x,init_y,image,screen):
pygame.sprite.Sprite.__init__(self)
for i in range(0,self.init_length):
self.x.append(init_x)
self.y.append(init_y)
# for i in range(0,self.length):
# print(f"{self.x[i]},{self.y[i]}")
for x in self.x:
print(x)
for y in self.y:
print(y)
self.image=image
self.screen=screen
self.rect=self.image.get_rect()
# self.rect.center=(self.x,self.y)
def move_R(self):
# self.x+=self.speed
self.direction=0
def move_L(self):
# self.x-=self.speed
self.direction=1
def move_U(self):
# self.y-=self.speed
self.direction=2
def move_D(self):
# self.y+=self.speed
self.direction=3
def update(self):
# self.updateCount = self.updateCount + 1
# if self.updateCount < self.updateCountMax:
for i in range(self.length-1,0,-1):
# print("self.x[" + str(i) + "] = self.x[" + str(i-1) + "]")
self.x[i] = self.x[i-1]
self.y[i] = self.y[i-1]
if(self.direction==0):
self.x[0]+=self.speed
elif(self.direction==1):
self.x[0]-=self.speed
elif(self.direction==2):
self.y[0]-=self.speed
elif(self.direction==3):
self.y[0]+=self.speed
# self.rect.center=(self.x,self.y)
# self.updateCount = 0
# for i in range(0,self.length):
# print(f"{self.x[i]},{self.y[i]}")
self.draw()
def draw(self):
for i in range(0,self.length):
self.screen.blit(self.image,(self.x[i],self.y[i]))
# print(f"rendered at {self.x[i]},{self.y[i]}")
# self.rect.center=(self.x[i],self.y[i])
class app:
width=1200
height=900
title="Snake"
done=False
def __init__(self):
pygame.init()
self.image=pygame.Surface((11,11))
self.image.fill((0,128,255))
pygame.display.set_caption(self.title)
self.screen = pygame.display.set_mode((self.width, self.height))
self.screen.fill((0,0,0))
self.clock=pygame.time.Clock()
self.snakes=pygame.sprite.Group()
self.player1=snake(500,10,self.image,self.screen)
self.snakes.add(self.player1)
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill((255,255,255))
def loop(self):
while(not self.done):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
pygame.event.pump()
keys = pygame.key.get_pressed()
if (keys[pygame.K_RIGHT]):
self.player1.move_R()
if (keys[pygame.K_LEFT]):
self.player1.move_L()
if (keys[pygame.K_UP]):
self.player1.move_U()
if (keys[pygame.K_DOWN]):
self.player1.move_D()
if (keys[pygame.K_ESCAPE]):
self.done = True
self.screen.blit(self.background,(0,0))
self.screen.fill((0,0,0))
self.player1.update()
self.snakes.draw(self.screen)
pygame.display.update()
self.clock.tick(60)
pygame.quit()
if __name__ == "__main__" :
theApp = app()
theApp.loop()
You add player1 to the snakes sprite group and draw that with self.snakes.draw(self.screen). However, you also draw the player in self.player1.update(), in the last line.
Remove self.snakes.draw(self.screen) to get rid of the phantom snake.
BTW: you create and set a self.background but you immediately overwrite it with self.screen.fill((0,0,0)), so you don't need a background at all.
What you see in the top left corner is the self.image of the player1 sprite. The draw method of sprite groups blits the images at the rect.topleft coordinates of the sprites and since you never move the player1.rect, the image will be blitted at the default (0, 0) coordinates. So just remove the line self.snakes.draw(self.screen) to fix this.
I also suggest that you use pygame.Rects instead of the self.x and self.y lists. You can create rect instances with the init_x and init_y coords as the topleft attribute and put them into a self.rects list. That allows you to simplify the update and draw methods, and the rects can also be used for the collision detection. I've already refactored your code (it kind of became a mini code review):
import pygame
class Snake(pygame.sprite.Sprite): # Use upper camelcase names for classes (see PEP 8).
def __init__(self, init_x, init_y, image,screen):
pygame.sprite.Sprite.__init__(self)
# These are instance attributes now (use class attributes if
# the values should be shared between the instances).
self.speed = 5
self.init_length = 10
self.direction = 0
self.updateCountMax = 2
self.updateCount = 0
self.length = 10
# The body parts are rects now.
self.rects = []
for i in range(self.init_length):
# Append pygame.Rect instances.
self.rects.append(pygame.Rect(init_x, init_y, 11, 11))
self.image = image
self.screen = screen
self.rect = self.rects[0] # I use the first rect as the self.rect.
def update(self):
for i in range(self.length-1, 0, -1):
# Update the topleft (x, y) positions of the rects.
self.rects[i].topleft = self.rects[i-1].topleft
if self.direction == 0:
self.rects[0].x += self.speed
elif self.direction == 1:
self.rects[0].x -= self.speed
elif self.direction == 2:
self.rects[0].y -= self.speed
elif self.direction == 3:
self.rects[0].y += self.speed
def draw(self):
# Iterate over the rects to blit them (I draw the outlines as well).
for rect in self.rects:
self.screen.blit(self.image, rect)
pygame.draw.rect(self.screen, (0, 255, 0), rect, 1)
class App:
width = 1200
height = 900
title = "Snake"
done = False
def __init__(self):
pygame.init()
self.image = pygame.Surface((11, 11))
self.image.fill((0, 128, 255))
pygame.display.set_caption(self.title)
self.screen = pygame.display.set_mode((self.width, self.height))
self.clock = pygame.time.Clock()
self.snakes = pygame.sprite.Group()
self.player1 = Snake(500, 10, self.image, self.screen)
self.snakes.add(self.player1)
def loop(self):
while not self.done:
# Handle the events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.done = True
keys = pygame.key.get_pressed()
# In Python we simply set the values of the
# attributes directly instead of using getter
# and setter methods.
if keys[pygame.K_RIGHT]:
self.player1.direction = 0
if keys[pygame.K_LEFT]:
self.player1.direction = 1
if keys[pygame.K_UP]:
self.player1.direction = 2
if keys[pygame.K_DOWN]:
self.player1.direction = 3
if keys[pygame.K_ESCAPE]:
self.done = True
# Update the game.
self.player1.update()
# Draw everything.
self.screen.fill((0, 0, 0))
self.player1.draw()
pygame.draw.rect(self.screen, (255, 0, 0), self.player1.rect, 1)
pygame.display.update()
self.clock.tick(60)
pygame.quit()
if __name__ == "__main__" :
the_app = App()
the_app.loop()

Categories