如何处理多个块的碰撞,就好像它们是一个实体一样?

How can I handle collisions of multiple blocks as if they were a single entity?

我正在使用 Corona SDK 创建一个 2D 平台游戏,但遇到了碰撞问题。

基本上有一个角色在这个由块组成的地面上奔跑。这是因为有时地面上可能会有洞。 游戏是一个无尽的游戏,所以随着角色向前移动,新的方块(和洞)会被动态添加 - 如果它们离开屏幕也会被移除。

它工作得很好,但这种方法对碰撞系统有效,让我解释一下如何。 现在我已经有了地面,我希望角色跳跃,但前提是它接触地面,以避免在空中跳跃。 每当在 characterground 之间检测到碰撞时,都会触发一个事件 - 两次。第一次是角色进入地块,第二次是角色离开地块。因此,当角色降落在地面上时,一个 isGround 布尔值被设置为 true。当 - 在跳跃之后 - 它离开时,标志设置为 false。问题是每次它离开一个街区进入另一个街区——沿着地面行走而不跳跃——旗帜都会更新。这使得基于 isGround 标志的跳转不太可靠。有时会发生无法跳跃的情况,因为 isGround == false 虽然角色在地上。

地面块创建片段

-- init() method set the sprite of the ground block and physic to that sprite
function GroundBlock:init()
    self.sprite = display.newImageRect(self.path, self.width, self.height)

    self.sprite.x = self.x
    self.sprite.y = self.y

    physics.addBody(self.sprite, 'static', {
        density = 0,
        friction = 0,
        bounce = 0,
        box = {
            halfWidth = self.width / 2,
            halfHeight = self.height / 2,
            y = 16,
            x = 0
        }
    })

    local collisionObj = {
        name = 'ground'
    }

    self._collision = collisionObj
    self.sprite._collision = collisionObj

    self.isShow = true
end

地面放置 GroundBlocks 片段

-- init() method initialize the ground with a fixed number of blocks
function Ground:init()
    self.offsetX = 0
    while self.offsetX < self.camera.borderRight * 2 do
        self._createBlock(1)
    end
    self.lastCameraPos = self.camera.borderRight
end

-- update() is called once per frame
function Ground:update()

    if (self.camera.borderRight - self.lastCameraPos > self._blockWidth) then
        local rand = math.ceil(math.random() * 10) % 2

        if self._skippedBlock >= 2 or rand == 0 then
            self._createBlock(1)
            self._skippedBlock = 0
        else
            self._createBlock(0)
            self._skippedBlock = self._skippedBlock + 1
        end

        self.lastCameraPos = self.camera.borderRight
    end

    for i, block in ipairs(self.blocks) do
        if block.sprite.x < self.camera.borderLeft - block.width then
            table.remove(self.blocks, i)
            self.camera:remove(block.sprite)
            block:delete()
        end
    end
end

碰撞检测片段

function Character:collision(event)
    if ( event.phase == "began" ) then

        if event.other._collision.name == "ground"  then
            self.isGround = true
        end

    elseif ( event.phase == "ended" ) then

        if event.other._collision.name == "ground" then
            self.isGround = false
            print('nope')
        end

    end
end

一种解决方案是将地面作为单个 imgRect,但如何在其中打孔?

您可以通过跟踪角色是否可以跳跃而不是跟踪角色是否在地面上来简化代码并防止此问题发生。

例如,

function jump( event )
    if event.phase == "began" then
        if canJump then
            canJump = false
            -- your code that makes the player jump
        end
    end
end

你应该是通过触摸来判断玩家角色是否跳跃吧?这样,只要角色尚未跳跃,您就会在触摸开始时触发跳跃。

然后您可以通过稍微编辑它来在碰撞函数中重置此值:

function Character:collision(event)
    if event.phase == "began" then
        if event.other._collision.name == "ground" then
            canJump = true
        end
    end
end

这样一来,角色的跳跃能力取决于玩家是否已经按下跳跃键,以及角色在上次跳跃后是否已经落地。

这种方法还使您能够转向实施双跳等机制。如果您选择使用数字变量而不是使用布尔 canJump 变量,例如jumpsLeft,你可以减少每次角色跳跃时剩下的跳跃次数,只有当 jumpsLeft 大于 0 时才让角色跳跃。然后你只需将值重置回 1(或其他任何值)你会想要在落地时)。