Як я можу обмежити рух гравця до поверхні 3D-об’єкта за допомогою Unity?


16

Я намагаюся створити ефект, схожий на Маріо Галактику або Геометрію Війни 3, коли гравець ходить по «планеті», гравітація, здається, підлаштовується, і вони не падають на край об'єкта, як це було б, якщо б гравітація була зафіксована в одному напрямку.

введіть тут опис зображення
(джерело: gameskinny.com )

Війни геометрії 3

Мені вдалося реалізувати щось близьке до того, що я шукаю, використовуючи підхід, коли об’єкт, який повинен мати силу тяжіння, притягує до нього інші жорсткі тіла, але за допомогою вбудованого фізичного двигуна для Unity, застосовуючи рух за допомогою AddForce тощо, Я просто не міг змусити рух відчути себе правильно. Я не міг змусити гравця рухатись досить швидко, без того, щоб гравець почав злітати з поверхні об'єкта, і я не міг знайти хороший баланс прикладеної сили та сили тяжіння, щоб пристосувати це. Моя нинішня реалізація - це адаптація того, що було знайдено тут

Я відчуваю, що рішення, ймовірно, все-таки використовуватиме фізику для того, щоб гравець опинився на об'єкті, якби вони покинули поверхню, але після того, як гравець буде заземлений, з’явиться спосіб вирвати гравця на поверхню і вимкнути фізику і керуйте гравцем іншими засобами, але я дійсно не впевнений.

Який підхід я повинен застосувати, щоб притиснути програвача до поверхні предметів? Зауважте, що рішення повинно працювати в тривимірному просторі (на відміну від 2D) і його можна реалізовувати за допомогою безкоштовної версії Unity.



Я навіть не думав шукати ходи по стінах. Я погляну, і чи допоможуть вони.
SpartanDonut

1
Якщо це питання стосується саме цього в 3D в Unity, його слід зробити більш зрозумілим з правками. (Тоді це не буде точним дублікатом існуючого .)
Анко

Це теж моє загальне відчуття - я буду бачити, чи зможу я адаптувати це рішення до 3D і розмістити його як відповідь (або якщо хтось інший міг бити мене до удару, я теж добре з цим). Я спробую і оновити своє питання, щоб бути більш зрозумілим щодо цього.
SpartanDonut

Потенційно корисно: youtube.com/watch?v=JMWnufriQx4
ssb

Відповіді:


7

Мені вдалося здійснити те, що мені потрібно, насамперед за допомогою цього допису в блозі, що розкриває поверхню частину головоломки і придумав власні ідеї щодо руху гравця та камери.

Прив’язання програвача до поверхні об'єкта

Основна установка складається з великої сфери (світ) і меншої сфери (гравця), обох із прикріпленими до них сферичними колідерами.

Основна частина проведеної роботи полягала в таких двох методах:

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

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

Для реалізації більш цікавої камери, але елементи керування приблизно такі ж, як у нас, ви можете легко реалізувати камеру, яка не відображається, або просто інший манекенний об'єкт, щоб базувати рух, а потім використовувати більш цікаву камеру, щоб зробити що ви хочете, щоб гра виглядала так. Це дозволить приємно переходити камеру, коли ви обходите об'єкти, не порушуючи керування.


3

Я думаю, що ця ідея спрацює:

Зберігайте копію планетарної сітки на стороні процесора. Мати сітчасті вершини також означає, що у вас є нормальні вектори для кожної точки планети. Потім повністю відключіть гравітацію для всіх сутностей, замість цього застосуйте силу в прямо протилежному напрямку від нормального вектора.

Тепер, на основі якої точки слід обчислювати цей нормальний вектор планети?

Найпростіша відповідь (за якою я впевнений, що це буде добре) - наблизити аналогічно до методу Ньютона : Коли об'єкти вперше нерестуються, ви знаєте всі їх початкові положення на планеті. Використовуйте це початкове положення для визначення upвектора кожного об'єкта . Очевидно, що гравітація буде знаходитись у зворотному напрямку (у напрямку down). У наступному кадрі перед тим, як застосувати гравітацію, киньте промінь від нового положення об'єкта до його старого downвектора. Використовуйте перетин цього променя з планетою як нове орієнтир для визначенняup вектора. Рідкісний випадок попадання променя нічого не означає, що щось жахливо пішло не так, і ви повинні перенести свій об’єкт туди, де він був у попередньому кадрі.

