虽然 Corona 的 Box2D 物理引擎可以轻松模拟 x 和 y 方向重力,但 径向重力 并不是
对于不知道什么是径向重力的人来说,它本质上就像一个行星的模拟引力或一块磁铁将外围金属物体拉向它。本教程将概述如何构建一个基本的径向重力模拟,这种模拟具有可以轻松更改的属性,包括重力场的尺寸和强度。
为了本教程的目的,让我们快速创建一个测试项目
打开 Corona 仿真器。
从欢迎窗口中单击“新建项目”或从
对于项目/应用程序名称,输入 RadialGravity
,并确保选中 空白 模板选项。保持屏幕尺寸设置不变,但将 默认方向 设置为 横向,然后单击 确定(Windows)或 下一步(Mac)。这将在您指定的路径(文件夹)中创建测试项目的相关文件。
找到项目文件夹,在您选择的文本编辑器中打开 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" )
详细检查这个代码时,我们执行了以下操作
首先,使用local objectToPull = event.other
经过第一个条件检查,我们确认 "刚开始"
阶段已经发生,并且 该物体还没有应用接触关节(这个第二个条件仅仅是一个
在短暂的定时延时后(请参见下面的说明),我们向物体添加一个接触关节,设置基本关节属性,然后将其目标设置为重力场的中心self.x
/self.y
)
因为某些物理动作(如创建关节)无法在与碰撞完全相同的时间步骤中完成,我们必须在短暂计时后执行这些动作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
)会将对象非常强烈地拉向中心。
正如您所看到的,使用一些基本的物理设置和一个碰撞检测函数,就可以轻松模拟径向重力。该概念可用于拥有引力的行星、磁铁或任何其他类型的游戏对象,您希望将一个或多个对象拉向另一个对象。尽情尝试各种可能性吧!