Pygame 2D Platformer Full Platform Collision [duplicate] - python

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.

Related

How to spawn enemy blocks continuously without manually creating them? [duplicate]

This question already has answers here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Issues with pygame.time.get_ticks() [duplicate]
(1 answer)
How to make instances spawn automatically around the player? [duplicate]
(1 answer)
Closed 1 year ago.
I am creating a game similar to the "Internet T-rex Game", Right now my game creates enemy blocks according to how many I manually create. I want it to be created by itself. My game is not fully completed yet, so please ignore the empty functions.
Heres my main.py
import pygame as pg
import random
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)
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()
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()
if event.type == pg.KEYDOWN:
if event.key == pg.K_RIGHT:
self.player.mover()
elif event.key == pg.K_LEFT:
self.player.movel()
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 mover(self):
#move right
self.vel.x = 5
def movel(self):
#move right
self.vel.x = -5
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 Enemy(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
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)
Thanks for any help and let me know if the problem needs more clarification.
Spawn the enemies by a time interval. In pygame the system time can be obtained by calling pygame.time.get_ticks(), which returns the number of milliseconds since pygame.init() was called. Define a time interval to spawn enemies. Continuously compare the current time with the time when the next enemy must appear. When the time comes, spawn an enemy and set the time the next enemy must spawn:
class Game(object):
# [...]
def Run(self):
self.next_enemy_time = 0
self.enemy_time_interval = 1000 # 1000 milliseconds == 1 second
# Game Loop
self.playing = True
while self.playing:
current_time = pygame.time.get_ticks()
if current_time > self.next_enemy_time:
self.next_enemy_time += self.enemy_time_interval
new_enemy = Enemy(700, 517, 50, 50)
self.all_sprites.add(new_enemy)
self.ene.add(new_enemy)
self.clock.tick(FPS)
self.Events()
self.Update()
self.Draw()
self.score()

How can I get my collisions to work with the sides of a platform in my platform?

