Is Grounded в 2D платформере: как узнать, стоит ли персонаж?

в 9:26, , рубрики: 2d, game development, IsGrounded, unity3d, платформер, метки:

Буду краток. Бился над этой задачей достаточно долгое время, посему решил поделиться решением. Движок — 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js