相对于其始终面对的点移动对象会导致螺旋形轨道 [英] Move object relative to a point it is always facing causes spiraling orbit

查看:55
本文介绍了相对于其始终面对的点移动对象会导致螺旋形轨道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing a simulation where player should be able to move around inside a 2D circle (referred to as sphere in my code). The players movement must be relative to the center of the circle.

我的第一步是确保玩家始终面对中心.我的工作很好.但是,当我尝试进行相对运动时,并没有得到我想要的结果.

My first step was to make sure the player always faces the center. I got the working fine. However when I tried to do the relative movement it doesn't give my quite the result I'm looking for.

当我将播放器移动到靠近圆心的位置并向侧面移动时(相对于播放器的朝向向量),播放器围绕中心旋转,然后慢慢开始向外盘旋.向外的螺旋线在中心附近更加明显,大约需要8个轨道才能到达圆的内边缘.相反,玩家应该在距中心一定距离的位置绕中心旋转.玩家为什么会向外盘旋?

When I move the player close to the center of circle and move sideways (which is relative to the player's facing vector), the player spins around the center but then slowly starts spiraling outwards. The outwards spiral is much more prominent near the center and takes about 8 orbits to reach the inner edge of the circle. Instead the player should be spinning around the center at a constant distance from the center. Why does the player spiral outwards?

这是我使用的代码:

// 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));

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

So I've tried a few things already:

  • 我认为这可能是一个四舍五入的问题.因此,我将relativeInputVector的X和Y舍入到小数点后第二位.没有帮助.
  • 我标准化了relativeInputVector向量.似乎没做多...
  • 我还认为也许我应该先移动然后旋转,而不是先旋转再移动.没用.
  • I thought it was maybe a rounding issue. So I rounded the relativeInputVector's X and Y to the 2nd decimal place. Didn't help.
  • I normalized the relativeInputVector vector. Didn't seem to do much...
  • I also thought maybe I should move and then rotate instead of rotate then move. Didn't work.

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

Now I'm thinking the issue is somewhere in the math (probably where I define relativeInputVector) but I can't find simular use cases regarding this so that I can compare and troubleshoot.

(当涉及我搜索的关键字时,这是一个相当饱和的主题)

推荐答案

如果直觉到一侧然后同时连续地调整正向向量的方向,则您的直觉会很有意义,但是这是交替进行和离散进行的.

Your intuition would make sense if you were moving to the side then adjusting the direction of your forward vector simultaneously and continuously, but it's being done alternating and discretely.

请考虑一下,如果 Time.deltaTime 对于一帧绝对很大,会发生什么情况.您将回避很多,甚至可能在一个方向离开屏幕,然后调整角度以面向圆心.那是一个夸张的例子,但是它确实是在小范围内发生的事情.

Consider what happens if Time.deltaTime was absolutely enormous for one frame. You would sidestep a huge amount, maybe even going off the screen in one direction, and then you would adjust your angle to face the center of the circle. That's an exaggerated example but its exactly what's happenening on a small scale.

下面的图显示了为什么您的代码呈螺旋形展开:

Here's a diagram showing why your code spirals out:

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

The way you're doing it, The angle between the circle's radius to the player's position at the beginning of the frame (A in the diagram) and the direction the rigidbody moves (1->2 in the diagram) is a right angle. At position 1, the radius A might be the correct distance, but the hypotenuse of a right triangle is always longer than each leg, so the new radius at position 2 (B) must be larger, and likewise, C must be larger than B.

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

The result of that is a spiral motion as you continue to accumulate length to your radius by switching from legs to hypotenuses of these right triangles.

基本上,为了使您的代码正常工作,您需要制作一个无限小的三角形- Time.deltaTime 需要无限地制作一个小三角形的直角三角形一条腿只是一条线,另一条腿和其斜边的长度相同.

Basically, in order for your code to work, you would need to be making infinitely small triangles--Time.deltaTime would need to be infinitely small--as a right triangle with one infinitely small leg is just a line, its other leg and its hypotenuse are the same length.

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

Of course if Time.deltaTime were infinitely small, the player would never move. ;) So, a different approach is needed:

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

Instead, we can calculate the player's angular velocity and then move the player according to that.

因此,首先请确定玩家距中心的新距离,然后再确定该半径范围内玩家绕圆行进的度数:

So, dirst determine the player's new distance from the center first, then how many degrees the player would travel around the circle at that radius:

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 确定使刚体在所需方向上向前/向上指向所需的旋转:

Then, rotate the player's new vertical position around the center point and set the player's rotation and position accordingly. You can use Quaternion.LookRotation to determine the rotation needed to make the rigidbody point forward/up in desired directions:

// 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因子的部分:

To remove a few calculations, you can include the part where you divide by 2 pi and multiply by 360f into the 20f factor:

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));

这篇关于相对于其始终面对的点移动对象会导致螺旋形轨道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