Мне удалось выполнить то, что мне было нужно, в первую очередь с помощью этого поста в блоге для поверхностного осколка головоломки, и я предложил свои собственные идеи для движения игрока и камеры.
Привязка игрока к поверхности объекта
Базовая установка состоит из большой сферы (мир) и меньшей сферы (игрок) с прикрепленными к ним коллайдерами.
Основная часть выполняемой работы выполнялась следующими двумя методами:
private void UpdatePlayerTransform(Vector3 movementDirection)
{
RaycastHit hitInfo;
if (GetRaycastDownAtNewPosition(movementDirection, out hitInfo))
{
Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, float.PositiveInfinity);
transform.rotation = finalRotation;
transform.position = hitInfo.point + hitInfo.normal * .5f;
}
}
private bool GetRaycastDownAtNewPosition(Vector3 movementDirection, out RaycastHit hitInfo)
{
Vector3 newPosition = transform.position;
Ray ray = new Ray(transform.position + movementDirection * Speed, -transform.up);
if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity, WorldLayerMask))
{
return true;
}
return false;
}
Vector3 movementDirection
Параметр так же , как это звучит, направление мы будем двигаться нашим игроком в этом кадре, и вычисление этого вектора, а в конечном итоге относительно простой в этом примере, было немного сложно для меня , чтобы выяснить , в первую очередь. Подробнее об этом позже, но имейте в виду, что это нормализованный вектор в направлении, в котором игрок перемещает этот кадр.
Поэтапно, первое, что мы делаем, это проверяем, попадает ли луч, исходящий из гипотетической будущей позиции, направленной к вектору игрока вниз (-transform.up), с помощью WorldLayerMask, который является общедоступным свойством LayerMask скрипта. Если вы хотите более сложные столкновения или несколько слоев, вам придется создать собственную маску слоя. Если радиопередача ударит по чему-либо, hitInfo используется для извлечения нормальной точки и точки попадания, чтобы вычислить новую позицию и вращение игрока, которое должно быть прямо на объекте. Может потребоваться смещение позиции игрока в зависимости от размера и происхождения рассматриваемого объекта игрока.
Наконец, это действительно только проверено и, вероятно, хорошо работает только на простых объектах, таких как сферы. Как следует из поста в блоге, на котором я основываю свое решение, вы, вероятно, захотите выполнить несколько лучевых трансляций и усреднить их для вашей позиции и поворота, чтобы получить более приятный переход при движении по более сложной местности. Могут быть и другие подводные камни, о которых я не думал в данный момент.
Камера и Движение
Как только игрок прилип к поверхности объекта, следующей задачей было движение. Первоначально я начал с движения относительно игрока, но столкнулся с проблемами на полюсах сферы, где направления внезапно менялись, заставляя моего игрока быстро менять направление снова и снова, не позволяя мне когда-либо проходить полюса. То, что я делал, заставляло моих игроков двигаться относительно камеры.
Для моих нужд было хорошо иметь камеру, которая бы точно следовала за игроком, основываясь исключительно на положении игроков. В результате, несмотря на то, что камера технически вращалась, нажатие вверх всегда перемещало плеер к верхней части экрана, вниз к нижней части и т. Д. Влево и вправо.
Для этого на камере, где целевым объектом был игрок, было выполнено следующее:
private void FixedUpdate()
{
// Calculate and set camera position
Vector3 desiredPosition = this.target.TransformPoint(0, this.height, -this.distance);
this.transform.position = Vector3.Lerp(this.transform.position, desiredPosition, Time.deltaTime * this.damping);
// Calculate and set camera rotation
Quaternion desiredRotation = Quaternion.LookRotation(this.target.position - this.transform.position, this.target.up);
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, desiredRotation, Time.deltaTime * this.rotationDamping);
}
Наконец, чтобы переместить игрока, мы использовали преобразование основной камеры так, чтобы с нашими элементами управления вверх двигались вверх, вниз двигались вниз и т. Д. И именно здесь мы вызываем UpdatePlayerTransform, который привязывает нашу позицию к объекту мира.
void Update ()
{
Vector3 movementDirection = Vector3.zero;
if (Input.GetAxisRaw("Vertical") > 0)
{
movementDirection += cameraTransform.up;
}
else if (Input.GetAxisRaw("Vertical") < 0)
{
movementDirection += -cameraTransform.up;
}
if (Input.GetAxisRaw("Horizontal") > 0)
{
movementDirection += cameraTransform.right;
}
else if (Input.GetAxisRaw("Horizontal") < 0)
{
movementDirection += -cameraTransform.right;
}
movementDirection.Normalize();
UpdatePlayerTransform(movementDirection);
}
Чтобы реализовать более интересную камеру, но элементы управления должны быть примерно такими же, как у нас здесь, вы можете легко реализовать камеру, которая не визуализируется, или просто другой фиктивный объект, чтобы основывать движение, а затем использовать более интересную камеру для рендеринга, что Вы хотите, чтобы игра выглядела так. Это обеспечит хорошие переходы камеры при обходе объектов без нарушения управления.