将刚体运动限制为高速/急转弯时的样条曲线
Constrain rigidbody movement to spline at high speeds / sharp curves
我有一款 2.5d 平台游戏。角色在样条曲线上使用刚体运动(使用曲线样条曲线资产),样条曲线以各种方式弯曲成 3d space,而相机保持固定在一侧,以便您看到路径和背景转动,但是保持二维横向滚动视角。
我实际上是在基于样条曲线创建外观旋转,然后使用该前向矢量移动玩家,并确保移除垂直于路径的任何速度,以便玩家即使在弯曲时也能保持在路径中心.我正在移除该矢量上的速度,而不是将所有速度投射到路径方向,这样玩家仍然可以像正常情况一样跳跃和跌落。
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 等来引用。幸运的是这个包提供了这些 return 有用向量的函数。我敢肯定,如果您要构建自己的样条系统,我无法做到这一点。
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 定义为(固定更新/物理步骤中的运行):
float currLocalVelocityX = (playerTF - prevPositionX) / Time.deltaTime;
localVelocityAs_X = Mathf.Lerp(localVelocityAs_X, currLocalVelocityX, 0.5f);
prevPositionX = playerTF;
其中 playerTF 是您在样条曲线上的位置(在本例中,使用统一资产商店中的弯曲样条曲线包。这些样条曲线位置 return 非常小的浮点数,所以在我的例子中我将 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;
}
漂移终于解决了。我的粗略修复基本上是每帧将播放器调整到样条线的中心。在水平方向上,没有变化,因为它抓住了最近的样条点,该点由该程序包计算为夹在样条起点和终点之间的浮动。在垂直方向上,我们被设置为玩家在局部向上方向上与样条线的距离——一种奇特的说法,我们根本没有垂直移动。必须这样做的原因是为了避免样条垂直位置覆盖球员,我们显然不能在我们的本地坐标 space 中将这个向量设置回 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 创建一个向量,并且 运行 当您在本地 space 中实际使用该向量时,这个函数可以转换它.这样,您就不必尝试在没有名称的方向上思考。你仍然可以用 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 样条曲线的横向卷轴平台游戏 - 就像我的一样!主要区别似乎是它根本不是基于物理学的,而且我不确定它是否具有音高变化和引力来补充。希望有一天这对某人有所帮助,并感谢那些为讨论做出贡献的人。
我有一款 2.5d 平台游戏。角色在样条曲线上使用刚体运动(使用曲线样条曲线资产),样条曲线以各种方式弯曲成 3d space,而相机保持固定在一侧,以便您看到路径和背景转动,但是保持二维横向滚动视角。
我实际上是在基于样条曲线创建外观旋转,然后使用该前向矢量移动玩家,并确保移除垂直于路径的任何速度,以便玩家即使在弯曲时也能保持在路径中心.我正在移除该矢量上的速度,而不是将所有速度投射到路径方向,这样玩家仍然可以像正常情况一样跳跃和跌落。
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 等来引用。幸运的是这个包提供了这些 return 有用向量的函数。我敢肯定,如果您要构建自己的样条系统,我无法做到这一点。
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 定义为(固定更新/物理步骤中的运行):
float currLocalVelocityX = (playerTF - prevPositionX) / Time.deltaTime;
localVelocityAs_X = Mathf.Lerp(localVelocityAs_X, currLocalVelocityX, 0.5f);
prevPositionX = playerTF;
其中 playerTF 是您在样条曲线上的位置(在本例中,使用统一资产商店中的弯曲样条曲线包。这些样条曲线位置 return 非常小的浮点数,所以在我的例子中我将 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;
}
漂移终于解决了。我的粗略修复基本上是每帧将播放器调整到样条线的中心。在水平方向上,没有变化,因为它抓住了最近的样条点,该点由该程序包计算为夹在样条起点和终点之间的浮动。在垂直方向上,我们被设置为玩家在局部向上方向上与样条线的距离——一种奇特的说法,我们根本没有垂直移动。必须这样做的原因是为了避免样条垂直位置覆盖球员,我们显然不能在我们的本地坐标 space 中将这个向量设置回 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 创建一个向量,并且 运行 当您在本地 space 中实际使用该向量时,这个函数可以转换它.这样,您就不必尝试在没有名称的方向上思考。你仍然可以用 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 样条曲线的横向卷轴平台游戏 - 就像我的一样!主要区别似乎是它根本不是基于物理学的,而且我不确定它是否具有音高变化和引力来补充。希望有一天这对某人有所帮助,并感谢那些为讨论做出贡献的人。