2

我正在使用 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

4 回答 4

3

我会介绍一个实际的时间度量。然后,您应该修改您的方程,使其成为实际经过时间和所需最大速度的函数。使用实际经过时间的好处是,即使在(由于负载或你有什么)不以编程的 FPS 运行的系统上,这些方程也能很好地工作。

顺便说一句 - 你应该使用Math.atan2(dy, dx)而不是Math.atan(dy/dx). (想想当 时会发生什么dx == 0。)

于 2012-09-12T03:00:04.007 回答
1

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

考虑一下理想情况下,您的游戏以 60fps 运行。还要考虑到,你的游戏很少会在每一个单帧游戏中精确地以 1000ms/60f 运行。

所以你的理想是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 帧。

当您谈论要做什么时drag-您自己说过:dragconstant您的模拟中。不管你说的时间跨度有多长或多短,阻力在那个时间跨度内对一个对象的影响与它在任何其他时间段内对任何对象的影响是一致的时间跨度。

于 2012-09-12T03:24:42.870 回答
0

将力加倍并保持阻力不变。

编辑:

数学:

运动可以完全用两个参数来描述:初始加速度和终速度。如果这两个看起来正确,则动作看起来正确。

对于初始加速度,阻力(这种阻力)无关紧要。由于force是加速度,我们所要做的就是将它与一秒钟内的帧数相加,以获得一秒钟的加速度:

30 * 30 = 力60 * 60
30 = 力60 * 60/30 = 2.0 * 力60 = 2.0 (0.35) = 0.7

最终速度发生在力和阻力平衡时。

V* 阻力 = 力
阻力 = 力 / V

我们想缩放 V term,但我们也在缩放force,所以缩放项取消;drag无需更改。

于 2012-09-12T03:01:48.640 回答
0

当您根据时间进行积分时,力的积分是速度,速度的积分是位置。如果你让你的时间步长等于两帧之间的时间,那么如果你有一个碰撞后检测器,你的子弹物理就可以工作。

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

FPS = 总帧数 / 总时间

时间步长=( 1.0/(float)FPS 秒数)*K。K 是一个常数,可以使您的时间步长足够小,从而使您的物理场足够稳定。

于 2012-09-12T16:40:38.123 回答