Unity技术求助:如何从激光重叠碰撞中获取单个Tile并删除
太空挖矿游戏:采矿激光删除Tilemap单个Tile的问题修正
我正在开发一款太空挖矿游戏,需要实现玩家发射的采矿激光束删除Tilemap中与光束重叠的Tile。该光束是从玩家枪械发出的静态持续矩形。原本使用OverlapBoxAll获取重叠对象,但返回的始终是(0,0,0)坐标,仅得到Tilemap整体而非重叠的单个Tile,求助修正代码以获取单个Tile的位置并删除。
可破坏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); } } } } }
核心改动说明
- 替换碰撞检测逻辑:不再依赖
OverlapBoxAll,直接通过坐标计算激光覆盖的Tile范围,避免误获取整个Tilemap对象。 - 坐标转换与遍历:将激光的世界范围转成Tilemap的单元格坐标,逐个遍历检查每个单元格是否有Tile。
- 精准删除Tile:用
HasTile()确认单元格存在Tile后,再调用SetTile(null)删除。
额外优化建议
- 给激光加个Sprite组件,让玩家能直观看到激光的实际范围。
- 添加冷却时间变量,防止按住鼠标时Tile被瞬间清空。
- 如果需要Tile有耐久度,可以把删除逻辑改成减少生命值,直到为0再删除。
内容的提问来源于stack exchange,提问作者Fenris
相关产品推荐
相关产品推荐

