You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity2D网格吸附时如何检测目标网格上的GameObject?

嘿,这个问题我之前帮好几个做网格类游戏的开发者解决过——不同形状的物体确实不能只查单个格子,得把它整个占的网格区域都扫一遍才行!下面给你几个实用的方案,你可以根据自己的游戏场景选:

核心思路:先确定物体占据的所有网格坐标

不管你的物体是1x1的方块、2x1的长条还是3x2的L型,第一步都是要算出:当它吸附到目标网格位置时,会覆盖哪些具体的网格点。比如一个横向2x1的物体,锚点在左下角的话,当锚点吸附到(2,3),它实际占的是(2,3)和(3,3)两个格子。

具体实现方案

方案1:给每个物体绑定“网格占用区域”数据

这是最灵活的方案,适合自定义各种复杂形状:

  • 给你的GameObject加一个自定义组件,比如GridOccupancy,里面存一个相对锚点的坐标列表。比如L型物体,锚点在左下角的话,相对坐标可以是[(0,0), (0,1), (1,0)]。
  • 维护一个全局的网格管理类(比如GridManager),用HashSet<Vector2Int>或者Dictionary<Vector2Int, GameObject>记录所有已被占用的网格点。
  • 当拖拽要吸附时,先根据目标锚点的网格坐标,加上相对坐标算出所有要占用的绝对网格坐标。
  • 遍历这些绝对坐标,只要有一个已经在占用集合里,就阻止吸附;全部空闲的话,就允许移动,同时更新占用集合(移除旧位置的网格点,添加新位置的)。

示例代码(C#/Unity):

// 自定义的网格占用组件
public class GridOccupancy : MonoBehaviour
{
    [Tooltip("相对于物体锚点的占用网格坐标")]
    public List<Vector2Int> relativeOccupiedGrids;
}

// 拖拽时的位置有效性检测
public bool IsTargetPositionValid(Vector2Int targetAnchorGrid, GridOccupancy currentObjectOccupancy)
{
    foreach (var relativeGrid in currentObjectOccupancy.relativeOccupiedGrids)
    {
        var absoluteGrid = targetAnchorGrid + relativeGrid;
        // 检查这个网格是否已被占用
        if (GridManager.Instance.OccupiedGrids.Contains(absoluteGrid))
        {
            return false;
        }
    }
    return true;
}

方案2:用碰撞体检测(适合物理驱动/形状复杂的场景)

如果你的物体已经有准确的碰撞体(比如复合碰撞体),可以直接用物理检测来判断是否重叠:

  • 给所有可放置的物体单独设一个Layer,比如PlaceableObject,避免和场景中的障碍物混淆。
  • 当拖拽到目标吸附位置时,临时把物体移到目标位置,用Physics2D.OverlapCollider或者Physics2D.OverlapBoxAll检测是否和其他同Layer的物体重叠。
  • 检测完再把物体移回拖拽中的临时位置,根据检测结果决定是否允许吸附。

示例代码:

public bool IsTargetPositionValid(Vector3 targetWorldPosition, Collider2D currentObjectCollider)
{
    // 先记录当前位置,临时移动到目标吸附点
    var originalPosition = transform.position;
    transform.position = targetWorldPosition;

    // 只检测同Layer的可放置物体
    var filter = new ContactFilter2D().SetLayerMask(LayerMask.GetMask("PlaceableObject"));
    var hitColliders = new List<Collider2D>();
    Physics2D.OverlapCollider(currentObjectCollider, filter, hitColliders);

    // 移回原位置
    transform.position = originalPosition;

    // 排除自身碰撞体,只要有其他物体重叠就无效
    return hitColliders.Count == 0 || (hitColliders.Count == 1 && hitColliders[0].gameObject == gameObject);
}

方案3:网格节点预标记(适合大地图/大量物体的场景)

如果你的游戏是固定网格地图,可以提前给每个网格节点加状态标记:

  • 给每个网格节点(比如GridNode类)添加IsOccupied和OccupyingObject属性。
  • 当物体要移动时,计算它覆盖的所有GridNode,逐个检查IsOccupied状态。
  • 移动成功后,更新旧覆盖节点的IsOccupied为false,新覆盖节点设为true并绑定物体引用。
额外注意点
  • 锚点一致性:所有物体的锚点规则要统一(比如都以左下角、中心为锚点),不然计算相对坐标时会出错。
  • 旋转适配:如果物体支持旋转,记得在计算占用网格时,根据旋转角度调整相对坐标(比如横向2x1旋转90度后变成纵向1x2,相对坐标也要跟着变换)。
  • 拖拽预览:可以在拖拽时实时更新物体的颜色(比如无效时变红,有效时变绿),让玩家直观知道位置是否可用。

内容的提问来源于stack exchange,提问作者The Wolf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:46:59