unity 四邊形網格下的移動範圍顯示ide
先上效果圖,移動力三點,綠色格子消耗一點,棕色格子消耗兩點,淺白色是移動範圍。函數
移動力和消耗點數均可以自定義,下面就開始程序部分的說明,格子是用unity的tilemap去作的,至於怎麼刷格子,這裏就不說了。spa
定義一個List,用來存放移動方向。code
private static readonly List<Vector3Int> tileOffset = new List<Vector3Int>() { Vector3Int.down,Vector3Int.right,Vector3Int.up,Vector3Int.left };
定義一個Dictionary,用來存格子的消耗點數。orm
private static readonly Dictionary<string, int> tileMoveCostDictionary = new Dictionary<string, int>() { { "Base_Green",1},{"Base_Brown",2 } };
定義三個List,分別用來儲存移動範圍內格子的tilemap座標、高於一點消耗的格子的tilemap座標、高消耗格子當前已經消耗點數(每回合+1點)。對象
private List<Vector3Int> movePointRangeList; private List<Vector3Int> blockingPointList; private List<int> blockingRemainList;
tilemap座標以下。blog
先初始化List,還有定義一個Camera並初始化,用於後面2D射線檢測格子。隊列
void Start() { mainCamera = Camera.main; movePointRangeList = new List<Vector3Int>(); blockingPointList = new List<Vector3Int>(); blockingRemainList = new List<int>(); }
給白色格子加上BoxCollider2D並加上tag 「Infantry」,給Tilemap加上TilemapCollider2D並加上tag 「TileMap」,下面是在update裏2D射線檢測點擊的是白色格子仍是地圖上的格子。string
// Update is called once per frame void Update() { if (Input.GetMouseButtonDown(0)) { raycastHit2D = Physics2D.Raycast(mainCamera.ScreenToWorldPoint(Input.mousePosition), Vector2.zero); if (raycastHit2D.collider != null) { switch (raycastHit2D.transform.tag) { case "Infantry": currentSelect = raycastHit2D.transform; if (movePointObjParent.childCount == 0) { DisplayMovementRange(gridLayout.WorldToCell(raycastHit2D.point)); } else { currentSelect = null; CleanMovementRangeObj(); } break; case "TileMap": if (currentSelect != null) { currentSelect.localPosition = gridLayout.CellToLocal(gridLayout.WorldToCell(raycastHit2D.point)); currentSelect = null; CleanMovementRangeObj(); } break; } } } }
再定義一些變量,tilemap和gridlayout就不說了即當前使用的tilemap,movePointPrefab是淺白色格子的prefab,其實就是一個sprite而後作成預製體,movePointObjParent是存放移動範圍的GameObject,movementPoints表示移動點數,currentSelect表示當前選中的白色格子,考慮到可能有多個移動對象,因此這裏這麼處理。it
public Tilemap tilemap; public GridLayout gridLayout; public GameObject movePointPrefab; public Transform movePointObjParent; public int movementPoints; private Transform currentSelect;
獲取移動範圍內的格子的函數以下,遇到高消耗格子就存到blockingPointList裏面,而後下一次點數計算時,就把該格子對應響應點數+1,若是小於前面Dictionary裏定義的消耗點數,繼續放進隊列裏,直到不知足條件,才讓該格子的四個方向進行探索。
private void DisplayMovementRange(Vector3Int startPos) { Queue<Vector3Int> currentQueue = new Queue<Vector3Int>(); Queue<Vector3Int> nextQueue = new Queue<Vector3Int>(); Vector3Int currentPoint; Vector3Int nextPoint; int value; nextQueue.Enqueue(startPos); for (int i = 0; i < movementPoints; i++) { currentQueue = new Queue<Vector3Int>(nextQueue); nextQueue.Clear(); while (currentQueue.Count > 0) { currentPoint = currentQueue.Dequeue(); if (blockingPointList.Contains(currentPoint)) { int index = blockingPointList.IndexOf(currentPoint); value = GetTileCost(currentPoint); blockingRemainList[index]++; if (blockingRemainList[index] < value) { nextQueue.Enqueue(currentPoint); continue; } } //4 Direction for (int j = 0; j < 4; j++) { nextPoint = currentPoint + tileOffset[j]; if (IsNextPointInRange(nextPoint)) { if (!movePointRangeList.Contains(nextPoint)) { value = GetTileCost(nextPoint); movePointRangeList.Add(nextPoint); nextQueue.Enqueue(nextPoint); if (value > 1 && !blockingPointList.Contains(nextPoint)) { blockingPointList.Add(nextPoint); blockingRemainList.Add(0); } } } } } } CreateMovementRangeObj(); }
其餘的輔助函數以下。
private int GetTileCost(Vector3Int tilePos) { int value; if (tileMoveCostDictionary.TryGetValue(tilemap.GetTile(tilePos).name, out value)) { return value; } else { print("Cannot Find Tile Cost"); return -1; } } private bool IsNextPointInRange(Vector3Int nextPoint) { return nextPoint.x >= 0 && nextPoint.x < 16 && nextPoint.y >= 0 && nextPoint.y < 16; } private void CreateMovementRangeObj() { foreach (Vector3Int item in movePointRangeList) { GameObject obj = Instantiate(movePointPrefab, movePointObjParent); obj.transform.localPosition = gridLayout.CellToLocal(item); } movePointRangeList.Clear(); } private void CleanMovementRangeObj() { if (movePointObjParent.childCount == 0) return; for (int i = 0; i < movePointObjParent.childCount; i++) { Destroy(movePointObjParent.GetChild(i).gameObject); } blockingPointList.Clear(); blockingRemainList.Clear(); }
歡迎交流,轉載註明出處:)