Может кто-нибудь объяснить, как работает обнаружение столкновений Unity 2d и почему иногда происходит сбой? - PullRequest
0 голосов
/ 26 сентября 2018

Я попытался адаптировать контроллер персонажа 2d platformer из этого сеанса в реальном времени: https://www.youtube.com/watch?v=wGI2e3Dzk_w&list=PLX2vGYjWbI0SUWwVPCERK88Qw8hpjEGd8

В контроллер персонажа 2D сверху вниз.Казалось, что это работает, но можно переходить на коллайдеры с некоторой комбинацией нажатых клавиш, которую я не мог найти, но это легко сделать.

Дело в том, что я не понимаю, какздесь действительно работает обнаружение столкновений, поэтому я не знаю, как это исправить.Я ценю, если кто-то может объяснить, как это работает.

Спасибо:)

Вот как настроен плеер: Инспектор игрока

PlayerControllerTopDown2D.cs :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerControllerTopDown2D : PhysicsObject2D
{
    public float maxSpeed = 7;

    private SpriteRenderer spriteRenderer;
    private Animator animator;

    private bool facingUp, facingDown, facingLeft, facingRight;

    void Awake()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
        animator = GetComponent<Animator>();

        facingUp = true;
        facingDown = facingLeft = facingRight = false;
    }

    protected override void ComputeVelocity()
    {
        Vector2 move = Vector2.zero;

        move.x = Input.GetAxis("Horizontal");
        move.y = Input.GetAxis("Vertical");

        targetVelocity = move * maxSpeed;

        if (move.y > minMoveDistance && !facingUp)
        {
            clearOthersAndSet(0);
            // sprite rotation
        }

        if (move.y < -minMoveDistance && !facingDown)
        {
            clearOthersAndSet(1);
            // sprite rotation
        }

        if (move.x < -minMoveDistance && !facingLeft)
        {
            clearOthersAndSet(2);
            // sprite rotation
        }

        if (move.x > minMoveDistance && !facingRight)
        {
            clearOthersAndSet(3);
            // sprite rotation
        }


    }

    void clearOthersAndSet(int x)
    {
        switch (x)
        {
            case 0;
                facingUp = true;
                facingDown = facingLeft = facingRight = false;
                break;
            case 1:
                facingDown = true;
                facingUp = facingLeft = facingRight = false;
                break;
            case 2:
                facingLeft = true;
                facingUp = facingDown = facingRight = false;
                break;
            case 3:
                facingRight = true;
                facingUp = facingDown = facingLeft = false;
                break;
        }
    }
}

PhysicsObject2D.cs :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PhysicsObject2D : MonoBehaviour
{
    protected Rigidbody2D rb2d;
    protected Vector2 velocity;
    protected Vector2 targetVelocity;

    protected ContactFilter2D contactFilter;
    protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
    protected List<RaycastHit2D> hitBufferList = new List<RaycastHit2D>(16);


    protected const float minMoveDistance = 0.001f;
    protected const float shellRadius = 0.01f;

    protected bool hitSomething = false;


    void OnEnable()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

    void Start()
    {
        contactFilter.useTriggers = false;
        int layerMask = Physics2D.GetLayerCollisionMask(gameObject.layer);
        contactFilter.SetLayerMask(layerMask);
        contactFilter.useLayerMask = true;
    }

    void Update()
    {
        targetVelocity = Vector2.zero;
        ComputeVelocity();
    }

    protected virtual void ComputeVelocity()
    {

    }

    void FixedUpdate()
    {
        if (hitSomething)
        {
            targetVelocity = -targetVelocity * 5;
            hitSomething = false;
        }

        velocity.x = targetVelocity.x;
        velocity.y = targetVelocity.y;

        Vector2 deltaPosition = velocity * Time.deltaTime;

        Vector2 move = Vector2.right * deltaPosition.x;

        Movement(move, false);

        move = Vector2.up * deltaPosition.y;

        Movement(move, true);
    }

    void Movement(Vector2 move, bool yMovement)
    {
        float distance = move.magnitude;

        if (distance > minMoveDistance)
        {
            int count = rb2d.Cast(move, contactFilter, hitBuffer, distance + shellRadius);

            if (count > 0)
                hitSomething = true;
            else
                hitSomething = false;

            hitBufferList.Clear();
            for (int i = 0; i < count; i++)
            {
                hitBufferList.Add(hitBuffer[i]);
            }

            for (int i = 0; i < hitBufferList.Count; i++)
            {
                float modifiedDistance = hitBufferList[i].distance - shellRadius;
                distance = modifiedDistance < distance ? modifiedDistance : distance;
            }
        }

        rb2d.position = rb2d.position + move.normalized * distance;
    }
}

Ответы [ 3 ]

0 голосов
/ 27 сентября 2018

Как предположил Матеус, я изменил режим коллайдеров на непрерывный, однако проблема все еще возникает.Я нашел способ сделать это наверняка.

Чтобы сделать это, я удалил строку 38 из PhysicsObject2D.cs:

targetVelocity = -targetVelocity * 5;

См. Это изображение

В этой позиции я нажимаю влево + вниз , и игрок перемещается вверх в коллайдер бокса,Игрок не был внутри других коллайдеров в начале.

Когда тела перекрываются, ему разрешается свободно перемещаться внутрь, но движение намного медленнее, а направления меняются местами, нажатие вверх перемещается вниз, нажатие влевострелка перемещается вправо.

0 голосов
/ 27 сентября 2018

1) Установите для режима обнаружения столкновений значение «Непрерывный» для важных объектов, таких как игрок.

2) Используйте rb2d.MovePosition(); для движения.

3) Не звоните rb2d.MovePosition()более одного раза в кадре.

эти 3 вместе должны сделать ваше движение и обнаружение столкновений работать нормально.Я не буду подвергать сомнению остальную часть вашего кода, но вот мои обобщенные предложения

Vector2 MovementDirection; //Assuming this is assigned in Update() from Input
public float MaxSpeed;
void FixedUpdate() 
{
    Vector2 finalMoveDir = MovementDirection.normalized * MaxSpeed;
    //
    //any additional changes to the final direction should happen here
    //
    finalMoveDir *= Time.deltaTime;
    rb2d.MovePosition((Vector2)transform.position + finalMoveDir);
}
0 голосов
/ 27 сентября 2018

упрощение, проверка единства для обнаружения каждого кадра, если ваш объект движется быстро (преодолевая большое расстояние за короткое время), есть шанс, что ваш объект пройдет через стену в то же самое время между промежутком проверки столкновенияи другой.как указано и проверено в этом потоке .

, если ваш объект проходит через объект, первое, что вы хотите изменить, это режим обнаружения столкновений , когдарежим установлен на дискретный, вы говорите, что объект проверяет наличие столкновений с более низкой скоростью. enter image description here

и когда вы устанавливаете его в непрерывный режим, объект проверяет наличиестолкновение чаще.enter image description here

, поэтому, вероятно, достаточно установить режим обнаружения с «дискретного» на непрерывный, чтобы решить вашу проблему.

...