Unity Character Motor
Автор: WISHMASTER35
Когда-то давно, еще во времена 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 не устарел.

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. Благодаря этому персонаж может с разбегу перейти на очень крутой склон, не оторвавшись от земли. Но можно поспорить хорошо это или плохо, ибо парой это выглядит не реалистично и странно. Возможно будет лучше уменьшить смещение.