Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / Unity Character Motor

Unity Character Motor

Автор:

Когда-то давно, еще во времена Unity 3, мне стало интересно как работает физика персонажа. И я заглянул в класс CharacterMotor. Класс был написан на JavaScript, был огромный, страшный и непонятный. Я решил переписать его на C#, попутно отрефакторив. Недавно я вспомнил про свой старый CharacterMotor, решил еще немного подправить его и поделиться им. Тем более, тема физики персонажа не очень популярная (я вообще не видел никакой информации), хотя довольно интересная.

1. Введение
2. CharacterController
3. CharacterMotor
4. CharacterMotor_Movement
5. CharacterMotor_Jumping
6. FPSInputController
7. MouseLook
8. Заключение

1. Введение

Я взял за основу CharacterMotor из Unity 3. В Unity 4 CharacterMotor не менялся, а вот в Unity 5, это абсолютно новый класс. Новый CharacterMotor, значительно уменьшился в коде, и видимо, и в функционале. Я не особо разбирался в нем и почти не использовал его, но заметил, что скольжение с крутого склона теперь не работает, и вообще качество кода мне не понравилось. Видимо писался он на скорую руку. Также Unity 5 перешел на новый PhysX 3, но в CharacterController я никаких изменений не заметил. Так что, думаю, мой CharacterMotor не устарел.

Character Motor | Unity Character Motor

2. CharacterController

Обычно персонаж не является обычным физическим объектом и работает по своим законам физики. В Unity для персонажа используется CharacterController. Это комбинация коллайдера в форме капсулы и метода Move. Метод Move двигает персонажа в указанную позицию, обрабатывая при этом коллизию. Обработка коллизии тут тоже не обычная, например, коллизия обрабатывается так, чтобы персонаж мог свободно подниматься на небольшие склоны, но не мог на большие, или auto stepping - фича, которая позволяет персонажу подниматься на маленькие препятствия. CharacterController не совместим с RigidBody, это значит, что он не может взаимодействовать с другими физическими объектами. Это создает некоторые проблемы: персонаж не может двигать другой физический объект, а другой объект не может сдвинуть персонажа. Кинематические объекты вообще свободно проходят сквозь персонажа, что делает проблематичным создание лифтов и движущихся платформ.

3. CharacterMotor

Вместо RigidBody мы должны использовать свой класс, в Unity это CharacterMotor. Хотя, CharacterMotor — это намного больше, чем RigidBody. CharacterMotor отвечает за любые движения нашего персонажа, например: ходьба, бег, скольжение, падение, прыжки, движение на платформе, на лифте, подъем по вертикальной лестнице, плавание. Все это реализуется, фактически, с помощью различных ухищрений, а не законов физики. Ведь персонажи обычно ведут себя не по законам, например: персонаж может немного управляться во время прыжка или падения, или просто резко останавливаться или менять направление движения. В Painkiller Дэниел мог ускоряться, просто прыгая без разбега. Конечно это все не сильно нарушает законов игровой физики, и можно было бы попытаться сделать максимально все физически корректно, но ведь проще просто ограничить скорость падения, чем вычислять сопротивление воздуха.

Я разделил мой CharacterMotor на 3 partial класса: CharacterMotor, CharacterMotor_Movement и CharacterMotor_Jumping. В оригинальном классе был еще класс отвечающий за движение на платформе, но я убрал его.

Базовая часть CharacterMotor реализует следующие:

  • Событие FixedUpdate, в котором вызываются все функции, необходимые для вычисления скорости. В Оригинальном классе можно было выбирать: использовать Update или FixedUpdate, но я убрал это т.к. движение игрока в FixedUpdate выглядит вполне плавно и требует меньше вычислительной нагрузки.
  • Событие OnControllerColliderHit, в котором определяется стоит ли персонаж на земле или нет. Здесь есть одна маленькая деталь, персонаж не может встать на землю, если он находится в состоянии прыжка и его скорость направлена вверх. Это нужно для того, чтобы когда игрок прыгает на крутой склон и касается его, то прыжок продолжался.
  • ApplyDownOffset и CancelDownOffset. Иногда персонаж может отрываться от земли. Чтобы предотвратить это, мы как бы применяем дополнительную гравитацию. Метод ApplyDownOffset смещает позицию персонажа вниз. Если после этого смещения персонаж все же не коснулся земли, то это смещение нужно отменить методом CancelDownOffset. Благодаря этому персонаж может с разбегу перейти на очень крутой склон, не оторвавшись от земли. Но можно поспорить хорошо это или плохо, ибо парой это выглядит не реалистично и странно. Возможно будет лучше уменьшить смещение.

+ Показать

4. CharacterMotor_Movement

