如何使用Unity3d的内置物理引擎绘制弹丸轨迹?

时间:2016-06-01 23:13:03

标签: c# unity3d

我正在制作一款能够击退对物理力量作出反应的射弹的游戏。我在游戏中使用内置的Unity物理,但是我在射击之前绘制了射弹的轨迹。为此,我使用LineRenderer和一个简单的Physics类来计算位置:

public class SlingPhysics
{
    private Vector3 HalfGravity;
    private float ForceMultiplier;

    public void Init(Vector3 gravity, float slingForce, float mass, float drag)
    {
        HalfGravity = gravity / 2.0f;
        ForceMultiplier = slingForce / mass / (1 + drag);
    }

    public Vector3 GetPosition(Vector3 start, Vector3 direction, float timeDelta)
    {
        Vector3 pos = direction * (timeDelta * ForceMultiplier);
        pos.y += (HalfGravity.y * timeDelta * timeDelta);
        return start + pos;
    }

    /// <summary>
    /// Computes an array of Trajectory object positions by time.
    /// </summary>
    /// <returns>Number of positions filled into the buffer</returns>
    /// <param name="startPos">Starting Position</param>
    /// <param name="direction">Direction (and magnitude) vector</param>
    /// <param name="timeIncrement">Time increment of the samples</param>
    /// <param name="yFloor">Minimum height, below which is clipped</param>
    /// <param name="positions">Buffer to fill with positions</param>
    public int GetPositions(Vector3 startPos, Vector3 direction, float timeIncrement, float yFloor, Vector3[] positions)
    {
        int maxItems = positions.Length;
        int i = 0;
        for (; i < maxItems; i++)
        {
            Vector3 pos = GetPosition(startPos, direction, timeIncrement * i);
            if (pos.y < yFloor)
                break;
            positions[i] = pos;
        }
        return i;
    }
}

这很有效,并且(有些令人惊讶地)完全匹配射弹在发射时最终行进的实际轨迹,并由Unity物理引擎控制。

但是现在,我们决定将拖拽引入刚体,并且轨迹不再完美。我可以继续尝试改变我的物理模型以匹配Unity正在做的事情,但这似乎是一场失败的战斗。

有没有办法使用Unity Physics引擎预先计算和绘制轨迹?

如果没有,在Unity Physics中如何以数学方式实现拖动和角度拖动?

是否有Unity(5.3.4)物理实现的文档源?

2 个答案:

答案 0 :(得分:5)

  

我可以继续尝试改变我的物理模型以匹配Unity   正在做

不,不要这样做。每当Unity更新时,物理学的东西就会破坏。例如,Unity 4和5物理学不一样。不得不大量修改我的Unity 4物理代码以使其正常工作。

解决您问题的最简单方法是计算任何内容。我这样做的方法是创建两个射弹。 Projectile1 是可以看到和射击的主要射弹。 Projectile2 是隐藏的射弹,不能被玩家看到。

在拖动模式下,拍摄 Projectile2 (隐藏),然后存储它在List中经过的路径或位置(Vector3)。使用该存储的路径绘制Projectile Trajectory

您只需要拍摄 Projectile2 (隐藏)一次并存储路径。如果在拖动模式下,手指,鼠标或拖动移动到另一个位置,则必须清除 List,重新拍摄 Projectile2 (隐藏)一次再次,将位置存储到List,然后再次更新Projectile Trajectory

当释放手指时,您现在可以拍摄播放器可见的 Projectile1 (主射弹)。

如果您使用lineRenderer,则SetVertexCount的数量与List.Count的数量相同。通过循环List

中的位置,可以轻松绘制线条

无论Unity更新多少次,结果总是相同的。

答案 1 :(得分:3)

计算拖拽以匹配Unity引擎的方法是在FixedUpdate中计算并跟踪速度,然后更新Drag的速度。

这里提供了Drag的Unity公式: http://forum.unity3d.com/threads/drag-factor-what-is-it.85504/

这确实很简单;关键部分是:

velocity *= Mathf.Clamp01(1f - Drag * Time.fixedDeltaTime);

自定义Physics类必须重新设计,要求时间增量为Unity的Time.fixedDeltaTime(FixedUpdate调用之间的时间)

public class SlingPhysics
{
    private Vector3 Gravity;
    private float Force, Mass, Drag;

    public void Init(Vector3 gravity, float force, float mass, float drag)
    {
        Gravity = gravity;
        Force = force;
        Mass = mass;
        Drag = drag;
    }

    /// <summary>
    /// Computes an array of Trajectory object positions by time.
    /// </summary>
    /// <returns>Number of positions filled into the buffer</returns>
    /// <param name="startPos">Starting Position</param>
    /// <param name="direction">Direction (and magnitude) vector</param>
    /// <param name="yFloor">Minimum height, below which is clipped</param>
    /// <param name="positions">Buffer to fill with positions</param>
    public int GetPositions(Vector3 startPos, Vector3 direction, float yFloor, Vector3[] positions)
    {
        float timeIncrement = Time.fixedDeltaTime;

        int maxItems = positions.Length;
        int i = 0;
        Vector3 velocity = direction * Force / Mass;
        Vector3 pos = startPos;
        for (; i < maxItems; i++)
        {
            velocity += Gravity * timeIncrement;
            velocity *= Mathf.Clamp01(1f - Drag * timeIncrement);
            pos += velocity * timeIncrement;
            if (pos.y < yFloor)
                break;
            positions[i] = pos;
        }
        return i;
    }
}