【问题标题】:Unity rigid object receives twice the force it is supposed to receiveUnity 刚体接受的力是它应该接受的力的两倍
【发布时间】:2019-10-01 07:36:47
【问题描述】:

我正在制作一款子弹可以开始环绕玩家的游戏。轨道是圆形的,而不是椭圆形的。子弹的阻力系数为 0。

起初,我只是简单地计算了轨道中的下一个位置,并将增量位置除以 fixedDeltaTime 以获得它的新速度。

不幸的是,这意味着快速子弹会遵循多边形路径,并且经常会错过目标。

我想通过给它们一个向内的力和一个几乎与轨道相切的速度来提高精度,这样它们就可以沿着玩家周围的抛物线段而不是直线。

抛物线段由它们的起点和终点以及它们的顶点定义,它们必须位于轨道弧上,速度等于我之前使用的速度 ((endpos - startpos) / fixedDeltaTime)。

为了计算抛物线,我计算了圆弧和线段上的中点,它们的差与施加的力成正比。

所以我们使用以下名称

fdt = fixedDeltaTime
t = fdt / 2
f = the force applied during the incoming physics frame
v0 = the velocity at the start of the frame
v1 = the velocity at the middle of the frame (at the vertex of the parabola)

dp1 = the relative position at the middle of the frame (midpos - startpos) and vertex of the parabola
dp2 = the relative position at the end of the frame (endpos - startpos)

力由以下两个方程定义:

// at vertex, only the "lateral" velocity remains
v1 = dp2 / fdt

// the difference between dp2 / 2 and dp1 is what the force can apply over half a frame

dp2 / 2 - dp1  = f * 0.5tt
therefore
(dp2 / 2 - dp1) / (0.5 * t * t) = f
(dp2 / 2 - dp1) / (0.5 * fdt/2 * fdt/2) = f
(dp2 - dp1 * 2) * 4 / (fdt * fdt) = f

//v0 is then easily calculated
v0 = v1 - t * force
v0 = (dp2 / fdt) - force * (fdt / 2)

然后我们得到这个工作代码:

Vector3 startPos = _rigidbody.position;

if (firstFrame == false && Vector3.Distance(predictedNextFramePos, startPos) > 0.01f)
    Debug.Log("WTF");

Vector3 nextPosition;
GetLocalOrbitPos(nextTime, out nextPosition);
nextPosition += _parent.GetPosition();

float fdt = Time.fixedDeltaTime;

float halfTime = (time + nextTime) / 2f;
Vector3 halfPosition;
GetLocalOrbitPos(halfTime, out halfPosition);
halfPosition += _parent.GetPosition();

Vector3 dp2 = nextPosition - startPos;
Vector3 dp1 = halfPosition - startPos;

Vector3 force = (dp2 - 2 * dp1) * 4 / (fdt * fdt);
Vector3 v0 = (dp2 / fdt) - (force * fdt / 2f);

Vector3 deltaPosPredicted = PhysicsTools.GetMovement(v0, force, fdt);
if (Vector3.Distance(deltaPosPredicted, dp2) > 0.001f)
    Debug.Log("position prediction error: " + Vector3.Distance(deltaPosPredicted, dp2));

predictedNextFramePos = deltaPosPredicted + startPos;

Vector3 deltaHPosPredicted = PhysicsTools.GetMovement(v0, force, fdt / 2f);
if (Vector3.Distance(deltaHPosPredicted, dp1) > 0.001f)
    Debug.Log("position prediction error: " + Vector3.Distance(deltaHPosPredicted, dp1));

//drawing the startpos, midpos, endpos triangle
Debug.DrawLine(startPos, startPos + dp2, Color.magenta, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos, startPos + dp1, Color.red, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos + dp2, startPos + dp1, Color.red, Time.fixedDeltaTime * 2);
//drawing v0 and force
Debug.DrawLine(startPos, startPos + force, Color.gray, Time.fixedDeltaTime * 2);
Debug.DrawLine(startPos, startPos + v0, Color.blue, Time.fixedDeltaTime * 2);
//drawing the parabola arc
{
    Vector3 pos = startPos;
    Vector3 vel = v0;
    for (int i = 0; i < 10; i++)
    {
        Vector3 offset = PhysicsTools.GetMovementUpdateVelocity(ref vel, force, Time.fixedDeltaTime / 10f);
        Debug.DrawLine(pos, pos + offset, Color.green, Time.fixedDeltaTime * 2);
        pos += offset;
    }
}

// Old version
// Vector3 deltaPosition = nextPosition - _rigidbody.position;
// Vector3 velocity = deltaPosition / t;
// SetPhysicsState(_rigidbody.position, velocity, time);

//Applying results
SetPhysicsState(startPos, v0, time);
_rigidbody.AddForce(force / 2f, ForceMode.Acceleration);

我正在使用我的物理助手类

public static class PhysicsTools
{

    public static Vector3 GetMovement(Vector3 velocity, Vector3 force, float time)
    {
        return (velocity * time) + 0.5f * force * (time * time);
    }

    public static Vector3 GetMovementUpdateVelocity(ref Vector3 velocity, Vector3 force, float time)
    {
        Vector3 ret = (velocity * time) + 0.5f * force * (time * time);
        velocity += force * time;

        return ret;
    }
}

一切正常,但当且仅当我在应用时将力除以二。我自己使用 PhysicsTools 进行的模拟不需要这种篡改。

这是我的一个测试的图片,力因数同时应用于物理引擎和 PhysicsTools 模拟。您可以看到模拟的线向远处飞去,但实际的弹丸却没有,它应该保持在其怪异的五角星中。

在这里我们可以看到它按预期工作(仍然减少了施加的力)

我的问题,为什么我需要将这该死的力量分成两份?

【问题讨论】:

  • 您可以简单地将速度设置在相应的方向上(每次都在矢量中心上呈 90°->bullet),而不是使用力将子弹保持在圆形轨道上
  • 也许这有帮助:Rigidbody circular movement
  • @derhugo 由于框架不精确,这将创建一个螺旋,而不是一个圆圈。无论如何,代码工作正常,除了提高帧率之外,我的问题没有真正的解决方案。

标签: c# unity3d linear-algebra physics


【解决方案1】:

好吧,当你做出假设时会发生什么。

我假设ForceMode.Continuous 意味着力将通过框架连续施加。事实并非如此。

unity 物理引擎无法进行任何类型的连续加速或抛物线投射。任何物体都沿直线移动,AddForce 只是在当时和那里修改速度。

事实证明,简单地将力除以 2 就足以将起始速度重置为我之前对该问题的线性解决方案,而物体似乎在多边形之外做出反应的唯一原因是我的子弹对撞机太大了比我想象的要宽。

请阅读这篇文章了解更多信息:https://answers.unity.com/questions/696068/difference-between-forcemodeforceaccelerationimpul.html

解决该问题的唯一方法是提高物理帧速率,或者使用您自己的光线投射解决方案,这会带来一系列其他问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 2019-05-05
    • 1970-01-01
    相关资源
    最近更新 更多