Столкновение 2D-объектов Unity

Я разрабатываю простую 2D-игру в Unity и столкнулся с проблемой при столкновении. У меня есть два объекта, дерево и игрок. Дерево не двигается и представлено несколькими спрайтами и полигональным коллайдером. Игрок перемещается с помощью пользовательского скрипта (НЕ контроллера персонажа), и к нему прикреплены кинематический Ridgidbody и полигональный коллайдер.

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

Установка для компонента RidgidBody дерева значения «статический» или «динамический» приводит к тому, что столкновения не обнаруживаются. Я думал сделать проигрыватель «динамическим» твердым телом, но документация по Unity предполагают, что динамические твердые тела не должны перемещаться их компонентом преобразования, как работает моя текущая система. Кроме того, установка динамического значения приводит к непреднамеренному поведению, когда игрок зависает без причины, а поскольку физика не применяется к объекту игрока, это кажется плохим вариантом использования для динамического. Я могу просто ошибаться в этом.

Я мог бы использовать скрипт, чтобы каким-то образом заблокировать положение игрока при срабатывании события коллайдера, но это кажется очень хакерским. Может ли кто-нибудь дать некоторое представление о том, как справиться с этим?


person Jeremy    schedule 10.09.2017    source источник
comment
Добавьте скрипт, который вы используете для перемещения игрока   -  person Ignacio Alorre    schedule 10.09.2017


Ответы (1)


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

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Player : MovingObjects {

    protected override void AttemptMove<T> (int xDir, int yDir)
    {
        base.AttemptMove<T> (xDir, yDir);
        RaycastHit2D hit;       
    }
    protected override void onCantMove<T>(T component)
    {
        Wall hitwall = component as Wall;
        hitwall.DamageWall (wallDamage);        
    }

    // Update is called once per frame
    void Update () {

        int horizontal = 0;
        int vertical = 0;

        horizontal = (int)Input.GetAxisRaw ("Horizontal");
        vertical = (int)Input.GetAxisRaw ("Vertical");

        if (horizontal != 0)
            vertical = 0;

        if (horizontal != 0 || vertical != 0)
            AttemptMove<Wall> (horizontal, vertical);
    }
}

Который наследуется от:

using UnityEngine;
using System.Collections;

public abstract class MovingObjects : MonoBehaviour {

    public float moveTime = 0.1f;
    public LayerMask blockingLayer;

    private BoxCollider2D boxCollider;
    private Rigidbody2D rb2D;
    private float inverseMoveTime;

    protected virtual void Start()
    {
        boxCollider = GetComponent<BoxCollider2D> ();
        rb2D = GetComponent <Rigidbody2D>();
        inverseMoveTime = 1f / moveTime;

    }


    protected IEnumerator SmoothMovement(Vector3 end)
    {
        float sqrRemaininDistance = (transform.position - end).sqrMagnitude;

        while (sqrRemaininDistance > float.Epsilon) {

            Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime*Time.deltaTime);

            rb2D.MovePosition(newPosition);
            sqrRemaininDistance = (transform.position - end).sqrMagnitude;

            yield return null;
        }
    }

    protected bool Move(int xDir, int yDir, out RaycastHit2D hit)
    {
        Vector2 start = transform.position;
        Vector2 end = start + new Vector2 (xDir, yDir);

        boxCollider.enabled = false;

        hit = Physics2D.Linecast (start, end, blockingLayer);

        boxCollider.enabled = true;

        if (hit.transform == null) {

            StartCoroutine(SmoothMovement(end));
            return true;
        }

        return false;
    }

    protected virtual void AttemptMove<T>(int xDir, int yDir)
                            where T : Component
    {

        RaycastHit2D hit;
        bool canMove = Move (xDir, yDir, out hit);

        if (hit.transform == null)
            return;


        Debug.Log ("Something hit", gameObject);

        T hitComponent = hit.transform.GetComponent<T> ();

        if (!canMove && hitComponent != null)
            onCantMove (hitComponent);


    }

    protected abstract void onCantMove<T>(T component)
                       where T: Component;

}

Этот скрипт относится к учебнику официального сайта Unity. 2D-игра под названием Rogue. Вот ссылка на тот случай, если вы планируете сделать что-то подобное:

https://unity3d.com/es/learn/tutorials/projects/2d-roguelike-tutorial

person Ignacio Alorre    schedule 10.09.2017
comment
Я решил эту проблему, задав для типа тела значение dynamic и используя RigidBody.MovePosition в сочетании с Vector2 для управления движением игрока. Спасибо за ваш ответ. - person Jeremy; 11.09.2017