微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将刚体运动限制为高速/尖锐曲线的样条

如何解决将刚体运动限制为高速/尖锐曲线的样条

我有一个 2.5d 平台游戏。角色在样条上使用刚体运动(使用曲线样条资产),该样条以各种方式弯曲到 3d 空间,而相机保持固定在侧面,以便您看到路径和背景转向,但保持 2d 侧面滚动透视。

我基本上是根据样条线创建一个外观旋转,然后使用该前向向量移动玩家,并确保移除垂直于路径的任何速度,以便玩家即使在弯曲时也能保持在路径的中心。我正在移除该向量上的速度,而不是将所有速度投射到路径方向,以便玩家仍然可以像正常一样跳跃和坠落。

void SetLookRotation()
{
    // get nearest TF and point on spline
    Vector3 p;
    mTF = Spline.GetNearestPointTF(transform.localPosition,out p);

    // Get forward and up vectors of point on spline
    _localHorizontal = Spline.GetTangentFast(mTF);
    _localVertical = Spline.GetorientationUpFast(mTF);

    // Set look rotation to path 
    transform.rotation = Quaternion.LookRotation(Vector3.Cross(_localHorizontal,_localVertical),_localVertical);
}

void Movement()
{
    Vector3 m = transform.right * groundacceleration * moveInput;
    rb.AddForce(RemoveCrossveLocity(m));

    rb.veLocity = RemoveCrossveLocity(rb.veLocity);

    Vector3 localVeLocity = transform.InverseTransformDirection(rb.veLocity);
    localVeLocity.z = 0;
    rb.veLocity = transform.TransformDirection(localVeLocity);
}

Vector3 RemoveCrossveLocity(Vector3 v)
{
    // get magnitude going in the cross product / perpindicular of localHorizontal and localVertical vector 
    // (essentially the magnitude on "local Z" or to the sides of the player)
    Vector3 crossveLocity = Vector3.Project(v,Vector3.Cross(transform.right,transform.up));

    // and remove it from the vector
    return v -= crossveLocity;
}

前 2 个函数按所示顺序在 FixedUpdate() 中发生。

问题是,当高速击中尖角时,一些惯性会导致玩家稍微偏离路径中心,并且很多动量变成向上动量,将玩家向上发射。最终玩家可以完全脱离路径(尽管我确实有一个作用于样条的自定义重力)。尽管如此,它在较低的速度下也能完美运行,即使在处理尖角时也是如此。至少据我所知。

我也尝试了一些来自 https://answers.unity.com/questions/205406/constraining-rigidbody-to-spline.html代码,但没有成功。

有没有办法可以将玩家刚体约束在不是全局 x/y/z 轴之一的向量上?我尝试了许多其他解决方案,例如将玩家的变换设置为样条曲线的中心,但我似乎无法在不感到非常生涩的情况下获得它。使用力使玩家“橡皮筋”来回靠近并越过中心。也许我的数学有问题。无论如何,我希望有人可以帮助我确保玩家始终停留在样条曲线的中心,但只停留在玩家面部方向两侧的向量上,这样它就不会与跳跃混淆。预先非常感谢您!

解决方法

对于潜在的未来访问者,我已经想到了这一点。有一些组件(如果您尝试进行基于样条的完整物理,还有更多组件,但只是从运动开始......)

首先我们必须定位我们的角色,以便我们的本地坐标系可以被transform.right等引用。幸运的是,这个包提供了这些返回有用向量的函数。我敢肯定,如果您要构建自己的样条系统,则我无法完成数学运算。

void SetLookRotation()
    {
        // get nearest TF and point on spline
        Vector3 p;
        playerTF = currentSpline.GetNearestPointTF(transform.localPosition,out p);

        // Get forward and up vectors of point on spline
        _localHorizontal = currentSpline.GetTangentFast(playerTF);
        _localVertical = currentSpline.GetOrientationUpFast(playerTF);

        // Set look rotation to path 
        transform.rotation = Quaternion.LookRotation(Vector3.Cross(_localHorizontal,_localVertical),_localVertical);
    }

这里我直接设置速度,但如果您使用力,原理相同。

if (Mathf.Abs(localVelocityAs_X) <= maxDashSpeed * Mathf.Abs(moveInput))
{
    Vector3 m = transform.right * maxDashSpeed * moveInput;
    rb.velocity = RemoveCrossVelocity(m);
}

