Unity 2D非网格不规则地块移动点范围检测实现问题
不规则地块回合制战棋的移动范围检测实现方案
我正在开发一款回合制战棋游戏,玩家部队可在类似Paradox风格的不规则形状省份地块上移动,默认每支部队每回合拥有2点移动点,可移动对应数量的省份。但作为新手,我无法实现准确的移动范围检测——要么判定所有地块都在范围内,要么无法精准计算。由于地块为不规则形状,未采用网格系统,目前我通过Collider重叠来检测相邻地块,以下是我尝试的代码(重点查看GetTilesInRange方法):
{ private void Awake() { Army = GameObject.FindGameObjectsWithTag("Division"); Map = GameObject.FindGameObjectsWithTag("Tile"); DivisionHeightOffset = new Vector3(0f, -ñDivisionHeightOffsetY, 0f); } private void Update() { mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); CheckforSelectedDivisions(); } private void CheckforSelectedDivisions() { foreach ( GameObject Division in Army) { if (Division.GetComponent<DivisionBehaviour>().Selected && !Checked) { Checked = true; SelectedDivision = Division; SelectedDivisionPosition = Division.transform.position; NameOfSelectedDivision = Division.name; DivisionOverlapPoint = Division.transform.position + DivisionHeightOffset; DivisionMovementPoints = (Division.GetComponent<DivisionBehaviour>().MovementPoints); GetCurrentTile(); } } if(SelectedDivision != null) { if(!SelectedDivision.GetComponent<DivisionBehaviour>().Selected) {Checked = false;}} } private void GetCurrentTile() { foreach (GameObject Tile in Map) if (Tile.GetComponent<Collider2D>()) { if(Tile.GetComponent<Collider2D>().OverlapPoint(DivisionOverlapPoint)) { CurrentTile = Tile; Debug.Log(NameOfSelectedDivision + " is in " + CurrentTile.name); CurrentTilePosition = Tile.transform.position; CurrentTileBounds = Tile.GetComponent<Collider2D>().bounds; GetTilesInRange(); } } } private void GetTilesInRange() { i = DivisionMovementPoints; foreach (GameObject TileB in Map) if (TileB.GetComponent<Collider2D>() && TileB != CurrentTile && !TileB.GetComponent<TileInfo>().Movable && !TileB.GetComponent<TileInfo>().Movable2) { if(TileB.GetComponent<Collider2D>().bounds.Intersects(CurrentTileBounds)) { MovableTile = TileB; Debug.Log(MovableTile.name + " is in range!"); } else if(i > 0 && TileB.GetComponent<Collider2D>().bounds.Intersects(MovableTile.GetComponent<Collider2D>().bounds)) { i = i - 1; MovableTile = TileB; Debug.Log(MovableTile.name + " is in range!"); } } } }
核心问题分析
你的GetTilesInRange方法存在逻辑缺陷:
- 单次遍历试图处理多层移动,逻辑混乱,无法正确累加移动步数
MovableTile变量被反复覆盖,无法追踪已遍历的节点- 未区分不同移动步数的层级,导致范围判定误差
正确实现方案(基于BFS广度优先搜索)
不规则地块的移动范围检测本质是图的遍历问题:每个地块是节点,相邻地块是边,移动点数是遍历的最大深度。用BFS可以精准计算所有可达地块:
步骤1:预先生成地块邻接表
避免每次检测都遍历所有Collider,初始化时为每个地块生成邻接列表:
public class TileInfo : MonoBehaviour { public List<TileInfo> Neighbors = new List<TileInfo>(); public bool IsMovable; // 标记地块是否可移动 void Awake() { // 初始化时计算所有相邻地块 foreach (GameObject tileObj in GameObject.FindGameObjectsWithTag("Tile")) { TileInfo otherTile = tileObj.GetComponent<TileInfo>(); if (otherTile != this && GetComponent<Collider2D>().bounds.Intersects(otherTile.GetComponent<Collider2D>().bounds)) { Neighbors.Add(otherTile); } } } }
步骤2:实现BFS移动范围检测
替换原GetTilesInRange方法,用BFS遍历所有可达地块:
private void GetTilesInRange() { // 重置所有地块的可移动标记 foreach (GameObject tileObj in Map) { TileInfo tile = tileObj.GetComponent<TileInfo>(); tile.IsMovable = false; } TileInfo currentTileInfo = CurrentTile.GetComponent<TileInfo>(); // 队列存储(当前地块,剩余移动点数) Queue<(TileInfo tile, int remainingMovePoints)> queue = new Queue<(TileInfo, int)>(); HashSet<TileInfo> visited = new HashSet<TileInfo>(); // 起点入队(当前地块作为起点,不算可移动目标) queue.Enqueue((currentTileInfo, DivisionMovementPoints)); visited.Add(currentTileInfo); while (queue.Count > 0) { var (current, remaining) = queue.Dequeue(); // 剩余移动点数>0时,遍历相邻地块 if (remaining > 0) { foreach (TileInfo neighbor in current.Neighbors) { if (!visited.Contains(neighbor)) { neighbor.IsMovable = true; visited.Add(neighbor); queue.Enqueue((neighbor, remaining - 1)); Debug.Log(neighbor.gameObject.name + " is in range!"); } } } } }
步骤3:优化现有代码
- 避免在
Update中反复调用GetComponent,提前缓存组件引用 - 用事件监听替代
CheckforSelectedDivisions的每帧遍历,提升性能 - 修正
DivisionHeightOffset里的笔误ñDivisionHeightOffsetY,改为合法变量名(如_divisionHeightOffsetY)
额外优化建议
- 地块数量较多时,用空间划分(如QuadTree)减少邻接表预计算的碰撞检测次数
- 支持不同地块消耗不同移动点数(如山地耗2点、平原耗1点),只需在BFS入队时调整剩余点数
- 视觉上高亮可移动地块,提升玩家操作体验
内容的提问来源于stack exchange,提问作者Cow
相关产品推荐
相关产品推荐

