径向重力

虽然 Corona 的 Box2D 物理引擎可以轻松模拟 xy 方向重力,但 径向重力 并不是内置功能。幸运的是,只要使用标准物理碰撞事件和 接触关节,就可以相对轻松地完成这件事。

对于不知道什么是径向重力的人来说,它本质上就像一个行星的模拟引力或一块磁铁将外围金属物体拉向它。本教程将概述如何构建一个基本的径向重力模拟,这种模拟具有可以轻松更改的属性,包括重力场的尺寸和强度。

测试项目设置

为了本教程的目的,让我们快速创建一个测试项目

  1. 打开 Corona 仿真器。

  2. 从欢迎窗口中单击“新建项目”或从新建项目...文件 菜单中选择。

  3. 对于项目/应用程序名称,输入 RadialGravity,并确保选中 空白 模板选项。保持屏幕尺寸设置不变,但将 默认方向 设置为 横向,然后单击 确定(Windows)或 下一步(Mac)。这将在您指定的路径(文件夹)中创建测试项目的相关文件。

  4. 找到项目文件夹,在您选择的文本编辑器中打开 main.lua 文件,然后插入这些代码行

local physics = require( "physics" )
physics.start()  -- Start the physics engine
physics.setGravity( 0, 0 )  -- Set "space" gravity
math.randomseed( os.time() )  -- Seed the pseudo-random number generator

-- Set radial gravity simulation values
local fieldRadius = 120
local fieldPower = 0.4

创建径向场

第一步是创建一个重力场,该重力场将在进入该重力场的物体上施加拉力。在 Corona 中,我们可以将该重力场创建为一个简单的 径向实体,将其设置为 传感器。在您的项目中,添加以下行

local field = display.newCircle( display.contentCenterX, display.contentCenterY, fieldRadius )
field.alpha = 0.2

-- Add physical body (sensor) to field
physics.addBody( field, "static", { isSensor=true, radius=fieldRadius } )

与场碰撞的对象

其他物体受到径向重力影响的通用方法是它们与该重力场碰撞。从本质上讲,当一个物体进入重力场  —— 碰撞阶段为 “刚开始” —— 我们将在该物体和重力场中心之间创建一个物理接触关节。同样,如果一个物体离开重力场(碰撞阶段为 "已结束",我们会断开(解除)接触关节,让物体表现出正常的物理行为。

进入场中的对象

首先,我们向项目中添加以下代码,处理进入重力场的物体

local function collideWithField( self, event )

    local objectToPull = event.other

    if ( event.phase == "began" and objectToPull.touchJoint == nil ) then

        -- Create touch joint after short delay (10 milliseconds)
        timer.performWithDelay( 10,
            function()
                -- Create touch joint
                objectToPull.touchJoint = physics.newJoint( "touch", objectToPull, objectToPull.x, objectToPull.y )
                -- Set physical properties of touch joint
                objectToPull.touchJoint.frequency = fieldPower
                objectToPull.touchJoint.dampingRatio = 0.0
                -- Set touch joint "target" to center of field
                objectToPull.touchJoint:setTarget( self.x, self.y )
            end
        )
    end
end
field.collision = collideWithField
field:addEventListener( "collision" )

详细检查这个代码时,我们执行了以下操作

  1. 首先,使用local objectToPull = event.other,我们创建一个局部引用来指向与重力场碰撞的物体。

  2. 经过第一个条件检查,我们确认 "刚开始" 阶段已经发生,并且 该物体还没有应用接触关节(这个第二个条件仅仅是一个附加的安全措施).

  3. 在短暂的定时延时后(请参见下面的说明),我们向物体添加一个接触关节,设置基本关节属性,然后将其目标设置为重力场的中心self.x/self.y.

说明
  • 因为某些物理动作(如创建关节)无法在与碰撞完全相同的时间步骤中完成,我们必须在短暂计时后执行这些动作(此处为 10 毫秒). 为简便起见,我们在 timer.performWithDelay() 调用中使用匿名函数来创建触摸关节。

  • 在触摸关节的情况下,您实际上并未将对象连接到另一个对象,就像在大多数物理关节中所做的那样。对于此关节类型,“目标”只是世界坐标中的某一点。

离开场中的对象

为了不让引力场永久控制 出其半径的对象,我们需要添加一些代码来断开(移除)对象的触摸关节。

在您的项目中,在现有的 collideWithField() 函数内部添加以下代码

local function collideWithField( self, event )

    local objectToPull = event.other

    if ( event.phase == "began" and objectToPull.touchJoint == nil ) then

        -- Create touch joint after short delay (10 milliseconds)
        timer.performWithDelay( 10,
            function()
                -- Create touch joint
                objectToPull.touchJoint = physics.newJoint( "touch", objectToPull, objectToPull.x, objectToPull.y )
                -- Set physical properties of touch joint
                objectToPull.touchJoint.frequency = fieldPower
                objectToPull.touchJoint.dampingRatio = 0.0
                -- Set touch joint "target" to center of field
                objectToPull.touchJoint:setTarget( self.x, self.y )
            end
        )

    elseif ( event.phase == "ended" and objectToPull.touchJoint ~= nil ) then

        objectToPull.touchJoint:removeSelf()
        objectToPull.touchJoint = nil
    end
end

此内容添加很简单:在 "ended" 碰撞阶段,我们移除触摸关节,有效地让对象继续其正常物理行为。

生成对象

让我们通过以随机速度接近引力场的方式生成 5 个对象,来完成我们的项目。在上一行之后,将以下代码添加到您的项目中

for i = 1,5 do

    local object = display.newCircle( 0, (i*50)+10, 10 )
    physics.addBody( object, { radius=10 } )

    local velocity = math.random( 80, 120 )
    object:setLinearVelocity( velocity, 0 )
end

现在保存该项目,刷新/重新加载它,您将看到 5 个白色圆圈朝该场运动。在接触到引力场后,每个圆圈都将被绘制/吸引到场中心。运动速度足够快的圆圈可能会挣脱场半径的束缚,沿着其自身的自然路径飞走,而留在场内的圆圈则会持续被拉向中心。

为了测试行为差异,请将项目开头的 fieldPower 变量值更改为较高或较低的值。较低的值(如 0.2)会让场的拉力变得更柔和,而较高的值(如 0.8)会将对象非常强烈地拉向中心。

结论

正如您所看到的,使用一些基本的物理设置和一个碰撞检测函数,就可以轻松模拟径向重力。该概念可用于拥有引力的行星、磁铁或任何其他类型的游戏对象,您希望将一个或多个对象拉向另一个对象。尽情尝试各种可能性吧!