Эта часть класса отвечает за движения и гравитацию, и реализует следующие:

  • ApplyMoving. Эта функция отвечает за ходьбу и скольжение по крутому склону. Кстати, двигаться персонаж может и находясь в воздухе. Движение реализуется не силами или импульсами, а прямым изменением скорости с помощью Vector3.MoveTowards. У этого подхода есть свои плюсы и минусы. Главный плюс - мы напрямую задаем нужную нам скорость. Другая особенность - автоматически реализуется сила трения и сила сопротивления воздуха, но плюс это или минус, затрудняюсь сказать т.к. при нормальной симуляции физики, силы суммируются, но в данной реализации, если персонаж уже имеет скорость и при этом еще и сам движется, то обе эти скорости не будут суммироваться. На мой взгляд эта функция - самое спорное место, т.к. по логике вещей, скольжению здесь не место, ведь скольжение происходит под действием гравитации, а не из-за движение игрока.
  • GetDesiredVelocity. Функция вычисляет желаемую скорость ходьбы или бега.
  • GetDirectionSpeedFactor. Когда игрок идет боком или задом, то его скорость должна быть меньше.
  • GetSlopeSpeedFactor. Когда игрок идет вверх по склону, то его скорость тоже уменьшается.
  • GetDesiredSlidingVelocity. Функция вычисляет желаемую скорость скольжения. Когда персонаж находится на крутом склоне, то он должен скользить вниз по склону, при этом игрок может немного управлять своим движением.
  • AdjustVelocityToGround. Выравнивание вектора скорости вдоль земли. Эта функция должна обеспечить лучшую соприкасаемость персонажа с землей. Но так как этим занимается ApplyDownOffset, то вероятно в этой функции нет никакого смысла.
  • ApplyBraking. Когда персонаж упирается в стену, то надо уменьшить его скорость, чтобы он не бежал вдоль стену, уперевшись в нее носом.
  • ApplyGravity. Тут все просто и ясно.

+ Показать

5. CharacterMotor_Jumping

Это последняя часть класса, ответственная за прыжок. Она реализует фактически один ApplyJumping, в котором есть пара особенностей:

  • Игрок может регулировать высоту прыжка, отпуская или зажимая кнопку.
  • Направление прыжка зависит от нормали земли. Причем можно задать степерь влияния земли на обычной поверхности и крутом склоне.

+ Показать

6. FPSInputController

Чтобы сделать полноценного, управляемого персонажа, нам нужно еще пара вспомогательных классов. Первый – обрабатывает ввод. Этот класс очень простой, но в нем есть две особенности:

  • Персонаж может прыгнуть, если кнопка прыжка была нажата в последние 0.2 секунды. Это сделано для того, чтобы игрок мог не ловить момент, когда персонаж коснется земли, а мог нажимать кнопку чуть раньше.
  • Используя аналоговые джойстики, игрок может регулировать скорость движения персонажа. Скорость с джойстика находится в промежутке от 0 до 1. Это скорость возводится в квадрат, чтобы игрок мог лучше управлять маленькой скоростью или что-то типа того. Т.е. если игрок сдвинул джойстик лишь на 0.5, то скорость будет 0.25. А если джойстик на значении 1, то и будет 1. Сам я это не тестировал, и не уверен удобно ли это вообще.

Кстати, FPS расшифровывается, по идее, как first person shooter.

+ Показать

7. MouseLook

Это последний и простейший класс, ответственный за вращение камеры. Точнее, камера вращается только по оси X, а по оси Y вращается весь персонаж.

+ Показать

8. Заключение

Я переписал эти юнити скрипты практически до неузнаваемости, что-то добавил, что-то удалил, но тем не менее принципиально ничего не изменилось. Оригинальные скрипты для Unity 4 и 5, вы можете посмотреть в корне репозитория. Код, надеюсь, я довел до нормального состояния. Но, как я сказал, принципиально ничего не изменилось, то некоторые вещи мне не нравятся. Также есть еще много вещей, которые можно было бы добавить. Поэтому я буду стараться обновлять репозиторий, и буду рад вашим комментариям и предложениям.

Заметил пару багов. Когда CharacterController стоит на сфере, то нормаль поверхности сферы направлена внутрь сферы. Из-за этого персонаж не понимает, что он стоит на земле. На втором видео, в момент 3:07, видно странное разрешение коллизии, которое приводит к тому, что CharacterController резко толкается вниз, когда напрыгивает на такую платформу.

Официальный гид по CharacterController: http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/g… trollers.html

Репозиторий: https://bitbucket.org/dddenisss/unity-character-motor

29 июня 2016

#Unity, #Unity3D


Обновление: 26 июля 2016

2001—2017 © GameDev.ru — Разработка игр