I have tried searching for many solutions to do this but I cant seem to understand how to spawn the enemy block multiple times. In my program , the enemy block will only appear once. The game is incomplete, so please ignore empty functions.
I have created the enemy object using sprites. I want to create the program in such a way that , once the enemy object leaves the screen , another object(same size and colour) will appear.
import pygame as pg
from settings import *
from sprites import *
from os import path
class Game(object):
"""docstring for Game"""
def __init__(self):
# initialise game
global points , game_speed
pg.init()
self.screen = pg.display.set_mode((s_HEIGHT , s_WIDTH))
pg.display.set_caption(TITLE)
self.icon = pg.image.load("C:/Users/DELL/Documents/Jump Cube/Img/cube.png")
pg.display.set_icon(self.icon)
self.clock = pg.time.Clock()
self.running = True
self.game_speed = 14
points = 0
font = pygame.font.Font('freesansbold.ttf', 20)
self.load_data()
def load_data(self):
self.dir = path.dirname(__file__)
img_dir = path.join(self.dir, "Img")
#load spritesheet image
self.spritesheet = Spritesheet(path.join(img_dir, SPRITESHEET))
def new(self):
# Stars a new game
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.ene = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
pl = Platform(0, s_HEIGHT - 230, 800, 40)
en = enemies(50, 50)
self.all_sprites.add(pl)
self.platforms.add(pl)
self.all_sprites.add(en)
self.ene.add(en)
self.Run()
def Run(self):
# Game Loop
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.Events()
self.Update()
self.Draw()
self.score()
def Update(self):
# Game Loop - Update
self.all_sprites.update()
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
pass
def score(self):
global points , game_speed
points += 1
if points % 100 == 0:
game_speed += 1
text = font.render("Points : " + str(points), True, WHITE)
textrec = text.get_rect()
textrec.center = (700, 50)
self.screen.blit(text, textrec)
def Events(self):
# Game Loop - Events
for event in pg.event.get():
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump()
def Draw(self):
# Game Loop - Draw
self.screen.fill(BLACK)
self.score()
self.all_sprites.draw(self.screen)
pg.display.update()
pass
def start_screen(self):
# shows the start screen
pass
def end_screen(self):
# shows the end screen
pass
g = Game()
g.start_screen()
while g.running:
g.new()
g.end_screen()
pg.quit()
sprites.py
import pygame as pg
from settings import *
vec = pg.math.Vector2
class Spritesheet():
# utility class for laoding and parsing spritesheets
def __init__(self, filename):
self.spritesheet = pg.image.load(filename).convert()
def get_image(self, x, y, width, height):
# grabs images from large spritesheets
image = pg.Surface((width, height))
image.blit(self.spritesheet, (0,0), (x, y, width, height))
image = pg.transform.scale(image , (50, 85))
return image
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.image = self.game.spritesheet.get_image(614, 1063, 120, 191)
self.image.set_colorkey(BLACK1)
self.rect = self.image.get_rect()
self.rect.center = (s_WIDTH / 2, s_HEIGHT / 2)
self.pos = vec(100, 600)
self.vel = (0, 0)
self.acc = (0, 0)
def jump(self):
#jump only if standing on plat
self.rect.y += 1
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 1
if hits:
self.vel.y = -8
def update(self):
self.acc = vec(0, PLAYER_GRAV)
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w, h))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class enemies(pg.sprite.Sprite):
def __init__(self, w1, h1):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w1, h1))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = 700
self.rect.y = 517
def update(self):
self.rect.x -= game_speed
settings.py
import pygame
pygame.init()
#game options
TITLE = "Ninja Jump"
s_WIDTH = 600
s_HEIGHT = 800
FPS = 60
game_speed = 14
points = 0
font = pygame.font.Font('freesansbold.ttf', 20)
SPRITESHEET = "spritesheet_jumper.png"
#player properties
PLAYER_ACC = 0.5
PLAYER_GRAV = 0.3
#colors
WHITE = (199, 198, 196)
BLACK = (23, 23, 23)
GRAY = (121, 121, 120)
GREEN = (72, 161, 77)
BLACK1 = (0,0,0)
GRAY1 = (162, 162, 162)
First of enemies should be name Enemy. The class represents 1 single enemy. The coordinates of the enemy are arguments of the constructor. The en pygame.sprite.Group object is used to manage multiple enemies:
class Enemies(pg.sprite.Sprite):
def __init__(self, x, y, w1, h1):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w1, h1))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def update(self):
self.rect.x -= game_speed
class Game(object):
# [...]
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.ene = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
pl = Platform(0, s_HEIGHT - 230, 800, 40)
en1 = Enemy(700, 517, 50, 50)
en2 = Enemy(600, 517, 50, 50)
en3 = Enemy(500, 517, 50, 50)
self.all_sprites.add(pl)
self.platforms.add(pl)
self.all_sprites.add([en1, en2, en3])
self.ene.add([en1, en2, en3])
self.Run()
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()
sprites.py:
import pygame as pg
from settings import *
vec = pg.math.Vector2
# for movement
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.image = pg.Surface((30, 40))
self.image.fill((255, 255, 153))
self.rect = self.image.get_rect()
self.rect.center = WIDTH / 2, HEIGHT / 2
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
def jump(self):
self.rect.x += 1
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.x -= 1
if hits:
self.vel.y = -20
def update(self):
self.acc = vec(0, PLAYER_GRAV)
# pouze zryhlení
keys = pg.key.get_pressed()
if keys[pg.K_LEFT]:
self.acc.x = - PLAYER_ACC
if keys[pg.K_RIGHT]:
self.acc.x = PLAYER_ACC
# apply friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# pohyb tělesa
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
if self.pos.x > WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w, h))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
main.py:
import random
import pygame as pg
from settings import *
from sprites import *
class Game:
def __init__(self):
# initialize the game window
self.running = True
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption('My game')
self.clock = pg.time.Clock()
def new(self):
# to start a new game, reset the pieces
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
p1 = Platform(0, HEIGHT - 40, WIDTH, 40)
p2 = Platform(WIDTH / 2 - 50, HEIGHT * 3 / 4, 100, 20)
self.platforms.add(p2)
self.platforms.add(p1)
self.all_sprites.add(p1)
self.all_sprites.add(p2)
self.run()
def run(self):
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
self.all_sprites.update()
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.rect.midbottom = self.player.pos
self.player.vel.y = 0
def events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump()
def draw(self):
self.screen.fill(BLACK)
self.all_sprites.draw(self.screen)
pg.display.flip()
def show_start_screen(self):
pass
def show_go_screen(self):
pass
game = Game()
game.show_start_screen()
while game.running:
game.new()
game.show_go_screen()
pg.quit()
settings.py:
# game settings
WIDTH = 480
HEIGHT = 600
FPS = 60
# player settings
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = ((255, 255, 153))
I have a few questions:
Question n. 1:
Why doesn't the jump method work? And why is it using velocity -20 to jump?
Question n. 2:
I'm not quite sure if I understand the vector coordinates, can someone please try to explain it to me?
I think that this is exactly why I have problems understanding the code.
In pygame, the screen coordinates start at the top left (0,0). Positive Y goes down. Jump is -20 because the player is going up (toward zero). It looks like the player bounces up when it hits an object.
Concerning the jump issue, the game does not start for (Platform not defined) so I can't help there.
This question already has answers here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
I have been trying to create a 2D platformer in pygame, and I have encountered a problem with the collision of the player sprite with the platform. I hope to implement full collision for the platforms, so that the player sprite will stop when it hits the platform from any direction. Currently, the sprite stops when it hits the top of the platforms, but if it collides with the bottom or sides of a platform it instantly jumps to the top of the platform. I have tried various things to solve this, but to no avail. Any help would be appreciated.
I have seperated my code into 3 files.
This is my main file:
import pygame
from settings import *
from sprites import *
#Game Class
class Game:
def __init__(self):
pygame.init()
pygame.mixer.init()
self.gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
self.clock = pygame.time.Clock()
self.gameRunning = True
#Stars the game
def new(self):
self.allSprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.player = Player(self)
self.allSprites.add(self.player)
floor = Platform(0, 680, displayWidth, 40)
plat2 = Platform( 500, 400, 100, 40)
self.allSprites.add(floor, plat2)
self.platforms.add(floor, plat2)
self.run()
#Game Loop
def run(self):
self.gameRunning = True
while self.gameRunning == True:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
#Updates the screen
def update(self):
self.allSprites.update()
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.spd.y = 0
self.player.rect.midbottom = self.player.pos
if self.player.rect.left >= displayHeight - 200:
self.player.pos.x -= abs(self.player.spd.x)
for plat in self.platforms:
plat.rect.x -= abs(self.player.spd.x)
if self.player.rect.right <= displayHeight / 4:
self.player.pos.x += abs(self.player.spd.x)
for plat in self.platforms:
plat.rect.x += abs(self.player.spd.x)
#Events loop
def events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.player.jump()
#Draws things to the screen
def draw(self):
self.gameDisplay.fill(LIGHT_BLUE)
self.allSprites.draw(self.gameDisplay)
pygame.display.update()
def runGame():
game = Game()
game.new()
runGame()
Sprites file:
import pygame
from settings import *
vec = pygame.math.Vector2
#Player Class
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.image = pygame.Surface([40, 40])
self.image.fill(DARK_BLUE)
self.rect = self.image.get_rect()
self.rect.center = (displayWidth / 2, displayHeight / 2)
self.pos = vec(displayWidth / 2, displayHeight / 2)
self.spd = vec(0,0)
self.acc = vec(0,0)
def update(self):
self.acc = vec(0, playerGrav)
keysPressed = pygame.key.get_pressed()
if keysPressed[pygame.K_LEFT]:
self.acc.x = - playerAcc
if keysPressed[pygame.K_RIGHT]:
self.acc.x = playerAcc
self.acc.x += self.spd.x * playerFric
self.spd += self.acc
self.pos += self.spd + 0.5 * self.acc
self.rect.midbottom = self.pos
def jump(self):
self.rect.y +=1
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 1
if hits:
self.spd.y = -20
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((w, h))
self.image.fill((GREEN))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
Settings File:
import pygame
pygame.font.init()
#Setup
FPS = 60
displayWidth = 1280
displayHeight = 720
pygame.display.set_caption("2D Platformer")
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
clock = pygame.time.Clock()
#Player
playerAcc = 0.5
playerFric = -0.1
playerGrav = 0.5
#Colour Pallette
BLACK = (0, 0, 0 )
WHITE = (255, 255, 255)
RED = (200, 0, 0 )
GREEN = (0, 200, 0 )
BLUE = (0, 0, 200)
LIGHT_BLUE = (0, 191, 255)
DARK_BLUE = (0, 50, 150)
#Score
score = 0
This is the code that is responsible for collision:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.spd.y = 0
self.player.rect.midbottom = self.player.pos
I understand that the reason why the sprite jumps to the top of the platform is due to these lines of code:
if hits:
self.player.pos.y = hits[0].rect.top
However, I don't know how to implement full collision.