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

Javascript 如何将运动物理函数缩放到每秒帧数(在游戏引擎中)?,javascript,physics,game-engine,game-physics,Javascript,Physics,Game Engine,Game Physics,我正在用Javascript HTML5画布制作一个游戏。我实现了一个简单的算法,允许一个物体跟随另一个物体,在力矢量中混合了基本物理,以驱动物体朝正确的方向运动,速度叠加动量,但由于恒定的阻力而变慢。现在,我将它设置为一个矩形,跟随鼠标的x,y坐标。代码如下: // rectangle x, y position var x = 400; // starting x position var y = 250; // starting y position var FPS = 60; // fr

我正在用Javascript HTML5画布制作一个游戏。我实现了一个简单的算法,允许一个物体跟随另一个物体,在力矢量中混合了基本物理,以驱动物体朝正确的方向运动,速度叠加动量,但由于恒定的阻力而变慢。现在,我将它设置为一个矩形,跟随鼠标的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帧为输入的方程,并输出正确的阻力和力值,这些值在实时中的表现方式相同

我知道这是一个沉重的问题,但也许有游戏设计经验或编程物理知识的人能帮上忙。谢谢你的努力


jsfdle:

我将介绍一个实际的时间度量。然后,您应该将您的方程式修改为实际运行时间和期望最大速度的函数。使用实际运行时间的好处是,即使在由于负载或其他原因而无法以编程FPS运行的系统上,方程式也能很好地工作


另外,您应该使用Math.atan2dy,dx而不是Math.atandy/dx。想想当dx==0时会发生什么。我将引入一个实际的时间度量。然后,您应该将您的方程式修改为实际运行时间和期望最大速度的函数。使用实际运行时间的好处是,即使在由于负载或其他原因而无法以编程FPS运行的系统上,方程式也能很好地工作


另外,您应该使用Math.atan2dy,dx而不是Math.atandy/dx。想想当dx==0时会发生什么。

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

编辑:

数学:

运动可以完全用两个参数来描述:初始加速度和终端速度。如果这两个人看得对,议案也会看得对

对于初始加速度,阻力,这种阻力并不重要。由于力是一个加速度,我们所要做的就是把它加起来,一秒钟内有多少帧,得到一秒钟的加速度:

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

当力和阻力平衡时,出现终端速度

Vterm*阻力=力 阻力=力/Vterm


我们想要缩放Vterm,但我们也在缩放力,所以缩放项取消;阻力不需要改变。

将力加倍,保持阻力不变

编辑:

数学:

运动可以完全用两个参数来描述:初始加速度和终端速度。如果这两个人看得对,议案也会看得对

对于初始加速度,阻力,这种阻力并不重要。由于力是一个加速度,我们所要做的就是把它加起来,一秒钟内有多少帧,得到一秒钟的加速度:

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

当力和阻力平衡时,出现终端速度

Vterm*阻力=力 阻力=力/Vterm


我们想要缩放Vterm,但我们也在缩放力,所以缩放项取消;拖动不需要更改。

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

考虑到这一点,理想情况下,您的游戏以每秒60帧的速度运行。 同样要考虑的是,你的游戏很少会在每一个游戏框架中精确地运行1000毫秒/ 60F。 所以你的理想值是1000/60。 您的实际时间将是当前\u时间戳-上一个\u时间戳。 您的时间范围将是实际/理想的

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

任何与时间有关的事物都可以使用其每帧的理想值——震级=8;当前_震级=震级*刻度;vec.x*=电流的大小;向量y*=电流_幅值;矢量z*=电流的大小

你只需要小心地理解什么时候该乘,什么时候不该乘。 如果计算是基于时间的,则预乘。如果不是,那就不要

如果你的游戏以每分钟15帧的速度运行,你的时间尺度将是4倍,对吗? 这不应该影响任何事情的力量。比如说,它不会影响汽车的扭矩——发动机产生的被压抑的能量。这会影响到何鸿燊 在这个精确的时间跨度内,有很多线性加速度或其他加速度发生

如果汽车应该加速0.5m/s^2,或者你决定的任何速度,那么你只需要看看加速度加在当前速度上的分数,这适用于你当前所处的秒数的特定分数。 然后在下一次更新中,汽车应该以您设定的速度行驶,乘以您计算的时间刻度,作为当前帧与该点上一帧之间的差值

旋转应以相同的方式计算

这允许您将不一致的帧率与实际操作分开,因为您总是基于百分比而不是涉及时间的硬数字进行操作

这也使得像子弹时间这样的事情可以很容易地愚蠢地完成。 除了瞄准之外,在每件事上都增加一个子弹时间因素。 或者如果你想做一个忍者反射,对玩家和敌人应用不同的时间尺度

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

将所有值设置为0,然后忘记这些值,然后在恢复时让所有值从0开始累积动量 将所有值都设置为0,但保留所有先前值的帧。忘记0之后,但对待下一个时间跨度,就像暂停发生后只经过了1帧一样。 当你谈论如何处理阻力时,你自己也说过:阻力在你的模拟中是一个常数。
不管你说的时间跨度有多长,也不管你说的时间跨度有多短,物体在这段时间内受到的阻力的影响与它在任何其他时间跨度内对任何物体的影响是一致的。

理想情况下,你应该做的是将你的时间范围作为从上一帧开始经过的时间,然后根据实际增量计算比例因子,并将其用于基于时间的计算

考虑到这一点,理想情况下,您的游戏以每秒60帧的速度运行。 同样要考虑的是,你的游戏很少会在每一个游戏框架中精确地运行1000毫秒/ 60F。 所以你的理想值是1000/60。 您的实际时间将是当前\u时间戳-上一个\u时间戳。 您的时间范围将是实际/理想的

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

任何与时间有关的事物都可以使用其每帧的理想值——震级=8;当前_震级=震级*刻度;vec.x*=电流的大小;向量y*=电流_幅值;矢量z*=电流的大小

你只需要小心地理解什么时候该乘,什么时候不该乘。 如果计算是基于时间的,则预乘。如果不是,那就不要

如果你的游戏以每分钟15帧的速度运行,你的时间尺度将是4倍,对吗? 这不应该影响任何事情的力量。比如说,它不会影响汽车的扭矩——发动机产生的被压抑的能量。它所影响的是在精确的时间跨度内线性加速度或其他加速度的大小

如果汽车应该加速0.5m/s^2,或者你决定的任何速度,那么你只需要看看加速度加在当前速度上的分数,这适用于你当前所处的秒数的特定分数。 然后在下一次更新中,汽车应该以您设定的速度行驶,乘以您计算的时间刻度,作为当前帧与该点上一帧之间的差值

旋转应以相同的方式计算

这允许您将不一致的帧率与实际操作分开,因为您总是基于百分比而不是涉及时间的硬数字进行操作

这也使得像子弹时间这样的事情可以很容易地愚蠢地完成。 除了瞄准之外,在每件事上都增加一个子弹时间因素。 或者如果你想做一个忍者反射,对玩家和敌人应用不同的时间尺度

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

将所有值设置为0,然后忘记这些值,然后在恢复时让所有值从0开始累积动量 将所有值都设置为0,但保留所有先前值的帧。忘记0之后,但对待下一个时间跨度,就像暂停发生后只经过了1帧一样。 当你谈论如何处理阻力时,你自己也说过:阻力在你的模拟中是一个常数。
不管你说的时间跨度有多长,也不管你说的时间跨度有多短,阻力在该时间跨度内对物体的影响与它在任何其他时间跨度内对任何物体的影响是一致的。

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

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

FPS=总帧数/总时间

时间步长=1.0
/floatFPS seconds*K.K是一个常数,它使你的时间步长足够小,这样你的物理就足够稳定。

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

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

FPS=总帧数/总时间


时间步长=1.0/floatFPS秒*K.K是一个常数,可以使时间步长足够小,从而使物理足够稳定。

我删除了我的答案,因为它显然不正确,请看小提琴。不过我不知道为什么不对,对不起。这可能是因为物理实现的缘故,比如,你实际上没有使用运动学方程,AIUI。是的,这是一种粗略的实现。这是我第一次尝试模拟物理,所以不太好看。不管怎样,谢谢你的帮助,我很感激。我删除了我的答案,因为它显然是不正确的,看着小提琴。不过我不知道为什么不对,对不起。这可能是因为物理实现的缘故,比如,你实际上没有使用运动学方程,AIUI。是的,这是一种粗略的实现。这是我第一次尝试模拟物理,所以不太好看。无论如何,感谢您的帮助,我非常感谢。请记住,JavaScript的Math.atan1/0正确地返回pi/2,因为1/0是无穷大,而不是抛出某种除数为零的错误,因此不需要使用atan2。atan2为您提供的是对Math.atan2-1,-1等参数的正确象限处理@马特波尔-关于无限的好观点。我想我的Fortran时代已经开始了这里唯一需要注意的是,这将使您的实际结果取决于所使用机器的速度。例如,如果假设两个带电粒子之间的碰撞是线性运动,则会得到不同的答案。因此,如果需要独立于帧速率的结果,则必须非常小心对象的路径。此外,浮点错误的累积方式也不同,最终导致对帧速率的敏感度混乱。@PhilH-同意。通过实际时间测量,物理模拟将取决于机器速度。另一方面,如果假设每个动画周期始终是一个时间单位,则游戏体验将取决于机器速度。在速度较慢的机器上,时间会过得较慢。然而,OP描述了相当简单的物理,要解决的问题就是游戏体验随帧速率的变化而变化。记录在案,JavaScript的Math.atan1/0正确地返回pi/2,因为1/0是无穷大,而不是抛出某种除法零错误,所以不需要atan2。atan2为您提供的是对Math.atan2-1,-1等参数的正确象限处理@马特波尔-关于无限的好观点。我想我的Fortran时代已经开始了这里唯一需要注意的是,这将使您的实际结果取决于所使用机器的速度。例如,如果假设两个带电粒子之间的碰撞是线性运动,则会得到不同的答案。因此,如果需要独立于帧速率的结果,则必须非常小心对象的路径。此外,浮点错误的累积方式也不同,最终导致对帧速率的敏感度混乱。@PhilH-同意。通过实际时间测量,物理模拟将取决于机器速度。另一方面,如果假设每个动画周期始终是一个时间单位,则游戏体验将取决于机器速度。在速度较慢的机器上,时间会过得较慢。然而,OP描述了相当简单的物理,要解决的问题正是游戏体验随帧速率的变化而变化。