当前位置: 首页 > 知识库问答 >
问题:

随时间变化的加速度

赖鸿羲
2023-03-14

我正在做一个自上而下的视图游戏,在游戏中敌人向特定的位置移动。被移动的目的地经常会发生剧烈的变化--有时敌人还在向前一个目的地移动的时候……

我想要实现比直线移动更真实的移动,所以当敌人在目的地之间切换时,应该有一些加速和减速。

转向(方向)不是一个因素。你可能会认为sprites会像气垫船一样移动,在目的地之间以最快的速度进行加速和减速。

最后一个考虑是,敌人不仅有一个“最大速度”值,而且还有一个“最大加速度”值--这将是指导每一个敌人在反方向移动时能做出多快反应的东西。

为简单起见,假设敌人没有任何移动摩擦...当它停止加速时,它只是永远保持在当前的速度巡航。

关于上下文,让我们详细介绍car示例。特定的汽车具有:

    null

所有这些,我想唯一的值,我真正需要计算的是什么油门应该是,因为这是唯一的东西,我可以控制的车辆。

那么,我如何知道在任何给定的时刻使用多少油门,以尽可能快地到达目的地(像一些交通灯)而不超过我的目的地呢?我需要知道多大程度的下油门,短暂地不加速,然后如何努力减速,因为我正在接近目的地。

这款游戏可能会有一个在线组件。也就是说,玩家将通过插座连接传送他们的位置...然而,即使是最好的连接也永远无法实现发送位置的频繁程度,以实现流畅的移动--你需要插补。您需要发送‘速度’(速度)与坐标,以便我可以假设未来的位置之间的时间的数据包收到。

所以,基本上我已经确定了,在任何时候需要计算的主要方面是使用多少油门。让我们来研究一下我的逻辑...

想象一个非常基本的线性运动随时间变化的公式...它看起来应该是这样的:

实施例1-随时间变化的位置

currentDY = 5;               // Current 'velocity' (also called Delta Y or 'DY')
currentY += currentDY * time // Current Y pos is increased by movement speed over time.
throttle = -1
currentDY += throttle * time;
currentY += (currentDY * time);
//Throttle being -1 eventually decelerates the DY over time...

尽管如此,仅仅随着时间的推移改变速度是不够的,但我们还需要能够在减速发生之前预知减速的幅度(即“倒油门”)。这是我到目前为止得到的,我几乎可以肯定这是错误的方法...

示例3-随时间节流??(我卡住了)

guideY = currentY + (currentDY * (timeScale * 3000));
dist = endY - guideY;
throttle = Math.max(-1, Math.min(1, dist / 200));
currentDY += throttle * time;
currentY += (currentDY * time);

正如你所看到的,这一次我们试图通过猜测敌人在未来任意时间(即3秒)的位置来预测使用多少油门。如果guidey经过了目的地,我们知道我们必须开始制动(即降低速度以停在目的地上方)。取决于敌人的未来位置有多远(即throttle=dist/200;)

我只是使用了3秒dist/200的错误值,还是我没有在这里实现一个完全工作的解决方案?

目前,与直线运动相比,到达目标位置的时间似乎要长8倍。我甚至还没有达到为DeltaVelocity或DeltaAcceleration实现最大值的程度--尽管下面的JSFiddle中没有这些值,但公认的解决方案必须考虑这些值...

我已经把我所有的例子放在一个工作的jsfiddle中。

正如您所看到的,我的第三个示例(请参见update函数)中,sprite“建立”到所需位置的时间比它应该花费的时间要长得多。我的数学错了。我搞不懂这里需要什么。

在任何给定时间,节流应该是什么?使用throttle是正确的方法吗?非常感谢您的协助。

好吧,我这几天都困在这了。这个问题是要得到一些phat赏金的。

所以,除了赏金,你还可以满足于知道你的答案将有助于毁了肮脏的视频游戏作弊者的生活!

共有1个答案

孙承弼
2023-03-14

