Перемещение объекта относительно точки, с которой он всегда сталкивается, вызывает вращающуюся орбиту - PullRequest
2 голосов
/ 01 июня 2019

Я разрабатываю симуляцию, в которой игрок должен иметь возможность перемещаться внутри 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));

Итак, я уже попробовал несколько вещей:

  • Я думал, что это может быть проблема округления.Таким образом, я округлил значения X и Y в относительном входном векторе до второго десятичного знака.Не помогло.
  • Я нормализовал векторlativeInputVector.Похоже, ничего особенного ...
  • Я также подумал, что, может быть, мне следует двигаться, а затем вращаться вместо вращения, а затем двигаться.Не сработало.

Теперь я думаю, что проблема где-то в математике (вероятно, где я определяю относительныйInputVector), но я не могу найти симулированные варианты использования относительно этого, чтобы я мог сравнить иустранения неполадок.

(это довольно насыщенная тема, когда речь идет о ключевых словах, по которым я ищу)

1 Ответ

3 голосов
/ 01 июня 2019

Ваша интуиция имела бы смысл, если бы вы двигались в сторону, а затем настраивали направление вашего прямого вектора одновременно и непрерывно, но это делается поочередно и дискретно.

Подумайте, что произойдет, если 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 пи иумножить на 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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...