c# - 相对于它始终面对的点移动对象会导致螺旋轨道

标签 c# unity3d math

我正在开发一个模拟,玩家应该能够在 2D 圆圈(在我的代码中称为球体)内四处移动。玩家的移动必须相对于圆心。

我的第一步是确保玩家始终面向中心。我工作正常。但是,当我尝试进行相对运动时,它并没有给我想要的结果。

当我将玩家移动到靠近圆心并向侧面移动(相对于玩家的面向向量)时,玩家会围绕圆心旋转,然后慢慢开始向外盘旋。向外的螺旋在中心附近更为突出,需要大约 8 个轨道才能到达圆的内边缘。 相反,玩家应该以与中心保持恒定距离的方式围绕中心旋转。为什么播放器会向外旋转?

这是我使用的代码:

// center of the sphere
Vector3 center = sphereComponent.transform.position - player.transform.position;

// always rotate towards the center so that transform.up is
float angle = Vector3.Angle(center, Vector3.up);
float sign = (center.x < rigidbody.transform.position.x) ? 1.0f : -1.0f;
rigidbody.MoveRotation(angle * sign);

// use the input vector to calculate a vector relative to the objects right and up vectors
Vector2 relativeInputVector =
        (rigidbody.transform.right * player.InputVector.x) +
        (rigidbody.transform.up * player.InputVector.y);

// below is same as doing: rigidbody += relativeInputVector.normalized * 20 * Time.deltaTime;
rigidbody.MovePosition(rigidbody.position + (relativeInputVector.normalized * 20 * Time.deltaTime));

所以我已经尝试了一些事情:

  • 我认为这可能是一个舍入问题。所以我将 relativeInputVector 的 X 和 Y 舍入到小数点后第二位。没有帮助。
  • 我标准化了 relativeInputVector 向量。似乎没有做太多...
  • 我还想也许我应该移动然后旋转而不是旋转然后移动。没用。

现在我认为问题出在数学上的某个地方(可能是我定义 relativeInputVector 的地方),但我找不到与此相关的类似用例,因此我可以比较和排除故障。

(就我搜索的关键字而言,这是一个相当饱和的话题)

最佳答案

如果您向一侧移动,然后同时且连续地调整前向矢量的方向,那么您的直觉是有道理的,但它是交替和离散地进行的。

考虑一下如果 Time.deltaTime 对于一帧来说绝对是巨大的会发生什么。你会回避很多,甚至可能朝一个方向离开屏幕,然后你会调整你的角度以面对圆心。这是一个夸张的例子,但这正是小范围内发生的事情。

这是一张图表,显示了为什么您的代码螺旋式上升:

What your code does

你这样做的方式,圆的半径与玩家在帧开始位置的位置(图中的 A)和刚体移动的方向(图中的 1->2)之间的角度是直角。在位置 1,半径 A 可能是正确的距离,但直角三角形的斜边总是比每条边都长,因此位置 2 (B) 的新半径必须更大,同样,C 必须大于 B .

结果是螺旋运动,因为您通过从这些直角三角形的边切换到斜边,继续将长度累积到您的半径。

基本上,为了让您的代码正常工作,您需要制作无限小的三角形——Time.deltaTime 需要无限小——作为一个直角三角形,其中一个无限小腿只是一条线,它的另一条腿和它的斜边是一样长的。

当然,如果 Time.deltaTime 无限小,玩家将永远不会移动。 ;) 因此,需要一种不同的方法:

an angular velocity approach

相反,我们可以计算玩家的角速度,然后据此移动玩家。

因此,首先确定玩家与中心的新距离,然后玩家将以该半径绕圆圈行进多少度:

Vector3 sphereCenterPoint = sphereComponent.transform.position

Vector3 playerToCenter = sphereCenterPoint  - player.transform.position;

float playerVerticalSpeed = 20f * player.InputVector.normalized.y;

newVerticalPosition = rigidbody.position + playerToCenter.normalized 
                                         * playerVerticalSpeed * Time.deltaTime;

playerToCenter = sphereComponent.transform.position - newVerticalPosition;

float circumferenceOfPlayerPath = 2f * playerToCenter.magnitude * Mathf.PI;

float playerHorizontalSpeed = 20f * player.InputVector.normalized.x;

float degreesTraveled = ( playerHorizontalSpeed * Time.deltaTime / circumferenceOfPlayerPath ) * 360f;

然后,围绕中心点旋转玩家的新垂直位置,并相应地设置玩家的旋转和位置。您可以使用 Quaternion.LookRotation 来确定使刚体指向所需方向向前/向上所需的旋转:

// rotates newVerticalPosition around sphereCenterPoint by degreesTraveled around z axis
Vector3 newPosition = Quaternion.Euler(0f,0f, degreesTraveled) 
                     * (newVerticalPosition - sphereCenterPoint ) + sphereCenterPoint;

rigidbody.MovePosition(newPosition); 

rigidbody.MoveRotation(
        Quaternion.LookRotation(Vector3.forward, sphereCenterPoint - newPosition));

要删除一些计算,您可以将除以 2 pi 并乘以 360f 的部分包含在 20f 因子中:

Vector3 sphereCenterPoint = sphereComponent.transform.position

Vector3 playerToCenter = sphereCenterPoint  - player.transform.position;

float playerVerticalSpeed = 20f * player.InputVector.normalized.y;

newVerticalPosition = rigidbody.position + playerToCenter.normalized 
                                         * playerVerticalSpeed * Time.deltaTime;

playerToCenter = sphereComponent.transform.position - newVerticalPosition;

float playerHorizontalSpeed = 1146f * player.InputVector.normalized.x;

float degreesTraveled = playerHorizontalSpeed * Time.deltaTime / playerToCenter.magnitude;

// rotates newVerticalPosition around sphereCenterPoint by degreesTraveled around z axis
Vector3 newPosition = Quaternion.Euler(0f,0f, degreesTraveled) 
                     * (newVerticalPosition - sphereCenterPoint ) + sphereCenterPoint;

rigidbody.MovePosition(newPosition); 

rigidbody.MoveRotation(
        Quaternion.LookRotation(Vector3.forward, sphereCenterPoint - newPosition));

关于c# - 相对于它始终面对的点移动对象会导致螺旋轨道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56403327/

相关文章:

c# - 我可以在 EF 中以良好的方式获取列表吗

unity3d - 如何在运行时向 Unity Tilemap 添加可编写脚本的磁贴?

c# - 如何使用自定义宽高比统一构建项目?

c++ - GraphicsMagick.NET 缺少 CORE_RL_bzlib_.lib 文件,C++ 链接器错误?

c# - 如何在磁盘上创建文件

c# - 使用经过验证的 JWT 进行图形身份验证

math - 生成一系列随机 6 位数字,避免潜在的因一位或换位错误

c - 递归斐波那契程序的时间复杂度

c# - 跨不同 ASP.NET 请求的静态属性

javascript - JavaScript 中的正数到负数?