【问题标题】:Gravity Collision Issue with pygamepygame的重力碰撞问题
【发布时间】:2018-01-21 17:48:22
【问题描述】:

在 pygame 中创建一个平台游戏,并使用矢量来表示重力和运动。现在我卡在与平台的碰撞上,因为平台会撞到平台并几乎完全沉入平台,如下所示:

Player sunk into platform block

(编辑)人们想要更多的代码信息,所以......

主类:

import pygame as pg
import sys

from sprites import *

class Game:

    def __init__(self):
        pg.init()
        pg.mixer.init()
        pg.display.set_caption(TITLE)
        self.screen = pg.display.set_mode((WIDTH, HEIGHT))
        self.clock = pg.time.Clock()
        self.running = True
        self.load_data()
        self.new_game()

    def load_data(self):
        # Eventually will load data
        pass

    def new_game(self):
        # Creates new instances of all of the sprites
        # groups and re-renders sprites at starting locations
        self.all_sprites = pg.sprite.Group()
        self.platform_sprites = pg.sprite.Group()
        self.player = Player(self, 60, 60)

        # Creates some test platforms
        for x in range(10):
            Platform(self, x, 6)

    def run(self):
        # Basic game loop
        while self.running:
              self.dt = self.clock.tick(FPS) # Get time passed since last        update / Set FPS
            self.events()
            self.render()
            self.update()

    def events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.running = False
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_ESCAPE:
                    self.running = False
    # Check for quit event and close window

    def render(self):
        self.screen.fill(WHITE)
        self.all_sprites.draw(self.screen) # Draws all sprites to screen
        pg.display.flip() # updates screen

    def update(self):
        self.all_sprites.update() # calls the update function of all of the sprites

if __name__ == '__main__':
    g = Game()
    while g.running:
        g.run()
    pg.quit()
    sys.exit()

....播放器和平台精灵代码:

import pygame as pg

from constants import *
vec = pg.math.Vector2


class Player(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites
        self.game = game
        super().__init__(self.groups)
        self.image = pg.Surface((40, 70))
        self.image.fill(BLUE)
        self.rect = self.image.get_rect()
        pg.draw.rect(self.image, BLACK, (0, 0, self.rect.width, self.rect.height), 3)

        self.pos = vec(x, y)
        self.vel = vec(0, 0)
        self.acc = vec(0, 0)

    def move(self):

        self.acc = vec(0, 0)
        keypress = pg.key.get_pressed()
        if keypress[pg.K_a]:
            self.acc.x += -PLAYER_MOVE_SPEED
        if keypress[pg.K_d]:
            self.acc.x += PLAYER_MOVE_SPEED

        self.acc += self.vel * PLAYER_FRICTION
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc
        self.rect.midbottom = self.pos

    def collisions(self, axis):
        if axis == 'y':
            for plat in self.game.platform_sprites:
                if self.acc.y > 0:
                    if pg.Rect.colliderect(self.rect, plat.rect):
                        self.acc.y = 0
                        self.pos.y = plat.rect.top

    def update(self):
        self.move()
        self.collisions('y')



class Platform(pg.sprite.Sprite):
    def __init__(self, game, x, y):
        self.groups = game.all_sprites, game.platform_sprites
        self.game = game
        super().__init__(self.groups)
        self.image = pg.Surface((TILESIZE, TILESIZE))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.x = x * TILESIZE
        self.y = y * TILESIZE

    def update(self):
        self.rect.x = self.x
        self.rect.y = self.y

“常量”文件只定义了屏幕大小、颜色和播放器速度等内容。如果有需要我会添加它。

【问题讨论】:

  • 如果您提供minimal, complete and verifiable example,我们会更容易为您提供帮助。
  • 了解您使用move()collisions() 的顺序肯定会有所帮助。如果玩家正在下沉但在某个点之后停止,这可能是因为您正在显示处理碰撞之前的播放器。

标签: python pygame game-physics


【解决方案1】:

Player.collisions

if self.acc.y > 0:
    if pg.Rect.colliderect(self.rect, plat.rect):
        self.acc.y = 0
        self.pos.y = plat.rect.top
  1. 问题的主要原因是玩家的速度:

    您正在重置垂直加速度 (acc.y) 和位置 (pos.y),但您似乎忘记了垂直速度 (vel.y)。 玩家正在下沉,因为它仍有向下的速度,您可以使用self.vel.y = 0 进行补救。
    .

  2. 另一个更小的问题可能是,正如@LarryMcMuffin 正确猜测的那样,您的移动/绘图之间存在排序问题:

    您可能会正确更新Player.pos.y,以便玩家完美地站在平台顶部,但如果您希望将角色绘制在新位置,您还需要更新self.rect.midbottom -- 你这样做了,但是在Player.move,它被称为before。您可以重新排序移动/碰撞函数,但我的建议是简单地将 self.rect.midbottom = self.posPlayer.move 移动到 Player.update 的最后。

最终结果:

def collisions(self, axis):
    if axis == 'y':
        for plat in self.game.platform_sprites:
            if self.acc.y > 0:
                if pg.Rect.colliderect(self.rect, plat.rect):
                    self.acc.y = 0
                    self.vel.y = 0
                    self.pos.y = plat.rect.top

def update(self):
    self.move()
    self.collisions('y')
    self.rect.midbottom = self.pos

我想指出的另一件事,即使它可能与您描述的问题无关: 使用增量时间

看,你有两个影响角色位置的值:

  • velocity (vel):表示位置的变化随着时间的推移
  • acceleration (acc):表示速度随时间的变化

随着时间的推移 在这里是关键——因为两次调用 move 之间经过的时间是可变的,这很重要:随着时间的流逝,速度应该更多/更少位置。幸运的是,您在 Game.dt 中保存了两帧/更新之间的时间。将其传递给您的播放器并或多或少地像这样使用它:

self.acc += self.vel * PLAYER_FRICTION
self.vel += self.acc * dt
self.pos += (self.vel + 0.5 * self.acc) * dt

它不会完全解决这个问题,因此两个 dt = .5 调用产生与一个 dt = 1 调用相同的效果,但它会“填充差距”。我认为如果不以固定间隔更新物理场,或者不采用昂贵且复杂的计算(使用 power 函数),您就无法实现它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    相关资源
    最近更新 更多