Постановка задачи
Есть персонаж, есть платформы. Само собой, на персонаже коллайдер и ригидбоди, всё двадэ. На платформах тоже коллайдеры. На платформах — эффекторы, ибо платформы должны быть твердые только сверху. То есть, если перснаж упал на платформу, он на ней стоит. Но если персонаж идет мимо платформы или прыгает под ней — он свободно проходит через нее.
Персонаж может прыгать, только если стоит на платформе. Нужно знать, стоим ли мы.
Условия:
1) Форма платформы произвольная.
2) Персонаж может стоять на самом краю платформы.
3) Платформы могут быть наклонными.
4) Платформы могут двигаться.
Популярное решение и почему оно не подходит
В стандартном примере из ассет-стора предлагается использовать дополнительный геймобжект под персонажем, кидать из персонажа рэйкасты в геймобжект и следить, нет ли пересечения с землей.
1) Если мы «идем мимо» или «запрыгиваем на», то рэйкаст вполне поймает «землю», хотя персонаж не стоит.
2) Рэйкаст работает по конкретной точке, а не по площади. А значит, если платформа не будет находиться именно под точкой рэйкаста — например, мы стоим на краю платформы, или на маленьком камушке — проверка ее не заметит.
Решение
Решение нельзя получить логически, только опытным путем. Ибо оно проистекает из особенностей работы физики движка. Вам нужны:
using System.Linq — вообще удобная штука, рекомендую ознакомиться, если еще не. Использую для поиска по коллекциям, уничтожает 90% форычей с вложенными ифами.
GetComponent().bounds.min.y — низ вашего коллайдера, то бишь минимальная ордината
OnCollisionStay2D docs.unity3d.com/ScriptReference/Collider2D.OnCollisionStay2D.html
OnCollisionExit2D
CollisionContacts docs.unity3d.com/ScriptReference/Collision-contacts.html docs.unity3d.com/ScriptReference/ContactPoint-point.html
OnCollisionStay2D срабатывает, когда персонаж и платформа движутся друг относительно друга. При попадании персонажа на платформу т.е. при OnCollisionEnter2D вышеуказанное событие тоже сработает.
Так вот, оказывается, если персонаж стоит на платформе, то геометрически его коллайдер и коллайдер платформы не пересекаются. А значит, «низ персонажа» должен быть ВНЕЗАПНО выше точки контакта. ВСЁ!
1) Создаем список:
List<Collider2D> GroundColliders = new List<Collider2D>();
2) По событию OnCollisionStay2D проверяем и при необходимости добавляем коллайдеры:
void OnCollisionStay2D(Collision2D coll)
{
if (!GroundColliders.Contains(coll.collider))
foreach (var p in coll.contacts)
if (p.point.y < myCollider.bounds.min.y)
{
GroundColliders.Add(coll.collider);
break;
}
}
3) Если больше не пересекаемся с платформой — удаляем ее из списка:
void OnCollisionExit2D(Collision2D coll)
{
if (GroundColliders.Contains(coll.collider))
GroundColliders.Remove(coll.collider);
}
4) Угадайте, что проверяем:
bool IsGrounded
{
get
{
return GroundColliders.Count > 0;
}
}
Просто, понятно, достаточно универсально, безо всяких тегов, рэйкастов, дополнительных геймобжектов. Пользуйтесь на здоровье.
Комментарии (18)
Panda_sama
16.03.2016 14:00+3Я, может, не понял проблемы, которую решает автор, но почему нельзя отслеживать контакт с землей просто по коллайду персонажа и платформы?
Раз столкнулся — значит встал на платформу — значит теперь стоит?
Ну и флажок какой "можноПрыгнуть" — прыгнули, сняли флажок, приземлились — включили флажок ?Neongrey
16.03.2016 16:40На платформах — эффекторы, ибо платформы должны быть твердые только сверху. То есть, если персонаж упал на платформу, он на ней стоит. Но если персонаж идет мимо платформы или прыгает под ней — он свободно проходит через нее.
Пересечение с коллайдером ВООБЩЕ не означает, что персонаж стоит на платформе. В этом и сложность.
Neongrey
16.03.2016 16:54И да,
приземлились — включили флажок ?
— а как узнать, что приземлились? Нулевая вертикальная скорость может быть и в верхней токе прыжка, плюс платформа, на которой мы находимся, может двигаться. Мы можем прыгать из-под платформы и в верхней точке прыжка пересекаться с ней, но все-таки не "встать" на нее — и вот, у перса нулевая скорость, и он пересекается с платформой — НО НЕ СТОИТ.
Также платформа по тем или иным причинам может исчезнуть из-под персонажа.
GreatRash
16.03.2016 18:29Я может чего не понимаю, но: http://docs.unity3d.com/Manual/class-PlatformEffector2D.html
Достаточно установить флажок — Use One Way.Neongrey
16.03.2016 19:54Конечно, можно. Но и что с того? Как это поможет узнать, можно прыгать или нет? Есть ли у вас более простой способ написать код, чтобы персонаж прыгал только если стоит на поверхности?
К тому же, если вы полагаете, что с этим флажком событие коллизии генерируется только "с нужной стороны" — вы ошибаетесь.GreatRash
17.03.2016 10:27Вот вам видео, где пацан за 2 минуты сделал так, чтобы персонаж прыгал сквозь платформу: https://www.youtube.com/watch?v=acFYSKle6wY
Или же я снова чего-то недопонял?Neongrey
17.03.2016 11:18Надо, чтобы он прыгал толкьо когда стоит. Задача — в том, чтобы определить, что персонаж прямо сейчас стоит.
Сложно не сделать прыжки и ходьбу сквозь платформу — сложно отследить, что персонаж стоит, с учетом того, что персонаж может прыгать-ходить сквозь платформы, двигатсья вместе с платформами, платформы могут быть наклонными и т. д.
Т.е. ни вертикальная скорость персонажа, ни пересечение с платформой, ни положение платформы и персонажа друг относительно друга не являются показателями того, что персонаж стоит.
Так что недопоняли, да.
lexxpavlov
17.03.2016 13:13Я правильно понял, две платформы могут быть друг над другом, но ближе, чем высота персонажа? можно сказать — "под ногами" и "в поясе"? И тогда, если я иду по нижней платформе, то я могу идти сквозь верхней платформы. А если прыгну — то попаду на верхнюю платформу.
Neongrey
17.03.2016 13:40Да. Это делается стандартными средствами юнити. А вот отличить в самом коде платформу, на которой ты стоишь от платформы, через которую ты идешь, отличить именно "коллизию стояния" — это то, что делает моё решение.
Sergiy
А значит, «низ персонажа» должен быть ВНЕЗАПНО выше точки контакта. ВСЁ!
ну я даже не знаю...
Neongrey
Я серьезно думал, что точкки контакта двух коллайдеров ПРИНАДЛЕЖАТ обоим коллайдерам, т.е. находятся внутри или на границе. И соответственно, точка контакта не может быть ниже самой низкой точки коллайдера. А однако ж!