Пока тут болею бронхитом, ковыряю понемногу Unity. На следующем месте работы, вполне вероятно, придётся с фронтом работать, а не только с беком.
Решил потренироваться на простеньком платформере. Одно из первых и важных в платформере — персонаж и его управление. И там не всё так просто.
Если погуглить на эту тему, то многие задаются вопросом, Dynamic или Kinematic коллайдер использовать? Многие не понимают, как работает kinematic тело.
В общем-то, довольно много вариантов контроллеров для персонажа уже сущестует:
- CharacterController от valgoun.
- 2D-Character-Controlle от Brackeys.
- Популярный CharacterController2D от prime31.
У unity есть неплохие уроки по созданию контроллера персонажа для платформера.
Предположим, мы не хотим заморачиваться с kinematic телом и используем стандартную физику с dynamic коллайдером. Вполне вероятно, что то, как оно работает по умолчанию, вам не очень понравится.
Если вам необходимо что-то сверх физики, какие-то свои фичи (например, прилипание к стенам), то с dynamic коллайдером это будет проблематично.
Что ж, будем использовать kinematic тело.
Возьмём контроллер из официальных уроков по Unity по созданию контроллера для платформера. Вся суть логики сводится к использованию Rigidbody2D.Cast
.
Сначала зададим маску:
1 2 3 4 5 |
void Start() { contactFilter.useTriggers = false; contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer)); contactFilter.useLayerMask = true; } |
Сама логика по обработке в FixedUpdate
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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, то определение коллизий будет работать плохо. Вплоть до того, что просто провалимся в коллайдер, пока нас не вытолкнет из него.
Попробуем популярный CharacterController2D от prime31.
Там логика работает вокруг Physics2D.Raycast
. Рейкастим по направлению движения (4 луча вертикально, 8 горизонтально).
После кастов корректируем вектор движения.
1 2 3 4 5 |
_raycastHit = Physics2D.Raycast(ray, rayDirection, rayDistance, platformMask); if (_raycastHit) { deltaMovement.x = _raycastHit.point.x - ray.x; rayDistance = Mathf.Abs(deltaMovement.x); } |
В результате худо-бедно вроде работает.
Будут проблемы при использовании нестандартных коллайдеров. Нужно будет подгонять скрипт под свои нужды. Но как отправная точка вполне сгодится.