Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/281.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在不依赖固定时间步长的情况下,随时间插值二维空间的角度_C#_Xna_Interpolation_Angle - Fatal编程技术网

C# 在不依赖固定时间步长的情况下,随时间插值二维空间的角度

C# 在不依赖固定时间步长的情况下,随时间插值二维空间的角度,c#,xna,interpolation,angle,C#,Xna,Interpolation,Angle,问题 在给定时间增量的情况下,如何在两个给定角度之间进行插值,以便当算法以不同频率运行时,来自旋转a或旋转B的模拟运动将花费相似的时间量(没有固定的时间步长相关性) 潜在解决方案 我一直在使用下面的C代码在两点之间进行这种插值。它解决了这种情况下的差异: Vector3 SmoothLerpVector3(Vector3 x0, Vector3 y0, Vector3 yt, double t, float k) { // x0 = current position // y0

问题

在给定时间增量的情况下,如何在两个给定角度之间进行插值,以便当算法以不同频率运行时,来自旋转a或旋转B的模拟运动将花费相似的时间量(没有固定的时间步长相关性)

潜在解决方案

我一直在使用下面的C代码在两点之间进行这种插值。它解决了这种情况下的差异:

Vector3 SmoothLerpVector3(Vector3 x0, Vector3 y0, Vector3 yt, double t, float k)
{
    // x0 = current position
    // y0 = last target position
    // yt = current target position
    // t = time delta between last and current target positions
    // k = damping

    Vector3 value = x0;

    if (t > 0)
    {
        Vector3 f = x0 - y0 + (yt - y0) / (k * (float)t);
        value = yt - (yt - y0) / (k * (float)t) + f * (float)Math.Exp(-k * t);
    }

    return value;            
}
通过将
Vector3
Z
坐标设置为0,此代码可用于二维坐标

“最后”和“当前”位置是因为目标可以在插值过程中移动。不考虑这一点会导致中等高速下的运动抖动

我没有写这段代码,它似乎可以工作。对于角度,我很难改变这一点,因为,例如,350°和10°之间的插值将采用“长”方式,而不是沿着20°角度差的方向进行

我已经研究了四元数slerp,但还没有找到一个考虑时间增量的实现。我已经想到了一个方法,但也无法实现,就是在两个角度之间插值两次,但第二次是在每个角度上相差180°,然后输出两个角度中较小的一个乘以-1


如有任何帮助或指导,将不胜感激

我以前做的方法是测试两个角度之间的差值是否大于180°,如果大于180°,则在较小的值上加上360°,然后使用这两个角度值进行处理。因此,在您的示例中,不是在350°和10°之间插值,而是在350°和370°之间插值。如果需要显示结果,您可以始终对其进行360度模化。

我以前的方法是测试两个角度之间的差值是否大于180°,如果大于180°,则将较小的值加上360°,然后使用这两个角度值进行处理。因此,在您的示例中,不是在350°和10°之间插值,而是在350°和370°之间插值。如果需要显示结果,您始终可以对其进行360度模化。

使用
Slerp()
并确保使用这些辅助函数之一包装-π和π之间的角度

    /// <summary>
    /// Wraps angle between -π and π
    /// </summary>
    /// <param name="angle">The angle</param>
    /// <returns>A bounded angle value</returns>
    public static double WrapBetweenPI(this double angle)
    {
        return angle+(2*Math.PI)*Math.Floor((Math.PI-angle)/(2*Math.PI));
    }

    /// <summary>
    /// Wraps angle between -180 and 180
    /// </summary>
    /// <param name="angle">The angle</param>
    /// <returns>A bounded angle value</returns>
    public static double WrapBetween180(this double angle)
    {
        return angle+360*Math.Floor((180-angle)/360);
    }
//
///-π和π之间的夹角
/// 
///角度
///有界角值
公共静态双WrapBetweenPI(此双角度)
{
返回角度+(2*Math.PI)*数学楼层((Math.PI角度)/(2*Math.PI));
}
/// 
///缠绕角度介于-180和180之间
/// 
///角度
///有界角值
180(此双角度)之间的公共静态双包装
{
返回角+360*数学地板((180角)/360);
}
注意:使用
Slerp()
的相关帖子,并确保使用其中一个辅助函数包装-π和π之间的角度

    /// <summary>
    /// Wraps angle between -π and π
    /// </summary>
    /// <param name="angle">The angle</param>
    /// <returns>A bounded angle value</returns>
    public static double WrapBetweenPI(this double angle)
    {
        return angle+(2*Math.PI)*Math.Floor((Math.PI-angle)/(2*Math.PI));
    }

    /// <summary>
    /// Wraps angle between -180 and 180
    /// </summary>
    /// <param name="angle">The angle</param>
    /// <returns>A bounded angle value</returns>
    public static double WrapBetween180(this double angle)
    {
        return angle+360*Math.Floor((180-angle)/360);
    }
//
///-π和π之间的夹角
/// 
///角度
///有界角值
公共静态双WrapBetweenPI(此双角度)
{
返回角度+(2*Math.PI)*数学楼层((Math.PI角度)/(2*Math.PI));
}
/// 
///缠绕角度介于-180和180之间
/// 
///角度
///有界角值
180(此双角度)之间的公共静态双包装
{
返回角+360*数学地板((180角)/360);
}

