unity 四边形网格下的移动范围显示
unity 四边形网格下的移动范围显示
先上效果图,移动力三点,绿色格子消耗一点,棕色格子消耗两点,浅白色是移动范围。
移动力和消耗点数都可以自定义,下面就开始程序部分的说明,格子是用unity的tilemap去做的,至于怎么刷格子,这里就不说了。
定义一个List,用来存放移动方向。
private static readonly List<Vector3Int> tileOffset = new List<Vector3Int>() { Vector3Int.down,Vector3Int.right,Vector3Int.up,Vector3Int.left };
定义一个Dictionary,用来存格子的消耗点数。
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坐标如下。
先初始化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射线检测点击的是白色格子还是地图上的格子。
// 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表示当前选中的白色格子,考虑到可能有多个移动对象,所以这里这么处理。
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(); }
欢迎交流,转载注明出处:)