Також зауважте, що, використовуючи цей метод, чим далі походить гравець від планети, тим гіршим стає наближення. Тому краще використовувати десь біля ніг кожного гравця як їх походження. Я здогадуюсь, але я думаю, що використання ніг як джерела також призведе до більш легкого керування та навігації гравця.


Останнє зауваження: для кращих результатів ви можете навіть зробити наступне: слідкувати за рухом гравця у кожному кадрі (наприклад, за допомогою current_position - last_position). Потім затисніть, movement_vectorщоб довжина до об'єкта upдорівнювала нулю. Назвемо цей новий вектор reference_movement. Перемістити попередній reference_pointна reference_movementі використовувати цю нову точку як трасування променів походження. Після (і якщо) промінь потрапить на планету, перемістіться reference_pointдо цієї точки влучення. Нарешті, обчисліть новий upвектор, з цього нового reference_point.

Деякі псевдокоди, щоб підсумувати його:

update_up_vector(player:object, planet:mesh)
{
    up_vector        = normalize(planet.up);
    reference_point  = ray_trace (object.old_position, -up_vector, planet);
    movement         = object.position - object.old_position;
    movement_clamped = movement - up_vector * dot (movement, up_vector);

    reference_point  += movement_clamped;
    reference_point  = ray_trace (reference_point, -up_vector, planet);
    player.up        = normal_vector(mesh, reference_point);
}

2

Ця публікація може бути корисною. Його суть полягає в тому, що ви не використовуєте контролери символів, а робите свої власні, використовуючи двигун фізики. Потім ви використовуєте нормали, виявлені під програвачем, щоб орієнтувати їх на поверхню сітки.

Ось приємний огляд техніки. Існує набагато більше ресурсів з пошуковими термінами в Інтернеті на кшталт "прогулянка єдністю на 3d-об'єктах mario galaxy".

Також є демонстраційний проект від юникс 3.x, який мав ходити з двигуном, з солдатом і собакою, і в одній із сцен. Він продемонстрував ходьбу на 3d-об'єктах Galaxy -style. Він називається рухомою системою руху.


1
На перший погляд, демонстрація працює не дуже добре, і в пакет Unity є помилки в побудові. Я побачу, чи зможу я зробити цю роботу, але буде вдячна більш повна відповідь.
SpartanDonut

2

Обертання

Ознайомтесь з відповіддю на це запитання на answer.unity3d.com (насправді запитав сам). Цитата:

Перше завдання - отримати вектор, який визначає. Зі свого малюнка це можна зробити одним із двох способів. Ви можете ставитися до планети як до сфери та використовувати (object.position - planet.position). Другий спосіб - використовувати Collider.Raycast () та використовувати 'hit.normal', який він повертає.

Ось код, який він мені запропонував:

var up : Vector3 = transform.position - planet.position;
transform.rotation = Quaternion.FromToRotation(transform.up, up) * transform.rotation;

Зателефонуйте до кожного оновлення, і ви маєте працювати. (зауважте, що код знаходиться в UnityScript ).
Той самий код у C # :

Vector3 up = transform.position - planet.position;
transform.rotation = Quaternion.FromToRotation(transform.up, up) * transform.rotation;

Гравітація

Для сили тяжіння ви можете використовувати мою "техніку фізики планети", яку я описав у своєму питанні, але вона не дуже оптимізована. Це було щось, що я придумав.
Я пропоную вам створити власну систему для тяжіння.

Ось підручник Youtube Себастьяна Лаге . Це дуже добре працює рішення.

EDIT: У єдності перейдіть до Правки > Налаштування проекту > Фізика та встановіть усі значення для Gravity на 0 (Або видаліть Жорстке тіло з усіх об'єктів), щоб запобігти конфлікту з вбудованою силою тяжіння (яка просто тягне програвача вниз) нестандартне рішення. (вимкнути)


Гравітація гравця до центру планети (і обертання їх відповідно) працює добре для майже сферичних планет, але я можу уявити, що це дійсно не так для менш сферичних об'єктів, як, наприклад, ракетна форма Маріо стрибає в першому GIF.
Анко

Ви можете створити куб, який обмежується лише переміщенням по осі X, наприклад, всередині несферичного об'єкта, і переміщати його, коли гравець рухається, так що він завжди знаходиться прямо під програвачем, а потім витягніть плеєр до куба. Спробуй це.
Даніель Квіст
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.