注意事项:解决方案的相关帖子

我有一些使用四元数的工作代码。为了考虑时间步长(消除对固定步长更新的依赖),使用
amount=1-Math.Exp(-k*t)
计算slerp/lerp金额。常数
k
影响阻尼(1-非常缓慢,20-几乎瞬间捕捉到目标)

我决定不尝试在3D上使用它,因为我正在开发2D游戏

public static float SlerpAngle(
    float currentAngle, float targetAngle, double t, float k)
{
    // No time has passed, keep angle at current
    if (t == 0)
        return currentAngle;

    // Avoid unexpected large angles
    currentAngle = MathHelper.WrapAngle(currentAngle);
    targetAngle = MathHelper.WrapAngle(targetAngle);

    // Make sure the shortest path between 
    // current -> target doesn't overflow from
    // -pi -> pi range otherwise the 'long
    // way round' will be calculated
    float difference = Math.Abs(currentAngle - targetAngle);
    if (difference > MathHelper.Pi)
    {
        if (currentAngle > targetAngle)
        {
            targetAngle += MathHelper.TwoPi;
        }
        else
        {
            currentAngle += MathHelper.TwoPi;
        }
    }

    // Quaternion.Slerp was outputing a close-to-0 value 
    // when target was in the range (-pi, 0). Ensuring
    // positivity, halfing difference between current
    // and target then doubling result before output 
    // solves this. 
    currentAngle += MathHelper.TwoPi;
    targetAngle += MathHelper.TwoPi;
    currentAngle /= 2;
    targetAngle /= 2;

    // Calculate spherical interpolation
    Quaternion qCurrent = Quaternion.CreateFromAxisAngle(
        Vector3.UnitZ, currentAngle);
    Quaternion qTarget = Quaternion.CreateFromAxisAngle(
        Vector3.UnitZ, targetAngle);
    Quaternion qResult = Quaternion.Slerp(
        qCurrent, qTarget, (float)(1 - Math.Exp(-k * t)));

    // Double value as above
    float value = 2 * 2 * (float)Math.Acos(qResult.W);

    return value;
}

转速在5Hz->1000Hz范围内保持一致。我认为这些是合适的极端。没有真正的理由使运行频率高于60Hz。

解决方案

我有一些使用四元数的工作代码。为了考虑时间步长(消除对固定步长更新的依赖),使用
amount=1-Math.Exp(-k*t)
计算slerp/lerp金额。常数
k
影响阻尼(1-非常缓慢,20-几乎瞬间捕捉到目标)

我决定不尝试在3D上使用它,因为我正在开发2D游戏

public static float SlerpAngle(
    float currentAngle, float targetAngle, double t, float k)
{
    // No time has passed, keep angle at current
    if (t == 0)
        return currentAngle;

    // Avoid unexpected large angles
    currentAngle = MathHelper.WrapAngle(currentAngle);
    targetAngle = MathHelper.WrapAngle(targetAngle);

    // Make sure the shortest path between 
    // current -> target doesn't overflow from
    // -pi -> pi range otherwise the 'long
    // way round' will be calculated
    float difference = Math.Abs(currentAngle - targetAngle);
    if (difference > MathHelper.Pi)
    {
        if (currentAngle > targetAngle)
        {
            targetAngle += MathHelper.TwoPi;
        }
        else
        {
            currentAngle += MathHelper.TwoPi;
        }
    }

    // Quaternion.Slerp was outputing a close-to-0 value 
    // when target was in the range (-pi, 0). Ensuring
    // positivity, halfing difference between current
    // and target then doubling result before output 
    // solves this. 
    currentAngle += MathHelper.TwoPi;
    targetAngle += MathHelper.TwoPi;
    currentAngle /= 2;
    targetAngle /= 2;

    // Calculate spherical interpolation
    Quaternion qCurrent = Quaternion.CreateFromAxisAngle(
        Vector3.UnitZ, currentAngle);
    Quaternion qTarget = Quaternion.CreateFromAxisAngle(
        Vector3.UnitZ, targetAngle);
    Quaternion qResult = Quaternion.Slerp(
        qCurrent, qTarget, (float)(1 - Math.Exp(-k * t)));

    // Double value as above
    float value = 2 * 2 * (float)Math.Acos(qResult.W);

    return value;
}

转速在5Hz->1000Hz范围内保持一致。我认为这些是合适的极端。运行频率高于60Hz没有真正的原因。

当前位置、“最后一个目标位置”和“当前目标位置”是什么意思?在输入和输出方面,您希望实现什么样的行为?你能描述一些简单的测试用例吗,例如,在时间0时,输出应该等于一个输入?我将在中编辑它,为歧义道歉。“当前位置”、“最后一个目标位置”和“当前目标位置”是什么意思?在输入和输出方面,您希望实现什么样的行为?你能描述一些简单的测试用例吗,例如,在时间0时,输出应该等于一个输入吗?我将在中编辑它,为歧义道歉。我不能使用原生XNA方法,因为它们不考虑时间步长。你可以在线找到
Slerp()
的代码,然后自己实现它。也可以将角度步长调整为时间步长。这不像调整角度步长那么简单,在这种情况下,时间不是标量,至少我认为不是传统意义上的。我不能使用原生XNA方法,因为它们不考虑时间步长。你可以找到代码