Проектування механік руху гри
Персонаж відривається від землі на половину кадру раніше, ніж натиснута кнопка прострибування — і гравець відчуває це як «пластилінове» управління. Саме тут розпочинається робота з проектування механік руху: не в налаштуванні Rigidbody.mass навмання, а у формалізації відчуття відповіді через конкретні числа та архітектурні рішення.
Чому «просто CharacterController» не працює
Unity пропонує два шляхи: фізичний Rigidbody + коллайдер і кінематичний CharacterController. Обидві реалізації мають специфічні підводні камені.
CharacterController.Move() не взаємодіє з фізичним рушієм безпосередньо — персонаж проходить крізь рухливі платформи, якщо не реалізувати власний механізм riding. Стандартний прапор isGrounded повертає false на одному кадрі при спуску по схилу — і персонаж починає безперервно «підстрибувати» через накопичену гравітацію у буфері вертикальної швидкості.
Rigidbody персонаж стійкіший до фізичних взаємодій, але вимагає ручного контролю тертя: без PhysicMaterial з нульовим dynamicFriction на капсулі персонаж застрягає на кутах геометрії. При цьому Rigidbody.AddForce() у режимі ForceMode.VelocityChange поводиться передбачувано лише при fixedDeltaTime 0.02 — змініть крок фізики, і всі налаштовані відчуття «поплисаву».
Окремий клас проблем — детектування землі. Physics.SphereCast вниз з радіусом трохи менше капсули надійніше, ніж Physics.Raycast, але вимагає обережного налаштування LayerMask, інакше каст потрапить у власний коллайдер персонажа.
Як будується архітектура системи руху
Добре спроектована система руху розділяє три зони відповідальності: введення, стан і фізика/переміщення.
Введення читається в Update() і записується у структуру MovementInput. Стани (Idle, Running, Jumping, Falling, Crouching, WallRunning) керуються кінцевим автоматом — звичайно це користувацький клас на основі MonoBehaviour, а не Animator State Machine, тому що логіка переходів часто нелінійна і пов'язана з умовами гри, а не з вагами анімації. Саме зміщення позиції відбувається в FixedUpdate() через Rigidbody.MovePosition() або безпосередньо через velocity.
Для платформерів з повітряним контролем важлива крива airControlCurve: горизонтальне прискорення у повітрі повинно бути менше наземного, але не нульовим. Реалізується через AnimationCurve у налаштуваннях персонажа ScriptableObject — дизайнер змінює криву в інспекторі без дотику до коду.
Coyote time і jump buffering — обов'язкові компоненти для відзивчивого управління. Перший дозволяє прострибнути через coyoteTimeDuration (зазвичай 0.1–0.15 секунди) після зійття з платформи. Другий зберігає натискання прострибування у буфер на jumpBufferDuration (0.1–0.2 секунди) і виконує його при першій можливості. Без цих двох механік гравець постійно «промахується» мимо прострибування на краю платформи.
Змінна висота прострибування — ще одна точка, де деталі вирішують все. Якщо відпустити кнопку прострибування в середині підйому, вертикальна швидкість зрізується до minJumpVelocity. Це реалізується простою перевіркою в Update(): якщо rb.velocity.y > minJumpVelocity && !jumpButtonHeld, то rb.velocity = new Vector3(rb.velocity.x, minJumpVelocity, rb.velocity.z).
Орієнтовні строки за масштабом
| Масштаб | Опис | Строк |
|---|---|---|
| Базовий | 2D/3D персонаж, земля, прострибування, coyote time | 2–5 днів |
| Середній | + подвійне прострибування, dash, wall jump, crouch | 1–2 тижні |
| Розширений | + плавання, лазання, ragdoll transition, рухливі платформи | 2–4 тижні |
| Повна система | + процедурний IK кроків, lean, мережева репліація | 4–8 тижнів |
Особливості для специфічних жанрів
У шутерах від першої особи Camera і CharacterController живуть у різних ієрархіях і оновлюються незалежно — це запобігає тремтінню камери при фізичних колізіях корпусу. Head bobbing реалізується на рівні камери через синус від пройденої відстані, а не через анімацію батьківського об'єкту.
У топ-даун RPG з NavMeshAgent рух по клацанню вимагає розмежування: NavMeshAgent управляє шляхом, але не анімацією. Параметри Speed і Direction Animator обчислюються з agent.velocity кожен кадр, а не з натиснутих клавіш. Частої помилки — включити updateRotation = true на агенті та одночасно крутити трансформ вручну, що призводить до тремтіння повороту.
Процес роботи над проектом
Починаємо з аналізу GDD і референсних ігор — виписуємо конкретні числа з механік (швидкість бігу в units/sec, висота прострибування, час dash). Потім проектуємо MovementSettings ScriptableObject зі всіма параметрами та PlayerMovement MonoBehaviour з задокументованими зонами відповідальності.
Прототип збирається на примітивах без анімацій — лише коллайдери та логіка. Це дозволяє нащупати feel управління за день-два, без витрати часу на інтеграцію з Animator. Після затвердження feel підключаємо Animator Controller з Blend Tree для локомоції та фінальні коллайдери по mesh-геометрії.
Тестування включає граничні випадки: прострибування в 1-юніт проріз, рухливі платформи з обертанням, перехід між NavMesh Surface різних сцен при адитивному завантаженні. Ці ситуації найчастіше розкривають проблеми з детектуванням землі та накопленням velocity.