localVelocityAs_X 定义为(在 fixedUpdate/物理步骤中运行):

float currLocalVelocityX = (playerTF - prevPositionX) / Time.deltaTime;
localVelocityAs_X = Mathf.Lerp(localVelocityAs_X,currLocalVelocityX,0.5f);
prevPositionX = playerTF;

playerTF 是您在样条曲线上的位置(在这种情况下,使用统一资产商店中的曲线样条包。这些样条位置返回非常小的浮点数,因此在我的情况下,我将 playerTF 乘以大约 10,000 以使其更容易可读指标)。这实质上只是通过将样条线上的当前位置与上一帧的位置进行比较,手动计算每帧玩家的速度。

RemoveCrossVelocity 同上。评论说明就足够了。

Vector3 RemoveCrossVelocity(Vector3 v)
    {
        // get magnitude going in the cross product / perpendicular of local horizontal and local vertical vectors 
        // (essentially the magnitude on "local Z" of the player)
        Vector3 crossVelocity = Vector3.Project(v,Vector3.Cross(transform.right,transform.up));

        // and remove it from the vector
        return v -= crossVelocity;
    }

终于解决了漂移问题。我的粗略修复基本上是将播放器调整到每帧样条线的中心。在水平方向上,没有变化,因为它抓住了最近的样条点,该点由该包计算为一种夹在样条起点和终点之间的浮点数。在垂直方向上,我们被设置为玩家在局部向上方向上与样条的距离 - 一种说我们根本没有垂直移动的奇特方式。必须这样做的原因是为了避免样条垂直位置覆盖玩家,而且我们显然不能在我们的本地坐标空间中将此向量设置回 playerPos.y,因此我们必须求助于使用方向向量 * 距离我们不断变化的地板。这在一天结束时并不是绝对理想的,但它可以工作,并且没有任何额外的抖动(在您的播放器的刚体上进行插值并且一些相机阻尼有帮助)。所有这些结合在一起,使玩家能够在具有物理特性的样条曲线的尖角附近快速加速,并且惯性永远不会导致玩家飞离或偏离中心。拿那个,火箭物理学!

void ResetPlayerToSpline()
    {
        Vector3 P; //closest spline point to player
        float pTf = currentSpline.GetNearestPointTF(transform.position,out P);

        playerHeight = Vector3.Distance(transform.position,P);

        transform.position = P + (transform.up * Vector3.Distance(transform.position,P));
    }

最终,对于那些可能希望在未来进行某种实现的人来说,您将遇到的最大问题是缺乏通常由游戏引擎提供的基本方向、面向全局的基于轴的函数和属性。对于入门,这里有一些我会使用的(不包括重力,它只是与你的向上向量乘以任何量级相反):

这个允许您像正常(理论上还有 z)一样使用 x 和 y 创建一个向量,并在您实际在本地空间中使用该向量时运行此函数来转换它。这样,您就不必尝试在没有名称的方向上思考。你仍然可以从 x 和 y 的角度来思考:

Vector3 ConvertWorldToLocalVector(Vector3 v)
    {
        Vector3 c;

        c = transform.right * v.x + transform.up * v.y;

        return c;
    }

这与 RemoveCrossVelocity() 中发生的情况基本相同,但重要的是要重申这是如何将方向上的速度设置为 0。第二部分展示了如何获得某个向量中的速度。

void Velocity_ZeroY()
    {
        rb.velocity -= GetLocalVerticalVelocity();
    } 

public Vector3 GetLocalVerticalVelocity() 
    {
        return Vector3.Project(rb.velocity,_localVertical);
    }

获取高度,因为您不能只比较 y 位置:

height = Vector3.Distance(transform.position,P);

我想这就是我能想到的所有好东西。我注意到游戏中创建基于样条的物理运动的资源严重缺乏,我现在猜测这是基于这样一个事实,即这是一项艰巨的任务。从那以后,我注意到游戏“Pandemonium”(1996 年)是一个基于曲线的 3d 样条横向滚动平台游戏 - 就像我的一样!主要的区别似乎是它根本不是基于物理学的,我不确定从我能看出它是否有音高变化和重力来赞美。希望有一天这对某人有所帮助,并感谢那些为讨论做出贡献的人。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。