2D 对象碰撞 Unity

Posted

技术标签:

【中文标题】2D 对象碰撞 Unity【英文标题】:2D Object Collison Unity 【发布时间】:2018-02-18 22:02:37 【问题描述】:

我正在使用 Unity 开发一个简单的 2D 游戏,但在处理碰撞时遇到了问题。我有两个对象,一棵树和一个玩家。树不会移动,而是由一些精灵和多边形碰撞器表示。玩家使用自定义脚本(不是角色控制器)移动,并附有 kinematic Ridgidbody 和多边形碰撞器。

我的预期行为是让玩家与树“碰撞”并被树挡住,因此所有物体都无法移动。但是,这似乎不是一种简单的方法。

将树的 RidgidBody 组件设置为“静态”或“动态”不会检测到碰撞。我考虑让玩家成为一个“动态”刚体,但unity docs 建议动态刚体不应该被它们的变换组件移动,这就是我当前系统的工作方式。此外,将其设置为动态会导致玩家无缘无故冻结的意外行为,并且由于不会对玩家对象应用任何物理,因此对于动态来说,这似乎是一个糟糕的用例。不过我可能是错的。

当碰撞事件被触发时,我可能会使用脚本以某种方式锁定玩家的位置,但这看起来很hacky。谁能提供一些有关如何处理此问题的见解?

【问题讨论】:

添加你用来移动播放器的脚本 【参考方案1】:

显然 2D 碰撞有点错误。这是避免此问题的一些方法。基本上,它不依赖对撞机,而是使用光线投射来检查玩家试图移动的地方是否有障碍物

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官方网站的教程。一款名为 Rogue 的 2D 游戏。这是链接,以防您打算做类似的事情:

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

【讨论】:

我已经解决了这个问题,将身体类型设置为动态,并使用rigidBody.MovePosition 结合Vector2 来处理玩家运动。谢谢你的回答。

以上是关于2D 对象碰撞 Unity的主要内容,如果未能解决你的问题,请参考以下文章

unity3d 碰撞检测

Unity 2d 中的碰撞检测

Unity 2D 轨迹渲染器碰撞

Unity2D 碰撞和一些物理

2D 项目中的对象不碰撞

使两个物理对象不碰撞,但在 Unity 中检测碰撞