I am currently creating a platform game using python pygame but have gotten stuck with the collisions. I have gotten the bottom and top collisions to work with my character sprite but as of now my character won't stop or bounce off the sides of a platform. when doing collisions, I am using the method sprite.spritecollide() which I would like to do in the same way if anyone can help. I have done the collision check correctly but my code for handling the collision I cannot seem to get it done correctly. My code where I do the collision detection is as follows in the main.py in the game update function:
import pygame
import random
from settings import *
from sprites import *
from camera import *
from os import path
class Game:
def __init__(self):
pygame.init() # initialises pygame
pygame.mixer.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) # sets the width and height of the pygame window
pygame.display.set_caption(TITLE)
self.clock = pygame.time.Clock()
self.running = True
self.font_name = pygame.font.match_font(FONT_NAME)
self.load_data()
def load_data(self):
pass
def new(self):
self.all_sprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.camera = Camera(WIDTH, HEIGHT) # creates the camera with WIDTH and HEIGHT of the screen
self.run()
def run(self): # Game Loop - runs the game
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self): # Game loop - update
self.all_sprites.update()
# collision with top of platform
if self.player.vel.y > 0:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False) # returns a list of platform sprites that hit the player
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
# collision with the bottom of a platform
if self.player.vel.y < 0:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.top = hits[0].rect.bottom
self.player.vel.y = -self.player.vel.y
# collision with the right side of a platform (moving left), here is the code for the right side of the platform
if self.player.acc.x < 0:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.left = hits[0].rect.right
self.player.acc.x = 0
# screen moves with player
self.camera.update(self.player) # is the camera that tracks players movement
def events(self): # Game loop - events
for event in pygame.event.get():
if event.type == pygame.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.player.jump()
def draw(self): # Game loop - draw
self.screen.fill(RED)
#self.all_sprites.draw(self.screen)
for sprite in self.all_sprites:
self.screen.blit(sprite.image, self.camera.apply(sprite)) # loops through the all_sprites group and blit's each sprite onto the screen
pygame.display.flip()
def start_screen(self):
pass
def game_over_screen(self):
pass
def wait_for_key(self):
pass
def draw_text(self,text, size, colour, x, y):
pass
g = Game()
g.start_screen()
while g.running:
g.new()
g.game_over_screen()
pygame.quit()
I have so far only tried doing collisions for the right side of the platforms, once I've done one side I can replicate for the other side.
P.S. if you need more of my code I will add it to the question if asked to.
EDIT
sprites.py
# will hold the sprite classes
import pygame
from settings import *
import random
vec = pygame.math.Vector2
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.image = pygame.Surface((30, 40))
self.image.fill(BLUE)
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):
# jump only if on a platform
self.rect.x += 1
hits = pygame.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)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pygame.K_RIGHT]:
self.acc.x = PLAYER_ACC
# apply friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# equations of motion
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# stop from running of the left side of the screen
if self.pos.x < 0:
self.pos.x = 0
self.rect.midbottom = self.pos
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((width, height))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
camera.py
import pygame
from settings import *
# A camera that keeps track of an offset that will be, how far we want to draw the screen which will include all objects on the screen. We are just shifting the drawing of our screen according to the offset. Camera needs to do two things, apply the offset and then update the movement of where the player is on the screen.
class Camera:
def __init__(self, width, height): # we will need to tell the camera how wide and high we want it to be
self.camera = pygame.Rect(0, 0, width, height) # is the rectangle we set to keep track of the screen/be the camera
self.width = width
self.height = height
def apply(self, entity): # method to apply the offset to the screen, by shifting the screen according to the movement of the entity within the camera screen
return entity.rect.move(self.camera.topleft)
def update(self, target): # method to update where the player/target has moved to, updates are done according to last known position of the target
# as the target moves the camera moves in the opposite direction of the target and stays within the center of the screen
x = -target.rect.x + int(WIDTH/2) # left to right
y = -target.rect.y + int(HEIGHT/2) # up and down
# limit scrolling to map size, keeps the 'camera' from going over the edges
x = min(0, x) # left
y = min(0, y) # top
y = max(-(self.height - HEIGHT), y) # bottom
self.camera = pygame.Rect(x, y, self.width, self.height) # adjusts the camera's rectangle with the new x and y
settings.py
# Game options/settings
TITLE = 'Platformer'
WIDTH = 900
HEIGHT = 500
FPS = 60
FONT_NAME = 'arial'
HS_FILE = 'highscore.txt'
SPRITESHEET = 'spritesheet_jumper.png'
# Game colours
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
# Starting Platforms:
PLATFORM_LIST = [(0, HEIGHT - 50, WIDTH, 50), (WIDTH / 2, HEIGHT * 1 / 2, 200, 30), (WIDTH + 150, HEIGHT - 50, WIDTH, 50), (WIDTH / 2, HEIGHT * 4 / 5, 200, 30)]
# player properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
I can't test your code but usually problem is that spritecollide doesn't inform if you collide on x or y or both.
When you move x and y at once and check collision then you don't know if you collided on x or y or both. If you collided only on y and you will check vel.x and move player then you get wrong result. The same if you collided only on x and you will check vel.y and move player then you get also wrong result.
You have to do it separatelly:
first move only x, check collisions and check only vel.x,
next move only y, check collisions again and check only vel.y,
Something like this:
def update(self):
#self.all_sprites.update()
# collision with top and bottom of platform
# update only y
self.player.pos.y += self.player.vel.y + 0.5 * self.player.acc.y
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
if self.player.vel.y > 0:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
elif self.player.vel.y < 0:
self.player.top = hits[0].rect.bottom
self.player.vel.y = -self.player.vel.y
# collision with left and right of platform
# update only x
self.player.pos.x += self.player.vel.x + 0.5 * self.player.acc.x
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
if self.player.acc.x < 0:
self.player.left = hits[0].rect.right
self.player.acc.x = 0
elif self.player.acc.x > 0:
self.player.right = hits[0].rect.left
self.player.acc.x = 0
You should see working example in platform examples on page Program Arcade Games With Python And Pygame
EDIT:
Full code
main.py
#import random
#from os import path
import pygame
from settings import *
from sprites import *
from camera import *
class Game:
def __init__(self):
# initialises pygame
pygame.init()
#pygame.mixer.init() # `pygame.init()` should aready runs `pygame.mixer.init()`
# sets the width and height of the pygame window
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
self.font_name = pygame.font.match_font(FONT_NAME)
self.load_data()
# main loop elements
self.clock = pygame.time.Clock()
self.running = True
def load_data(self):
pass
def new(self):
"""Run game"""
self.reset()
self.run()
def reset(self):
"""Reset data"""
self.all_sprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
# creates the camera with WIDTH and HEIGHT of the screen
self.camera = Camera(WIDTH, HEIGHT)
def run(self):
"""Game Loop - runs the game"""
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
"""Game loop - update"""
self.all_sprites.update()
# screen moves with player
self.camera.update(self.player) # is the camera that tracks players movement
def events(self):
"""Game loop - events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pygame.KEYDOWN:
# reset game but not exit
if event.key == pygame.K_ESCAPE:
if self.playing:
self.playing = False
# send event(s) to sprite(s)
self.player.events(event)
def draw(self):
"""Game loop - draw"""
self.screen.fill(RED)
# loops through the all_sprites group and blit's each sprite onto the screen
for sprite in self.all_sprites:
sprite.draw(self.screen, self.camera)
pygame.display.flip()
def start_screen(self):
pass
def game_over_screen(self):
pass
def wait_for_key(self):
pass
def draw_text(self,text, size, colour, x, y):
pass
# --- main ---
g = Game()
g.start_screen()
while g.running:
g.new()
g.game_over_screen()
#g.exit_screen()
pygame.quit()
sprites.py
# will hold the sprite classes
import random
import pygame
from settings import *
vec = pygame.math.Vector2
class BaseSprite(pygame.sprite.Sprite):
"""Base class with functions for all sprites"""
def draw(self, screen, camera):
screen.blit(self.image, camera.apply(self))
class Player(BaseSprite):
def __init__(self, game):
#pygame.sprite.Sprite.__init__(self)
super().__init__()
self.game = game
self.image = pygame.Surface((30, 40))
self.image.fill(BLUE)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.rect = self.image.get_rect()
self.rect.center = self.pos
self.on_ground = True
def jump(self):
if self.on_ground:
self.vel.y = -20
self.on_ground = False
def events(self, event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.jump()
def update(self):
self.acc = vec(0, PLAYER_GRAV)
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.acc.x = -PLAYER_ACC
if keys[pygame.K_RIGHT]:
self.acc.x = PLAYER_ACC
# apply friction
self.acc.x += self.vel.x * PLAYER_FRICTION
# equations of motion
self.vel += self.acc
# --- horizontal collision ---
self.pos.x += self.vel.x + 0.5 * self.acc.x
self.rect.centerx = self.pos.x
# stop from running of the left side of the screen
if self.rect.left < 0:
self.rect.left = 0
self.pos.x = self.rect.centerx
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
if hits:
if self.vel.x > 0:
self.rect.right = hits[0].rect.left
self.pos.x = self.rect.centerx
self.vel.x = 0
elif self.vel.x < 0:
self.rect.left = hits[0].rect.right
self.pos.x = self.rect.centerx
self.vel.x = 0
# --- vertical collision ---
self.pos.y += self.vel.y + 0.5 * self.acc.y
self.rect.centery = self.pos.y
# game over when left screen
if self.rect.top > HEIGHT:
self.game.playing = False
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
if hits:
if self.vel.y > 0:
self.rect.bottom = hits[0].rect.top
self.pos.y = self.rect.centery
self.vel.y = 0
self.on_ground = True
elif self.vel.y < 0:
self.rect.top = hits[0].rect.bottom
self.pos.y = self.rect.centery
self.vel.y = 0
class Platform(BaseSprite):
def __init__(self, x, y, width, height, color):
#pygame.sprite.Sprite.__init__(self)
super().__init__()
self.image = pygame.Surface((width, height))
self.image.fill(color)
#self.rect = self.image.get_rect()
#self.rect.x = x
#self.rect.y = y
# shorter
self.rect = self.image.get_rect(x=x, y=y)
camera.py
without changes
settings.py
I added few platforms
# Game options/settings
TITLE = 'Platformer'
WIDTH = 900
HEIGHT = 500
FPS = 60
FONT_NAME = 'arial'
HS_FILE = 'highscore.txt'
SPRITESHEET = 'spritesheet_jumper.png'
# Game colours
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
# Starting Platforms:
PLATFORM_LIST = [
# grounds
(0, HEIGHT - 50, WIDTH, 50, GREEN),
(WIDTH + 150, HEIGHT - 50, WIDTH, 50, GREEN),
# platforms
(WIDTH / 2, HEIGHT * 1 / 2, 200, 30, YELLOW),
(WIDTH / 2, HEIGHT * 4 / 5, 200, 30, YELLOW),
# walls
(WIDTH - 30, HEIGHT - 250, 30, 200, WHITE),
(WIDTH + 150, HEIGHT - 250, 30, 200, WHITE),
]
# player properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.8
I was thinging to moves some code to class Screen and then I would use this class to create GameScreen, StartScreen, GameOverScreen, etc.

Jumping in pygame + understanding pygame math (vector)

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.

How do I turn my pygame into a side scroller?

I'm building a game with pygame and I have a problem with this game, I don't know how to make the background and the platform scroll slowly to the right as my player sprite moves to the right.
I want the scrolling to happen in def shift_world().
Maybe someone can teach me how to add an image to the background as well. It would be great if you can utilize the libraries I am using. I have three files: The first for the game, the second for sprites, and the third for the settings.
main.py:
# Sarada's Blaze! - side scrolling platform shooting game
import pygame as pg
import random
import os
from settings import *
from sprites import *
class Game:
def __init__(self):
# initialize game window, etc
pg.init()
pg.mixer.init()
self.screen = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption(TITLE)
self.clock = pg.time.Clock()
self.running = True
self.font_name = pg.font.match_font(FONT_NAME)
def new(self):
# start a new game
self.score = 0
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
for plat in PLATFORM_LIST:
p = Platform(*plat)
self.all_sprites.add(p)
self.platforms.add(p)
self.run()
def run(self):
# Game loop
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
def update(self):
# Game Loop - Update
self.all_sprites.update()
self.platforms.update()
# check if player hits a platform - only if falling
if self.player.vel.y 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
def events(self):
# Game Loop - events
for event in pg.event.get():
# check for closing window
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_UP:
self.player.jump()
def draw(self):
# Game Loop - draw
self.screen.fill(bgcolor) # I want to change the background color to an image
self.all_sprites.draw(self.screen)
self.draw_text(str(self.score), 22, white, WIDTH / 2, 15)
# after drawing everything, flip the display
pg.display.flip()
# How far the world has been scrolled right
# This is where i want the side scroller stuff to happen
def shift_world(self):
# when the user moves left/right, i want to scroll everything
self.world_shift += shift_x
# i want all my platforms and the background to scroll when
# my player sprite moves closer to the right
for plat in PLATFORM_LIST:
self.platform.rect.x += shift_x
if self.pos.x = 500:
diff = self.pos.x - 500
self.pos.x = 500
self.shift_world(- diff)
if self.pos.x <= 120:
diff = 120 - self.pos.x
self.pos.x -= 120
self.shift_world(diff)
def show_start_screen(self):
# game splash/start screen
self.screen.fill(greenblue)
self.draw_text(TITLE, 48, red, WIDTH / 2, HEIGHT / 4)
self.draw_text("left arrow to move left, right arrow to move right, up arrow to jump", 22, blue, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to begin", 22, green, WIDTH / 2, HEIGHT * 3 /4)
pg.display.flip()
self.wait_for_key()
def show_go_screen(self):
# game over/continue
if not self.running:
return
self.screen.fill(greenblue)
self.draw_text("Game Over!", 48, red, WIDTH / 2, HEIGHT / 4)
self.draw_text("Score: " + str(self.score), 22, blue, WIDTH / 2, HEIGHT / 2)
self.draw_text("Press any key to continue", 22, green, WIDTH / 2, HEIGHT * 3 /4)
pg.display.flip()
self.wait_for_key()
def wait_for_key(self):
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
waiting = False
self.running = False
if event.type == pg.KEYUP:
waiting = False
def draw_text(self, text, size, color, x, y):
font = pg.font.Font(self.font_name, size)
text_surface = font.render(text, True, color)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
self.screen.blit(text_surface, text_rect)
g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()
pg.quit()
sprites.py:
# Sprite classes for platform shooting game
import pygame as pg
import random
from settings import *
import os
vec = pg.math.Vector2
game_folder = os.path.dirname(__file__)
img_folder = os.path.join(game_folder, "img")
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.image = pg.image.load(os.path.join(img_folder, "sarada_shooting.png")).convert()
self.image.set_colorkey(black)
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):
# jump only if standing on a platform
self.rect.x += 1
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.x -= 1
if hits:
self.vel.y = -PLAYER_JUMP
def update(self):
self.acc = vec(0, PLAYER_GRAV)
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
# equations of motion
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# wrap around the sides of the screen
if self.pos.x WIDTH:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = WIDTH
self.rect.midbottom = self.pos
# i want the platform graphic changed
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((WIDTH, h))
self.image.fill(greenblue)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
settings.py:
# game options/settings
TITLE = "Sarada's Blaze"
WIDTH = 800
HEIGHT = 600
FPS = 60
FONT_NAME = 'times new roman'
# Player properties
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.20
PLAYER_GRAV = 0.8
PLAYER_JUMP = 20
# starting platforms
PLATFORM_LIST = [(0, HEIGHT - 40, WIDTH, 40)]
# define colors
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
purple = (255, 0, 255) # ENEMY COLOR
greenblue = (0, 155, 155)
bgcolor = red
You need to iterate over the self.platforms list and subtract the player's vel.x from the x-position of every sprite in order to move them. Also, give your platform sprites a vector attribute for the position self.pos = vec(x, y) because the coordinates of pygame.Rects are truncated and turned into ints, so the movement will be inaccurate if your velocity vectors consist of floats.
def update(self):
# ...
self.shift_world()
def shift_world(self):
# Scroll when the player sprite moves closer to the right.
if self.player.pos.x >= 500:
self.player.pos.x = 500 # Stop at 500.
self.shift_platforms()
# Scroll when the player sprite moves closer to the left.
if self.player.pos.x <= 120:
self.player.pos.x = 120 # Stop at 120.
self.shift_platforms()
def shift_platforms(self):
for plat in self.platforms: # Iterate over the platform sprites.
plat.pos.x -= self.player.vel.x # Update the platform's pos vector.
plat.rect.x = plat.pos.x # Update the rect.
self.world_shift -= self.player.vel.x # For the background.
As for the background surface, you can use the self.world_shift attribute and subtract the self.player.vel.x in the shift_platforms method as well. Then blit it twice in the draw method and use the modulo operator to blit it at the correct x-positions.
I assume the background has the size of the screen and should be repeated.
# Load it once in the global scope.
BACKGROUND = pg.image.load(os.path.join(img_folder, "background.png")).convert()
def draw(self):
x = self.world_shift
self.screen.blit(BACKGROUND, (x % WIDTH, 0))
self.screen.blit(BACKGROUND, (x % WIDTH - WIDTH - 1, 0))

python and pygame shooting

I am working on a game in pygame, so far the player can walk around, 50 blue blocks spawn randomly on the screen and the player can walk around and shoot them, but there is one problem the player can only shoot up, I want the player to shoot towards the mouse but am having some trouble getting it to do this.
this is my code
import pygame
from pygame import *
import random
black = ( 0, 0, 0)
white = ( 255, 255, 255)
red = ( 255, 0, 0)
blue = ( 0, 0, 255)
player_x, player_y = 0, 0
move_player_x, move_player_y = 0, 0
class Block(pygame.sprite.Sprite):
def __init__(self, color):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20, 15])
self.image.fill(color)
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([20,20])
self.image.fill(red)
self.rect = self.image.get_rect()
def update(self):
pos = pygame.mouse.get_pos()
self.rect.x = player_x
self.rect.y = player_y
class Bullet(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([4, 10])
self.image.fill(black)
self.rect = self.image.get_rect()
def update(self):
self.rect.y -= 5
pygame.init()
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width,screen_height])
all_sprites_list = pygame.sprite.Group()
block_list = pygame.sprite.Group()
bullet_list = pygame.sprite.Group()
for i in range(50):
block = Block(blue)
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(350)
block_list.add(block)
all_sprites_list.add(block)
player = Player()
all_sprites_list.add(player)
done = False
clock = pygame.time.Clock()
score = 0
player.rect.y = 370
# -------- Main Program Loop -----------
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
bullet = Bullet()
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
all_sprites_list.add(bullet)
bullet_list.add(bullet)
if event.type== pygame.KEYDOWN:
if event.key==K_a:
move_player_x=-1
elif event.key==K_d:
move_player_x=+1
elif event.key==K_w:
move_player_y=-1
elif event.key==K_s:
move_player_y=+1
if event.type== pygame.KEYUP:
if event.key==K_a:
move_player_x=0
elif event.key==K_d:
move_player_x=0
elif event.key==K_w:
move_player_y=0
elif event.key==K_s:
move_player_y=0
# --- Game logic
all_sprites_list.update()
player_x += move_player_x
player_y += move_player_y
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 1
print( score )
if bullet.rect.y < -10:
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
screen.fill(white)
all_sprites_list.draw(screen)
pygame.display.flip()
clock.tick(20)
pygame.quit()
so there the code, any help is much appreciated
here is what i came up with, I changed some code from an RPG me and a friend are making
Change your bullet class code to this:
class Bullet(pygame.sprite.Sprite):
def __init__(self, mouse, player):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([4, 10])
self.image.fill(black)
self.mouse_x, self.mouse_y = mouse[0], mouse[1]
self.player = player
self.rect = self.image.get_rect()
def update(self):
speed = 4.
range = 200
distance = [self.mouse_x - self.player[0], self.mouse_y - self.player[1]]
norm = math.sqrt(distance[0] ** 2 + distance[1] ** 2)
direction = [distance[0] / norm, distance[1 ] / norm]
bullet_vector = [direction[0] * speed, direction[1] * speed]
self.rect.x -= bullet_vector[0]
self.rect.y -= bullet_vector[1]
now it takes the players pos and the mouse click pos as arguments for the class
the math is a bit confusing but it basically take the vector from the two points and gets a direction and sends the bullet that way
when you create a new bullet instance call it like this:
bullet = Bullet(pygame.mouse.get_pos(), [player.rect.x, player.rect.y])
that way the class gets the two points when the mouse button is clicked!
when you run it you may notice that the bullet goes in the opposite direction of the mouse click thought that would add some challenge. You can change it if you need
P.S. the speed variable can be adjusted! Oh, and the math module needs to be imported

Categories