Ваша интуиция имела бы смысл, если бы вы двигались в сторону, а затем настраивали направление вашего прямого вектора одновременно и непрерывно, но это делается поочередно и дискретно.
Подумайте, что произойдет, если Time.deltaTime
был абсолютно огромен для одного кадра.Вы бы отошли в сторону от огромного количества, возможно, даже отошли от экрана в одном направлении, а затем отрегулировали бы свой угол к центру круга.Это преувеличенный пример, но это именно то, что происходит в небольшом масштабе.
Вот диаграмма, показывающая, почему ваш код раскручивается:
То, как вы это делаете, угол между радиусом круга и положением игрока в начале кадра (A на диаграмме) и направлением движения твердого тела (1-> 2 на диаграмме) - это прямой угол,В позиции 1 радиус A может быть правильным расстоянием, но гипотенуза прямоугольного треугольника всегда длиннее каждой ноги, поэтому новый радиус в позиции 2 (B) должен быть больше, а также C должен быть больше B.
Результатом этого является спиральное движение, когда вы продолжаете накапливать длину до своего радиуса, переключаясь с ног на гипотенузы этих прямоугольных треугольников.
По сути, для того, чтобы ваш код работал, вам нужно создавать бесконечно маленькие треугольники - Time.deltaTime
должен быть бесконечно маленьким - так как прямоугольный треугольник с одной бесконечно маленькой ногой - это простолиния, другая нога и гипотенуза имеют одинаковую длину.
Конечно, если бы Time.deltaTime
были бесконечно малы, игрок никогда бы не пошевелился.;) Итак, нужен другой подход:
Вместо этого мы можем вычислить угловую скорость игрока и затем перемещать игрока в соответствии с этим.
Итак, сначала определите новое расстояние игрока от центра, а затем, на сколько градусов игрок будет перемещаться по кругу в этом радиусе:
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 пи иумножить на 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));