Платформер на Unity: физика и контроллер персонажа

Пока тут болею бронхитом, ковыряю понемногу Unity. На следующем месте работы, вполне вероятно, придётся с фронтом работать, а не только с беком. Решил потренироваться на простеньком платформере. Одно из первых и важных в платформере — персонаж и его управление. И там не всё так просто.

Платформер на Unity: физика и контроллер персонажа

Если погуглить на эту тему, то многие задаются вопросом, Dynamic или Kinematic коллайдер использовать? Многие не понимают, как работает kinematic тело.

В общем-то, довольно много вариантов контроллеров для персонажа уже сущестует:

Предположим, мы не хотим заморачиваться с kinematic телом и используем стандартную физику с dynamic коллайдером. Вполне вероятно, что то, как оно работает по умолчанию, вам не очень понравится.

Если вам необходимо что-то сверх физики, какие-то свои фичи (например, прилипание к стенам), то с dynamic коллайдером это будет проблематично.

Что ж, будем использовать kinematic тело.

Возьмём контроллер из официальных уроков по Unity по созданию контроллера для платформера. Вся суть логики сводится к использованию Rigidbody2D.Cast.

Сначала зададим маску:

void Start() { contactFilter.useTriggers = false; contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer)); contactFilter.useLayerMask = true; }

Сама логика по обработке в FixedUpdate.

int count = rb2d.Cast(move, contactFilter, hitBuffer, distance + shellRadius); hitBufferList.Clear(); for (int i = 0; i < count; i++) { hitBufferList.Add(hitBuffer[i]); } for (int i = 0; i < hitBufferList.Count; i++) { Vector2 currentNormal = hitBufferList[i].normal; if (currentNormal.y > minGroundNormalY) { grounded = true; if (yMovement) { groundNormal = currentNormal; currentNormal.x = 0; } } // знак при скалярном произведении векторов говорим нам об угле между векторами // в практическом плане это говорит о направлении движении по отношению к поверхности float projection = Vector2.Dot(velocity, currentNormal); if (projection < 0) { velocity = velocity - projection * currentNormal; } float modifiedDistance = hitBufferList[i].distance - shellRadius; distance = modifiedDistance < distance ? modifiedDistance : distance; }

Что из этого получится? С простыми коллайдерами оно даже будет работать, но если какая-то нестандартная структура будет, скажем, если использовать Sprite Shape, то определение коллизий будет работать плохо. Вплоть до того, что просто провалимся в коллайдер, пока нас не вытолкнет из него.

Там логика работает вокруг Physics2D.Raycast. Рейкастим по направлению движения (4 луча вертикально, 8 горизонтально).

Платформер на Unity: физика и контроллер персонажа

После кастов корректируем вектор движения.

_raycastHit = Physics2D.Raycast(ray, rayDirection, rayDistance, platformMask); if (_raycastHit) { deltaMovement.x = _raycastHit.point.x - ray.x; rayDistance = Mathf.Abs(deltaMovement.x); }

В результате худо-бедно вроде работает.

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

3030
7 комментариев

Да, спрайты именно оттуда. В общем-то, я как раз хочу сделать более-менее рабочий платформер по этому видео.

3
Ответить

Ещё CorgiEngine можешь поковырять на предмет кинематик контроллера. Я для своего проекта перепробовал все вышеописанные в статье готовые решения. Ни один не удовлетворил мои потребности полностью, по этому я остановился на Kinematic Character Controller (https://assetstore.unity.com/packages/tools/physics/kinematic-character-controller-99131 ) из Ассет стора. Фишка в том что он не работает из коробки, но предоставляет очень мощный лоу-левел API поверх которого ты сам пишешь логику. Собственно я кинул сверху свою FSM на scriptable object’ах и пока что никаких нареканий.

1
Ответить

Ага, CorgiEngine купил вчера. Хочу поковырять ^_^

1
Ответить