我目前正在使用python pygame创建平台游戏,但因碰撞而陷入困境。我已经遇到了底部和顶部碰撞,可以与角色精灵一起使用,但是到目前为止,角色不会停止或从平台的侧面弹起。在进行碰撞时,我使用的是sprite.spritecollide()
方法,如果有人可以提供帮助,我将以同样的方式进行操作。我已经正确完成了碰撞检查,但是处理碰撞的代码似乎无法正确完成。我进行碰撞检测的代码如下,在游戏更新功能的main.py中:
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()
到目前为止,我只尝试在平台的右侧进行碰撞,一旦完成了一侧的操作,就可以为另一侧进行复制。 附言如果您需要更多我的代码,我将在需要时将其添加到问题中。
编辑
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
答案 0 :(得分:3)
我无法测试您的代码,但通常的问题是spritecollide
无法告知您是否在x
或y
或两者上发生冲突。
当您一次移动x
和y
并检查碰撞时,您不知道在x
或y
或两者上是否发生碰撞。如果仅在y
上发生碰撞,并且您将检查vel.x
并移动播放器,那么您将得到错误的结果。如果仅在x
上发生碰撞,然后检查vel.y
并移动播放器,则结果相同。
您必须单独进行操作:
x
,仅检查冲突并仅检查vel.x
,y
,再次检查冲突并仅检查vel.y
,类似这样的东西:
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
您应该在platform examples的 Program Arcade Games With Python And Pygame中看到有效的示例
编辑:
完整代码
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
没有变化
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 = [
# 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
我打算将一些代码移到类Screen
上,然后使用该类来创建GameScreen
,StartScreen
,GameOverScreen
等。