非物理碰撞

Corona 包含一个强大的基于 Box2D 的物理引擎,具有碰撞检测功能,可以完成很多事情。它甚至是事件驱动的,这意味着您可以感知碰撞何时开始和结束,碰撞涉及哪两个对象,它们在哪里接触等等。唯一的“问题”是每个碰撞对象都必须是启用物理的对象,并受此物理引擎控制,如果您没有在游戏的其他方面使用物理,这可能有点 overkill——例如,如果您只需要了解一个简单对象是否与另一个简单对象占据相同的屏幕空间。

替代检测方法

有几种方法可以在不使用物理引擎的情况下检测碰撞,包括

如果矩形有任何旋转应用于它,第一种方法可能会很复杂,并且分离轴定理相当计算密集型,因此本教程不会涵盖这两种方法。相反,我们将讨论**重叠圆形**和**重叠矩形**方法。

注意

在考虑非物理碰撞检测之前,请记住,如果您的对象不是基本的矩形或圆形,物理引擎几乎肯定是一个更好的选择。使用它,您可以定义复杂的基于多边形的形状、链线、多元素结构等等。基本上,本教程中的两种非物理方法遵循“足够接近”的方法,因此如果您需要精确的碰撞,基于物理的碰撞更合适。

重叠的圆形

在某些游戏中,即使不是艺术上的圆形的对象,其碰撞边界通常也可以用圆形表示,例如包围整个图像的圆形,或者跨越对象“中心”周围略小区域的圆形。在快速的游戏过程中,玩家可能不会注意到精确的形状精确碰撞检测。

一些非常简单的计算可以确定任意大小的两个圆是否重叠。

local function hasCollidedCircle( obj1, obj2 )

    if ( obj1 == nil ) then  -- Make sure the first object exists
        return false
    end
    if ( obj2 == nil ) then  -- Make sure the other object exists
        return false
    end

    local dx = obj1.x - obj2.x
    local dy = obj1.y - obj2.y

    local distance = math.sqrt( dx*dx + dy*dy )
    local objectSize = (obj2.contentWidth/2) + (obj1.contentWidth/2)

    if ( distance < objectSize ) then
        return true
    end
    return false
end

对于此函数,我们只需传入两个显示对象 obj1obj2。使用 contentWidth 属性确定它们的宽度,我们检查两个圆是否比它们中心之间的距离更近,如果是,我们就知道它们正在接触。

重叠的矩形

使用 Corona内置对象的 contentBounds 属性,更容易检测重叠的矩形。

local function hasCollidedRect( obj1, obj2 )

    if ( obj1 == nil ) then  -- Make sure the first object exists
        return false
    end
    if ( obj2 == nil ) then  -- Make sure the other object exists
        return false
    end

    local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
    local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax
    local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
    local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax

    return ( left or right ) and ( up or down )
end

此函数非常适用于瓷砖和卡片等方形或矩形对象。它使用一组 if 语句检查来查看一个矩形的任何角是否在另一个矩形的边界内。

重要

对于周围有一些透明度的图像,请注意,这**将**包括透明区域,因为该函数使用这些图像的内容边界。如有必要,您可以调整比较中的数学运算,例如,对各种“min”和“max”属性添加或减去一个像素值,以考虑透明度的宽度。

碰撞检测

现在您已经有了几种不同的方法来确定两个项目是否发生碰撞,您如何使用它们?与基于物理的碰撞不同,没有碰撞侦听器事件,系统会准确地告诉您对象何时发生碰撞。相反,您必须在自己的代码中定期(或频繁)检查。

也许最明显的方法是使用 "enterFrame" 运行时侦听器,如下所示:

考虑这个示例函数,它假设您有一个 myPlayer 对象,并且多个硬币对象存储在一个名为 coinCache 的表中。假设玩家也可以使用基于圆形的检测,此函数在每一帧循环遍历 coinCache 表,并检查玩家是否与任何硬币接触。

local function gameLoop( event )

    for i = 1,#coinCache do
        -- Check for collision between player and this coin instance
        if ( coinCache[i] and hasCollidedCircle( myPlayer, coinCache[i] ) ) then
            -- Remove the coin from the screen
            display.remove( coinCache[i] )
            -- Remove reference from table
            coinCache[i] = nil
            -- Handle other collision aspects like increasing score, etc.
        end
    end
    return true
end
Runtime:addEventListener( "enterFrame", gameLoop )

总结

我们的非物理碰撞检测

教程到此结束。如您所见,当您构建一个需要基本碰撞检测的应用程序时,这是一种方便的方法,而且物理引擎的功能强大,超出了您任务的需求。