1. 程式人生 > >unity 四邊形網格下的移動範圍顯示

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();
    }

 

歡迎交流,轉載註明出處:)