编辑二:答案作者添加的小提琴;在他的回答中添加以下内容,以防其他人发现这个问题:http://jsfidle.net/inexorably/cstxljqf/。用法/数学将在下面进一步解释。

编辑1:为在评论中收到的澄清而改写。

您真的应该改变您的实现风格。

我还想改进如何生成指南值。我假设向导可以移动。在这种情况下,目标将具有上面列出的值:x、y、vx、vy、ax、ay、jx、jy。你可以随便给它们起名字,我要用targetX,targety...以更好地说明我的观点。

从这里你应该找到你的指导价值。当sprite距离目标超过三秒时,您可以在三秒内使用目标的位置(注意:我建议将其设置为一个变量,以便修改)。对于本例:

 predictionTime = 3000*timescale; //You can set this to however many seconds you want to be predicting the position from.

如果您真的想,您可以使用积分函数或引导值的循环来平滑曲线,以便从目标值得到更精确的引导值。但是,这不是一个好主意,因为如果实现多个targets/etc,可能会对性能产生负面影响。因此,我们将使用一个非常简单的估计,这是相当准确的成本如此低。

 if (sprite is more than predictionTime seconds away from the target){
      guideX = targetX + predictionTime * targetVX;
      guideY = targetY + predictionTime * targetVY;
 }
 else{
      guideX = targetX + remainingTime * targetVX;
      guideY = targetY + remainingTime * targetVY;.
 }

在这里,你有三个选择来寻找剩余时间的价值。您可以将remainingTime设置为零,使指南的坐标与目标的坐标相同。您可以将remainingTime设置为sqrt((targetX-currentX)^2+(targetY-currentY))/(sqrt(currentVX)^2+(currentVY)^2),这基本上是2D中的距离/时间,这是一个廉价而体面的近似值。或者,您可以使用前面提到的for循环来模拟一个积分,以解释变化的速度值(如果它们偏离maxV)。然而,通常您将保持在或接近maxV,所以这是不真正值得的费用。编辑:另外,如果剩余时间小于某个值(大约0.5左右),我也建议将剩余时间设置为0。这是因为你不想要hitbox问题,因为你的精灵在跟随有轻微偏移的引导坐标(以及目标在改变方向时绕圈移动会给它一个较大的速度值/本质上是一种很强的闪避策略)。也许你应该专门为此添加一些东西。

我们现在有了guideX和guideY值,并考虑到距离移动目标很近时,会影响到引导坐标应该放置在距离目标多远的地方。现在我们将处理“当前”值原型。

我们将首先更新最小的微分值,检查它们是否在我们最大值的范围内,然后更新下一个最小值,等等。注意JX和JY是前面提到的,以允许非恒定加速度。

 //You will have to choose the jerk factor -- the rate at which acceleration changes with respect to time.    

 //We need to figure out what direction we're going in first.  Note that the arc tangent function could be atan or whatever your library uses.
 dir = arctan((guideY-currentY)/(guideX-currentX));

这将返回一个角度的方向,无论是弧度或程度取决于您的触发库。这是您的精灵需要采取的角度,以走到向导的方向。

 t = time; //For ease of writing.
 newAX = currentAX + jerk*t*cos(dir);
 newAY = currentAY + jerk*t*sin(dir);

你可能想知道newAx值会怎么降低。如果是,请注意,如果guide在目标的左侧,则cos(dir)将返回负值,同样,如果sprite需要向下,则sin(dir)将返回负值。因此,也要注意,如果指南在精灵的正下方,那么newAx将为0,newAY将为负值,因为它正在下降,但加速度的大小,换句话说,你与maxA比较的大小,将为正--即使精灵向下移动,它也不是以负速度移动的。

注意,因为cos和sin大概与atan属于同一库,所以单位将是相同的(所有的度或所有的弧度)。我们有一个最大加速度值。所以,我们会检查,以确保我们没有超过。

 magnitudeA = sqrt(newAX^2+newAY^2);
 if (magnitudeA > maxA){
      currentAX = maxA * cos(dir);
      currentAY = maxA * sin(dir);
 }

