Буду краток. Бился над этой задачей достаточно долгое время, посему решил поделиться решением. Движок — Unity3D.
Постановка задачи
Есть персонаж, есть платформы. Само собой, на персонаже коллайдер и ригидбоди, всё двадэ. На платформах тоже коллайдеры. На платформах — эффекторы, ибо платформы должны быть твердые только сверху. То есть, если перснаж упал на платформу, он на ней стоит. Но если персонаж идет мимо платформы или прыгает под ней — он свободно проходит через нее.
Персонаж может прыгать, только если стоит на платформе. Нужно знать, стоим ли мы.
Условия:
1) Форма платформы произвольная.
2) Персонаж может стоять на самом краю платформы.
3) Платформы могут быть наклонными.
4) Платформы могут двигаться.
Популярное решение и почему оно не подходит
В стандартном примере из ассет-стора предлагается использовать дополнительный геймобжект под персонажем, кидать из персонажа рэйкасты в геймобжект и следить, нет ли пересечения с землей.
1) Если мы «идем мимо» или «запрыгиваем на», то рэйкаст вполне поймает «землю», хотя персонаж не стоит.
2) Рэйкаст работает по конкретной точке, а не по площади. А значит, если платформа не будет находиться именно под точкой рэйкаста — например, мы стоим на краю платформы, или на маленьком камушке — проверка ее не заметит.
Решение
Решение нельзя получить логически, только опытным путем. Ибо оно проистекает из особенностей работы физики движка. Вам нужны:
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)
{
foreach (var p in coll.contacts)
if (p.point.y < myCollider.bounds.min.y)
{
if (!GroundColliders.Contains(coll.collider))
GroundColliders.Add(coll.collider);
}
}
3) Если больше не пересекаемся с платформой — удаляем ее из списка:
void OnCollisionExit2D(Collision2D coll)
{
if (GroundColliders.Contains(coll.collider))
GroundColliders.Remove(coll.collider);
}
4) Угадайте, что проверяем:
bool IsGrounded
{
get
{
return GroundColliders.Count > 0;
}
}
Просто, понятно, достаточно универсально, безо всяких тегов, рэйкастов, дополнительных геймобжектов. Пользуйтесь на здоровье.
Автор: Neongrey