如何将运动物理功能缩放到每秒帧数(在游戏引擎中)?

时间:2012-09-12 02:38:57

标签: javascript physics game-engine game-physics

我正在使用Javascript(HTML5 Canvas)制作游戏。我实现了一个简单的算法,它允许一个物体跟随另一个混合了基本物理的物体(一个力矢量来驱动物体在正确的方向上,速度叠加动量,但是由一个恒定的拖曳力减慢)。此刻,我按照鼠标(x,y)坐标将其设置为矩形。这是代码:

// rectangle x, y position
var x = 400; // starting x position
var y = 250; // starting y position
var FPS = 60; // frames per second of the screen
// physics variables:
var velX = 0; // initial velocity at 0 (not moving)
var velY = 0; // not moving
var drag = 0.92; // drag force reduces velocity by 8% per frame
var force = 0.35; // overall force applied to move the rectangle
var angle = 0; // angle in which to move

// called every frame (at 60 frames per second):
function update(){
    // calculate distance between mouse and rectangle
    var dx = mouseX - x;
    var dy = mouseY - y;
    // calculate angle between mouse and rectangle
    var angle = Math.atan(dy/dx);
    if(dx < 0)
        angle += Math.PI;
    else if(dy < 0)
        angle += 2*Math.PI;

    // calculate the force (on or off, depending on user input)
    var curForce;
    if(keys[32]) // SPACE bar
        curForce = force; // if pressed, use 0.35 as force
    else
        curForce = 0; // otherwise, force is 0

    // increment velocty by the force, and scaled by drag for x and y
    velX += curForce * Math.cos(angle);
    velX *= drag;
    velY += curForce * Math.sin(angle);
    velY *= drag;

    // update x and y by their velocities
    x += velX;
    y += velY;

并且每秒60帧可以正常工作。现在,棘手的部分:我的问题是,如果我将其更改为不同的帧速率(例如,30 FPS),我如何修改力和拖动值以保持运动不变?

也就是说,现在我的矩形(其位置由x和y变量决定)以每秒约4个像素的最大速度移动,并在大约1秒内加速到其最大速度。但是,如果我改变帧速率,它移动得更慢(例如30 FPS加速到每帧仅2个像素)。

那么,我怎样才能创建一个以FPS(每秒帧数)为输入的方程式,并吐出正确的“拖动”和“强制”值,这些值会以相同的方式实时显示?

我知道这是一个很重要的问题,但也许有游戏设计经验或编程物理知识的人可以提供帮助。谢谢你的努力。

jsFiddle:http://jsfiddle.net/BadDB

4 个答案:

答案 0 :(得分:3)

我会介绍一个实际的时间测量。然后,您应该将公式重新设计为实际经过时间和所需最大速度的函数。使用实际经过时间的好处是方程式即使在(因为负载或你有什么)不能在编程的FPS下运行的系统上也能很好地工作。

暂且不说 - 您应该使用Math.atan2(dy, dx)代替Math.atan(dy/dx)。 (想想dx == 0时会发生什么。)

答案 1 :(得分:1)

理想情况下,您应该将时间框架视为自上一帧开始经过的时间,然后根据实际增量计算比例因子,并将其用于基于时间的计算。

理想情况下,您的游戏运行速度为60fps。 同样要考虑的是,对于每一个游戏框架,你的游戏很少会以1000毫秒/ 60英尺的速度运行。

所以你的理想是1000/60。 您的实际值为current_timestamp - previous_timestamp。 您的时间范围是actual/ideal

现在,您只需使用比例来转换时间敏感值。

任何时间函数都可以使用“每帧理想值”(magnitude = 8; current_magnitude = magnitude * scale; vec.x *= current_magnitude; vec.y *= current_magnitude; vec.z *= current_magnitude;)。

你只需要小心了解何时繁殖,何时不要。 如果计算是基于时间的,则预乘。如果不是,则不要。

如果您的游戏以15fps运行,那么您的时间尺度将是4倍,对吧? 这不应该影响任何东西的力量。它不会影响汽车的扭矩 - 发动机正在建造的被压抑的能量。 影响的是在这段精确时间内发生了多少加速度(线性或其他)。

如果汽车应该加速0.5m / s ^ 2,或者你要决定的任何东西,那么你只看到加速度的一部分(加到当前速度上),这适用于特定的部分你现在的第二个。 然后在下一次更新中,汽车应以您设定的速度行驶,乘以您计算的时间尺度,作为当前帧与前一帧之间的差值。

旋转应该以相同的方式计算。

这允许您将不一致的帧率与实际操作分开,因为您始终基于百分比而不是硬数字(涉及时间)来操作。

这也允许像子弹时间这样容易愚蠢地完成。 将添加的项目符号时间因素添加到 所有内容 ,但瞄准除外。 或者如果你想做一个忍者反射 - 无论如何,对玩家应用不同的时间尺度,而不是敌人。

对于冻结时间,您有两种选择:

  1. 将所有内容设置为0并忘记之前的值(让所有内容从0恢复,恢复时)
  2. 将所有内容设置为0,但保留之前所有值的“框架”。之后忘记0,但是将下一个时间段视为暂停发生后只有1帧已经过去。
  3. 当您谈论如何处理drag时 - 您自己说过:drag在您的模拟中是constant。 一段时间或你所谈论的时间跨度有多长并不重要,拖拽在一段时间内对物体的影响与其对任何其他物体的影响一致时间跨度。

答案 2 :(得分:0)

加倍力并保持阻力不变。

修改

数学:

可以通过两个参数完全描述运动:初始加速度和终端速度。如果这两个看起来正确,那么动作将是正确的。

对于初始加速度,拖动(这种拖动)无关紧要。由于force是一个加速度,我们所要做的就是在一秒钟内添加多少帧,以获得一秒钟的加速度:

强制 30 * 30 =强制 60 * 60
force 30 = force 60 * 60/30 = 2.0 * force 60 = 2.0(0.35)= 0.7

当力和阻力平衡时,会发生终点速度。

V term * drag = force
drag = force / V term

我们希望缩放V term ,但我们也缩放force,因此缩放项取消; drag无需更改。

答案 3 :(得分:0)

力的积分是速度,当你按照时间进行积分时,速度的积分就是位置。如果你的时间步长等于两帧之间的时间,那么如果你有一个碰撞后探测器,你的子弹物理将会起作用。

两帧之间的时间与FPS成反比。

FPS =总帧数/总时间

时间步长=(1.0 /(浮点)FPS秒)* K. K是一个常数,使你的时间步长足够小,所以你的物理足够稳定。