所以在这一点上,我们要么已经达到了加速度上限,要么已经有了令人满意的加速度分量,其幅度小于maxa。让我们对速度做同样的研究。

 newVX = currentVX + currentAX*t;
 newVY = currentVY + magnitudeA*t*sin(dir);

注意,我在这里包括了两种求速度分量的方法。无论哪种方法都有效,为了简单起见,我建议选择一种方法,并将其用于x和y速度值。我只想强调加速度大小的概念。

 magnitudeV = sqrt(newVX^2+newVY^2);
 if (magnitudeV > maxV){
      currentVX = maxV * cos(dir);
      currentVY = maxV * sin(dir);
 }

我们也不想再绕着目标飞来飞去了。但是,我们并不想像在JSFiddle中那样说放慢速度,因为如果目标正在移动,它就会离开(lol)。因此,我建议检查你的距离有多近,如果你在一定距离内,以目标速度的偏移量线性地降低你的速度。所以将关闭时间设置为一个很小的值,比如0.3,或者在你的游戏中感觉很好的值。

 if (remainingTime < closeTime){
      //We are very close to the target and will stop the boomerang effect.  Note we add the target velocity so we don't stall while it's moving.
      //Edit: We should have a min speed for the enemy so that it doesn't slow down too much as it gets close.  Lets call this min speed, relative to the target.
      currentVX = targetVX + currentVX * (closeTime - remainingTime);
      currentVY = targetVY + currentVY * (closeTime - remainingTime);
      if (minSpeed > sqrt(currentVX^2+currentVY^2) - sqqrt(targetVX^2-targetVY^2)){
           currentVX = minSpeed * cos(dir);
           currentVY = minSpeed * sin(dir);
      }

 }
 magnitudeV = sqrt(currentVX^2+currentVY^2);
 newX = currentX + currentVX*t;
 newY = currentY + currentVY*t;

 //Check if x, y values are good.
 current X = newX; currentY = newY;
 类似资料:
  • 用例是为数字合成产生正弦波,因此,我们需要计算sin(d t)的所有值,其中: 幼稚的实现方式是: 但是,问题是当t增加时,精度会降低,因为给“sin”函数提供了大量的数字。 改进的版本如下: 输出:

  • 给定一个场景,例如: null 然而,如果一个新的开发人员出现并试图从一开始就滚动更改,这不会失败吗?由于liquibase只查看文件的当前状态,ChangeSet3不会因为包B的最新版本包含对表a中一个列的引用而失败吗? 如何解决这一问题?有没有一种方法让Liquibase基于repo commit或标记来拉变更集?或者您必须编写一个包装器来串行检查所有的发布标记并将liquibase应用于每个

  • 问题内容: 因此,这是我要在python中执行的操作的抽象代码。 在这里,当我清除dict_时,显然list_中的所有元素都被删除,因为它们只是地址映射到变量dict_。 我想要的是复制dict_的实例,以便可以将其存储在list_中。 有人可以解释一下在每个循环中将获取的字典存储到list_中的方法吗?提前致谢。 问题答案: 您正在将对字典的引用添加到列表中,然后清除字典 本身 。这将删除字典的

  • 我有一个程序,它写了很多如果结果。例如,这: (不是实际代码) 所以当您键入一个大数字时,控制台中会有大量答案等待输入。所以,当它达到某个点时,内置浏览器编译器(Opera GX)就停止工作。我需要一些方法在计算后立即写出这些数字,希望不会浪费时间。我该怎么做?(让您知道,我的实际代码将结果放在“div”元素中)

  • 我是新来的Prestashop,但完全能够修改来源。 我如何才能将对给定产品的访问权限限制为仅在其他组24小时之前访问一个给定组? 谢谢你的回答。

  • 我试着用各种“Rest”和“atOnceUser”进行注射。我在留档上没有找到一个好的解决方案。 我的方法是使用本地计数器创建一个名为“getNextNumber()”的函数,以增加“atOnceUser”的数量,但该函数一开始只调用一次。 有什么想法吗? 我的代码: