检测哪些切片地图像元与Unity中的Collider2D发生冲突

人气:361 发布:2022-10-16 标签: 2d unity3d collision-detection

问题描述

我有一个Tilemap。它有一个TilemapCollider2D组件。上面画着几个瓷砖,每个瓷砖都有自己的精灵对撞机形状。然而,它们是精灵瓷砖,不是预制板。(它们不是使用预制画笔绘制的。)

我还有一个游戏对象,Collider2D(在我的情况下是CircleCollider2D),isTrigger设置为true,没有附加Rigidbody2D,因为该游戏对象保持在相对于其父对象的固定位置。

[编辑:我发现这个对撞机实际上使用的是父游戏对象的Rigidbody2D。如果没有刚体,则根本检测不到碰撞。]

Collider2D进入/退出磁贴时,我如何识别该磁贴的网格坐标(Vector3Int)?

明确地说,我希望从Tilemap脚本中检测到这一点。即TilemapCollider2D.OnTriggerEnter2D()TilemapCollider2D.OnCollisionEnter2D()

例如,在下图中,我希望收到磁贴B、C、D和E的OnTriggerEnter2D(),并知道它们在网格中的位置。

推荐答案

由于此问题不仅涉及Collider2DTilemapCollider2D之间的冲突,而且还涉及Collider2D和每个瓷砖之间的冲突,因此检测碰撞器之间的冲突并不那么简单。

(Simple detection of collision between the colliders is covered in this question on answers.unity.com.)

为了让tiemap脚本检测到每个磁贴的进入和退出,它需要响应OnTriggerEnter2D()OnTriggerStay2D()&;OnTriggerExit2D()

这是我的解决方案,基于检测CircleCollider2D何时与每个瓷砖相交,而不考虑瓷砖内任何碰撞器的几何形状。交叉点是近似值(为了提高效率),可能需要调整以适应其他类型的Collider2D

概述

OnTriggerEnter2D()中,获取CircleCollider2D的边界框并从中标识它与哪些磁贴相交。

对于这些瓷砖中的每一个,获取最接近该瓷砖中心的CircleCollider2D的世界位置。如果世界位置在平铺内,则存在交集。除了根据需要处理此问题外,还应将此磁贴的坐标添加到跟踪列表中。

OnTriggerStay2D()中,执行与OnTriggerEnter2D()相同的操作,但从跟踪列表中删除不再相交的磁贴并处理其相交出口。

OnTriggerExit2D()内,两个对撞器已分离,因此处理跟踪列表中所有磁贴的交集出口并清除跟踪列表。

代码示例

using System.Collections;
using System.Collections.Generic;
using System.Linq; // needed for cloning the list with .ToList()
using UnityEngine;
using UnityEngine.Tilemaps; // needed for Tilemap

public class MyTilemapScript : MonoBehaviour
{
    List<Vector3Int> trackedCells;
    Tilemap tilemap;
    GridLayout gridLayout;

    void Awake()
    {
        trackedCells = new List<Vector3Int>();
        tilemap = GetComponent<Tilemap>();
        gridLayout = GetComponentInParent<GridLayout>();
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        // NB: Bounds cannot have zero width in any dimension, including z
        var cellBounds = new BoundsInt(
            gridLayout.WorldToCell(other.bounds.min),
            gridLayout.WorldToCell(other.bounds.size) + new Vector3Int(0, 0, 1));

        IdentifyIntersections(other, cellBounds);
    }

    void OnTriggerStay2D(Collider2D other)
    {
        // Same as OnTriggerEnter2D()
        var cellBounds = new BoundsInt(
            gridLayout.WorldToCell(other.bounds.min),
            gridLayout.WorldToCell(other.bounds.size) + new Vector3Int(0, 0, 1));

        IdentifyIntersections(other, cellBounds);
    }

    void OnTriggerExit2D(Collider2D other)
    {
        // Intentionally pass zero size bounds
        IdentifyIntersections(other, new BoundsInt(Vector3Int.zero, Vector3Int.zero));
    }

    void IdentifyIntersections(Collider2D other, BoundsInt cellBounds)
    {
        // Take a copy of the tracked cells
        var exitedCells = trackedCells.ToList();

        // Find intersections within cellBounds
        foreach (var cell in cellBounds.allPositionsWithin)
        {
            // First check if there's a tile in this cell
            if (tilemap.HasTile(cell))
            {
                // Find closest world point to this cell's center within other collider
                var cellWorldCenter = gridLayout.CellToWorld(cell);
                var otherClosestPoint = other.ClosestPoint(cellWorldCenter);
                var otherClosestCell = gridLayout.WorldToCell(otherClosestPoint);

                // Check if intersection point is within this cell
                if (otherClosestCell == cell)
                {
                    if (!trackedCells.Contains(cell))
                    {
                        // other collider just entered this cell
                        trackedCells.Add(cell);

                        // Do actions based on other collider entered this cell
                    }
                    else
                    {
                        // other collider remains in this cell, so remove it from the list of exited cells
                        exitedCells.Remove(cell);
                    }
                }
            }
        }

        // Remove cells that are no longer intersected with
        foreach (var cell in exitedCells)
        {
            trackedCells.Remove(cell);

            // Do actions based on other collider exited this cell
        }
    }
}

FYI

From a separate discussion on forum.unity.com,我学到了几个要点(对我来说并不明显):

对于TilemapCollider2DOnTriggerEnter2D()和amp;OnTriggerExit2D()是在整个TilemapCollider2D级别调用的,而不是在每个磁贴级别调用的。即就像任何其他对撞机类型一样。 Collision2D.contactsContactPoint2D的数组。ContactPoint2D.point被描述为世界空间中两个对撞机之间的接触点。这对我来说意味着它是交叉点。然而,它实际上是物理模型想要施加力的位置。因此,我的解决方案使用触发对撞器和OnTriggerXxxx而不是OnCollisionXxxx,并且我自己计算交点。

1,001