Буду краток. Бился над этой задачей достаточно долгое время, посему решил поделиться решением. Движок — Unity3D.

Постановка задачи


Есть персонаж, есть платформы. Само собой, на персонаже коллайдер и ригидбоди, всё двадэ. На платформах тоже коллайдеры. На платформах — эффекторы, ибо платформы должны быть твердые только сверху. То есть, если перснаж упал на платформу, он на ней стоит. Но если персонаж идет мимо платформы или прыгает под ней — он свободно проходит через нее.

Персонаж может прыгать, только если стоит на платформе. Нужно знать, стоим ли мы.

Условия:

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)


  1. Sergiy
    16.03.2016 13:24
    +1

    А значит, «низ персонажа» должен быть ВНЕЗАПНО выше точки контакта. ВСЁ!

    ну я даже не знаю...


    1. Neongrey
      16.03.2016 16:51
      +1

      image
      Я серьезно думал, что точкки контакта двух коллайдеров ПРИНАДЛЕЖАТ обоим коллайдерам, т.е. находятся внутри или на границе. И соответственно, точка контакта не может быть ниже самой низкой точки коллайдера. А однако ж!


  1. Panda_sama
    16.03.2016 14:00
    +3

    Я, может, не понял проблемы, которую решает автор, но почему нельзя отслеживать контакт с землей просто по коллайду персонажа и платформы?
    Раз столкнулся — значит встал на платформу — значит теперь стоит?
    Ну и флажок какой "можноПрыгнуть" — прыгнули, сняли флажок, приземлились — включили флажок ?


    1. Neongrey
      16.03.2016 16:40

      На платформах — эффекторы, ибо платформы должны быть твердые только сверху. То есть, если персонаж упал на платформу, он на ней стоит. Но если персонаж идет мимо платформы или прыгает под ней — он свободно проходит через нее.

      Пересечение с коллайдером ВООБЩЕ не означает, что персонаж стоит на платформе. В этом и сложность.


    1. Neongrey
      16.03.2016 16:54

      И да,

      приземлились — включили флажок ?
      — а как узнать, что приземлились? Нулевая вертикальная скорость может быть и в верхней токе прыжка, плюс платформа, на которой мы находимся, может двигаться. Мы можем прыгать из-под платформы и в верхней точке прыжка пересекаться с ней, но все-таки не "встать" на нее — и вот, у перса нулевая скорость, и он пересекается с платформой — НО НЕ СТОИТ.
      Также платформа по тем или иным причинам может исчезнуть из-под персонажа.


  1. foxin
    16.03.2016 14:02
    +2

    Game development? 2D? Платформер? И ни одной картинки в статье?
    Ну вы, блин, даете. (с)


    1. Neongrey
      17.03.2016 17:41
      -1

      Уговорили, нате картинку: о)

      image


  1. GreatRash
    16.03.2016 18:29

    Я может чего не понимаю, но: http://docs.unity3d.com/Manual/class-PlatformEffector2D.html
    Достаточно установить флажок — Use One Way.


    1. Neongrey
      16.03.2016 19:54

      Конечно, можно. Но и что с того? Как это поможет узнать, можно прыгать или нет? Есть ли у вас более простой способ написать код, чтобы персонаж прыгал только если стоит на поверхности?
      К тому же, если вы полагаете, что с этим флажком событие коллизии генерируется только "с нужной стороны" — вы ошибаетесь.


      1. GreatRash
        17.03.2016 10:27

        Вот вам видео, где пацан за 2 минуты сделал так, чтобы персонаж прыгал сквозь платформу: https://www.youtube.com/watch?v=acFYSKle6wY
        Или же я снова чего-то недопонял?


        1. Neongrey
          17.03.2016 11:18

          Надо, чтобы он прыгал толкьо когда стоит. Задача — в том, чтобы определить, что персонаж прямо сейчас стоит.
          Сложно не сделать прыжки и ходьбу сквозь платформу — сложно отследить, что персонаж стоит, с учетом того, что персонаж может прыгать-ходить сквозь платформы, двигатсья вместе с платформами, платформы могут быть наклонными и т. д.
          Т.е. ни вертикальная скорость персонажа, ни пересечение с платформой, ни положение платформы и персонажа друг относительно друга не являются показателями того, что персонаж стоит.
          Так что недопоняли, да.


  1. lexxpavlov
    17.03.2016 13:13

    Я правильно понял, две платформы могут быть друг над другом, но ближе, чем высота персонажа? можно сказать — "под ногами" и "в поясе"? И тогда, если я иду по нижней платформе, то я могу идти сквозь верхней платформы. А если прыгну — то попаду на верхнюю платформу.


    1. Neongrey
      17.03.2016 13:40

      Да. Это делается стандартными средствами юнити. А вот отличить в самом коде платформу, на которой ты стоишь от платформы, через которую ты идешь, отличить именно "коллизию стояния" — это то, что делает моё решение.


      1. lexxpavlov
        17.03.2016 13:47

        не думаете ассет сделать на основе вашего решения?