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

Unity技术求助:如何从激光重叠碰撞中获取单个Tile并删除

太空挖矿游戏:采矿激光删除Tilemap单个Tile的问题修正

我正在开发一款太空挖矿游戏,需要实现玩家发射的采矿激光束删除Tilemap中与光束重叠的Tile。该光束是从玩家枪械发出的静态持续矩形。原本使用OverlapBoxAll获取重叠对象,但返回的始终是(0,0,0)坐标,仅得到Tilemap整体而非重叠的单个Tile,求助修正代码以获取单个Tile的位置并删除。

可破坏Tilemap的Inspector面板截图:
可破坏Tilemap的Inspector面板

原代码:

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

public class Laser : MonoBehaviour
{
    [SerializeField] float range;
    private Tilemap tilemap;
    private GameObject player;

    void Start()
    {// Start is called before the first frame update

        tilemap = GameObject.Find("Destructible Tiles").GetComponent<Tilemap>();
        player = GameObject.Find("Player");
    }
        
    void Update()
    {// Update is called once per frame

        PlayerInput();
    }

    private void PlayerInput()
    {
        if (Input.GetMouseButton(0))
        {//left mouse click
            Fire();
        }
    }

    private void Fire()
    {
        //player coords
        float px = player.transform.position.x;
        float py = player.transform.position.y;

        //coords for box overlap centerpoint
        float offset;

        if (range <= 1)
        {
            offset = 1;
        }
        else
        {
            offset = (range / 2) + 0.5f;
        }

        //get all targets in range of laser
        Collider2D[] targets = { };

        if (player.transform.localScale.x == 1)
        {//facing right

            targets = Physics2D.OverlapBoxAll(new Vector2(px + offset, py), new Vector2(range, 0.1f), 0);
        }
        else if (player.transform.localScale.x == -1)
        {//facing left

            targets = Physics2D.OverlapBoxAll(new Vector2(px - offset, py), new Vector2(range, 0.1f), 0);
        }
        
        foreach (Collider2D target in targets)
        {
            if (target.tag == "Ground")
            {//target is minable
                Vector3Int tile = tilemap.WorldToCell(target.transform.position);
                tilemap.SetTile(tile, null);
            }
        }
    }
}

我尝试通过OverlapBoxAll获取单个Tile的数组,但仅得到Tilemap整体。


问题根源

你用OverlapBoxAll拿到的是整个Tilemap的碰撞体,不是单个Tile的。因为默认情况下Tilemap的碰撞体是覆盖整张地图的,所以检测结果只会返回Tilemap对象,它的transform位置就是(0,0,0),自然删不到正确的Tile。

修正方案

直接跳过物理碰撞检测,通过坐标范围遍历激光覆盖的所有Tile单元格,判断并删除存在的Tile:

修正后的完整代码

using UnityEngine;
using UnityEngine.Tilemaps;

public class Laser : MonoBehaviour
{
    [SerializeField] float range;
    [SerializeField] float laserWidth = 0.1f; // 可调整的激光宽度
    private Tilemap tilemap;
    private GameObject player;

    void Start()
    {
        tilemap = GameObject.Find("Destructible Tiles").GetComponent<Tilemap>();
        player = GameObject.Find("Player");
    }
        
    void Update()
    {
        PlayerInput();
    }

    private void PlayerInput()
    {
        if (Input.GetMouseButton(0))
        {
            Fire();
        }
    }

    private void Fire()
    {
        Vector2 playerPos = player.transform.position;
        bool isFacingRight = player.transform.localScale.x > 0;

        // 计算激光覆盖的世界坐标范围
        float startX = isFacingRight ? playerPos.x : playerPos.x - range;
        float endX = isFacingRight ? playerPos.x + range : playerPos.x;
        float startY = playerPos.y - laserWidth / 2;
        float endY = playerPos.y + laserWidth / 2;

        // 把世界坐标转成Tilemap的单元格坐标
        Vector3Int startCell = tilemap.WorldToCell(new Vector3(startX, startY, 0));
        Vector3Int endCell = tilemap.WorldToCell(new Vector3(endX, endY, 0));

        // 遍历激光覆盖的所有单元格
        for (int x = Mathf.Min(startCell.x, endCell.x); x <= Mathf.Max(startCell.x, endCell.x); x++)
        {
            for (int y = Mathf.Min(startCell.y, endCell.y); y <= Mathf.Max(startCell.y, endCell.y); y++)
            {
                Vector3Int currentCell = new Vector3Int(x, y, 0);
                // 检查单元格是否有Tile,有就删除
                if (tilemap.HasTile(currentCell))
                {
                    tilemap.SetTile(currentCell, null);
                }
            }
        }
    }
}

核心改动说明

  1. 替换碰撞检测逻辑:不再依赖OverlapBoxAll,直接通过坐标计算激光覆盖的Tile范围,避免误获取整个Tilemap对象。
  2. 坐标转换与遍历:将激光的世界范围转成Tilemap的单元格坐标,逐个遍历检查每个单元格是否有Tile。
  3. 精准删除Tile:用HasTile()确认单元格存在Tile后,再调用SetTile(null)删除。

额外优化建议

  • 给激光加个Sprite组件,让玩家能直观看到激光的实际范围。
  • 添加冷却时间变量,防止按住鼠标时Tile被瞬间清空。
  • 如果需要Tile有耐久度,可以把删除逻辑改成减少生命值,直到为0再删除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 19:21:00