unity3D PathFinding2D

using UnityEngine;
using System.Collections.Generic;
using System;
using System.Linq;

public static class PathFinding2D
{
    /**
     * find a path in grid tilemaps
     */
    public static List<Vector2Int> find4(Vector2Int from, Vector2Int to, Dictionary<Vector2Int, int> map, List<int> passableValues)
    {
        Func<Vector2Int, Vector2Int, float> getDistance = delegate (Vector2Int a, Vector2Int b)
        {
            float xDistance = Mathf.Abs(a.x - b.x);
            float yDistance = Mathf.Abs(a.y - b.y);
            return xDistance * xDistance + yDistance * yDistance;
        };
        Func<Vector2Int, List<Vector2Int>> getNeighbors = delegate (Vector2Int pos)
        {
            var neighbors = new List<Vector2Int>();
            neighbors.Add(new Vector2Int(pos.x, pos.y + 1));
            neighbors.Add(new Vector2Int(pos.x, pos.y - 1));
            neighbors.Add(new Vector2Int(pos.x + 1, pos.y));
            neighbors.Add(new Vector2Int(pos.x - 1, pos.y));
            return neighbors;
        };

        return astar(from, to, map, passableValues, getDistance, getNeighbors);
    }

    /**
     * find a path in hexagonal grid tilemaps (when grid rows are staggered with each other)
     */
    public static List<Vector2Int> find6X(Vector2Int from, Vector2Int to, Dictionary<Vector2Int, int> map, List<int> passableValues)
    {
        Func<Vector2Int, Vector2Int, float> getDistance = delegate (Vector2Int a, Vector2Int b)
        {
            float xDistance = Mathf.Abs(a.x - b.x);
            float yDistance = Mathf.Abs(a.y - b.y) * Mathf.Sqrt(3);
            return xDistance * xDistance + yDistance * yDistance;
        };
        Func<Vector2Int, List<Vector2Int>> getNeighbors = delegate (Vector2Int pos)
        {
            var neighbors = new List<Vector2Int>();
            neighbors.Add(new Vector2Int(pos.x + 1, pos.y + 1));
            neighbors.Add(new Vector2Int(pos.x - 1, pos.y + 1));
            neighbors.Add(new Vector2Int(pos.x + 1, pos.y - 1));
            neighbors.Add(new Vector2Int(pos.x - 1, pos.y - 1));
            neighbors.Add(new Vector2Int(pos.x - 2, pos.y));
            neighbors.Add(new Vector2Int(pos.x + 2, pos.y));
            return neighbors;
        };
        return astar(from, to, map, passableValues, getDistance, getNeighbors);
    }

    /**
     * find a path in hexagonal grid tilemaps (when grid columns are staggered with each other)
     */
    public static List<Vector2Int> find6Y(Vector2Int from, Vector2Int to, Dictionary<Vector2Int, int> map, List<int> passableValues)
    {
        Func<Vector2Int, Vector2Int, float> getDistance = delegate (Vector2Int a, Vector2Int b)
        {
            float xDistance = Mathf.Abs(a.x - b.x) * Mathf.Sqrt(3);
            float yDistance = Mathf.Abs(a.y - b.y);
            return xDistance * xDistance + yDistance * yDistance;
        };
        Func<Vector2Int, List<Vector2Int>> getNeighbors = delegate (Vector2Int pos)
        {
            var neighbors = new List<Vector2Int>();
            neighbors.Add(new Vector2Int(pos.x + 1, pos.y + 1));
            neighbors.Add(new Vector2Int(pos.x - 1, pos.y + 1));
            neighbors.Add(new Vector2Int(pos.x + 1, pos.y - 1));
            neighbors.Add(new Vector2Int(pos.x - 1, pos.y - 1));
            neighbors.Add(new Vector2Int(pos.x, pos.y - 2));
            neighbors.Add(new Vector2Int(pos.x, pos.y + 2));
            return neighbors;
        };
        return astar(from, to, map, passableValues, getDistance, getNeighbors);
    }


    static List<Vector2Int> astar(Vector2Int from, Vector2Int to, Dictionary<Vector2Int, int> map, List<int> passableValues,
                      Func<Vector2Int, Vector2Int, float> getDistance, Func<Vector2Int, List<Vector2Int>> getNeighbors)
    {
        var result = new List<Vector2Int>();
        if (from == to)
        {
            result.Add(from);
            return result;
        }
        Node finalNode;
        List<Node> open = new List<Node>();
        if (findDest(new Node(null, from, getDistance(from, to), 0), open, map, to, out finalNode, passableValues, getDistance, getNeighbors))
        {
            while (finalNode != null)
            {
                result.Add(finalNode.pos);
                finalNode = finalNode.preNode;
            } 
        }
        result.Reverse();
        return result;
    }

    static bool findDest(Node currentNode, List<Node> openList,
                         Dictionary<Vector2Int, int> map, Vector2Int to, out Node finalNode, List<int> passableValues,
                      Func<Vector2Int, Vector2Int, float> getDistance, Func<Vector2Int, List<Vector2Int>> getNeighbors)
    {
        if (currentNode == null) {
            finalNode = null;
            return false;
        }
        else if (currentNode.pos == to)
        {
            finalNode = currentNode;
            return true;
        }
        currentNode.open = false;
        openList.Add(currentNode);

        foreach (var item in getNeighbors(currentNode.pos))
        {
            if (map.ContainsKey(item) && passableValues.Contains(map[item]))
            {
                findTemp(openList, currentNode, item, to, getDistance);
            }
        }
        var next = openList.FindAll(obj => obj.open).Min();
        return findDest(next, openList, map, to, out finalNode, passableValues, getDistance, getNeighbors);
    }

    static void findTemp(List<Node> openList, Node currentNode, Vector2Int from, Vector2Int to, Func<Vector2Int, Vector2Int, float> getDistance)
    {

        Node temp = openList.Find(obj => obj.pos == (from));
        if (temp == null)
        {
            temp = new Node(currentNode, from, getDistance(from, to), currentNode.gScore + 1);
            openList.Add(temp);
        }
        else if (temp.open && temp.gScore > currentNode.gScore + 1)
        {
            temp.gScore = currentNode.gScore + 1;
            temp.fScore = temp.hScore + temp.gScore;
            temp.preNode = currentNode;
        }
    }

    class Node:IComparable
    {
        public Node preNode;
        public Vector2Int pos;
        public float fScore;
        public float hScore;
        public float gScore;
        public bool open = true;

        public Node(Node prePos, Vector2Int pos, float hScore, float gScore)
        {
            this.preNode = prePos;
            this.pos = pos;
            this.hScore = hScore;
            this.gScore = gScore;
            this.fScore = hScore + gScore;
        }

        public int CompareTo(object obj)
        {
            Node temp = obj as Node;

            if (temp == null) return 1;

            if (Mathf.Abs(this.fScore - temp.fScore) < 0.01f) {
                return this.fScore > temp.fScore ? 1 : -1;
            }

            if (Mathf.Abs(this.hScore - temp.hScore) < 0.01f)
            {
                return this.hScore > temp.hScore ? 1 : -1;
            }
            return 0;
        }
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;


/**
 * 1.create a unity 2D project
 * 2.copy scripts to you project folder
 * 3.create a gameobject,and attach example.cs to it
 * 4.play
 */
public class Example : MonoBehaviour
{
    enum TileType
    {
        none,
        wall,
    }

    public int width = 15;      //tile map width
    public int height = 12;     //tile map height
    public int obstacleFillPercent = 30;    //tile map obstacle fill percent
    public float scale = 32f;

    Sprite tilePrefab;
    string message = "";

    List<int> passableValues;

    GameObject allMapTiles;     //the map and tiles
    GameObject player;          //the player
    GameObject goal;            //the goal



    void Start()
    {
        Camera.main.orthographicSize = scale * 10;
        Camera.main.gameObject.transform.position = new Vector3(width * scale / 2, height * scale / 2, -10);
        tilePrefab = Sprite.Create(new Texture2D((int)scale, (int)scale), new Rect(0, 0, scale, scale), new Vector2(0.5f, 0.5f), 1f);

        goal = new GameObject("goal");
        goal.AddComponent<SpriteRenderer>();
        goal.GetComponent<SpriteRenderer>().sprite = tilePrefab;
        goal.GetComponent<SpriteRenderer>().color = Color.yellow;
        goal.GetComponent<SpriteRenderer>().sortingOrder = 1;

        player = new GameObject("player");
        player.AddComponent<SpriteRenderer>();
        player.GetComponent<SpriteRenderer>().sprite = tilePrefab;
        player.GetComponent<SpriteRenderer>().color = Color.red;
        player.GetComponent<SpriteRenderer>().sortingOrder = 2;


        passableValues = new List<int>();
        passableValues.Add((int)TileType.none);
    }

    void OnGUI()
    {
        if (GUI.Button(new Rect(10, 10, 150, 30), "pathfinding 4"))
        {
            message = "finding...";
            simPathFinding4();
        }
        if (GUI.Button(new Rect(10, 50, 150, 30), "pathfinding 6X"))
        {
            message = "finding...";
            simPathFinding6();
        }
        if (GUI.Button(new Rect(10, 90, 150, 30), "pathfinding 6Y"))
        {
            message = "finding...";
            simPathFinding6(false);
        }

        GUI.Label(new Rect(180, 20, 300, 30), message);
    }

    /**
     * simulate path finding in grid tilemaps
     */
    public void simPathFinding4()
    {
        StopAllCoroutines();

        //init map
        var map = mapToDict4(generateMapArray(width, height));
        float xScale = scale;
        float yScale = scale;
        renderMap(map, xScale, yScale);

        //init player and goal
        var playerPos = new Vector2Int(0, 0);
        map[playerPos] = (int)TileType.none;
        setTransformPosition(player.transform, playerPos, xScale, yScale);
        var goalPos = new Vector2Int(width - 1, height - 1);
        map[goalPos] = (int)TileType.none;
        setTransformPosition(goal.transform, goalPos, xScale, yScale);

        //finding
        var path = PathFinding2D.find4(playerPos, goalPos, map, passableValues);
        if (path.Count == 0)
        {
            message = "oops! cant find goal";
        }
        else
        {
            StartCoroutine(movePlayer(path, xScale, yScale, .2f));
        }
    }

    /**
     * simulate path finding in hexagonal grid tilemaps
     */
    public void simPathFinding6(bool staggerByRow = true)
    {
        StopAllCoroutines();

        //init map
        var map = mapToDict6(generateMapArray(width, height), staggerByRow);
        var hexScale = scale + 4f; //addtional 4f makes tiles seperated
        float xScale = staggerByRow ? hexScale / 2 : hexScale;
        float yScale = staggerByRow ? hexScale : hexScale/2;
        renderMap(map, xScale, yScale);

        //init player and goal
        var mapPoses = map.Keys.ToList();
        mapPoses.Sort((a, b) => a.x + a.y - b.x - b.y);
        var playerPos = mapPoses.First();
        map[playerPos] = (int)TileType.none;
        setTransformPosition(player.transform, playerPos, xScale, yScale);
        var goalPos = mapPoses.Last();
        map[goalPos] = (int)TileType.none;
        setTransformPosition(goal.transform, goalPos, xScale, yScale);

        //find
        List<Vector2Int> path;
        if (staggerByRow) {
            path = PathFinding2D.find6X(playerPos, goalPos, map, passableValues);
        } else {
            path = PathFinding2D.find6Y(playerPos, goalPos, map, passableValues);
        }
        if (path.Count == 0)
        {
            message = "oops! cant find goal";
        }
        else
        {
            StartCoroutine(movePlayer(path, xScale, yScale, .2f));
        }
    }


    void setTransformPosition(Transform trans, Vector2Int pos, float xScale, float yScale)
    {
        trans.position = new Vector3(pos.x * xScale, pos.y * yScale, 0);
    }

    void renderMap(Dictionary<Vector2Int, int> map, float xScale, float yScale)
    {
        Destroy(allMapTiles);
        allMapTiles = new GameObject("allMapTiles");
        foreach (var item in map)
        {
            GameObject temp = new GameObject();
            temp.transform.position = new Vector3(item.Key.x * xScale, item.Key.y * yScale, 0);
            SpriteRenderer spr = temp.AddComponent<SpriteRenderer>();
            spr.sprite = tilePrefab;
            switch (item.Value)
            {
                case (int)TileType.none:
                    spr.color = Color.white;
                    break;
                case (int)TileType.wall:
                    spr.color = Color.black;
                    break;
            }
            temp.transform.parent = allMapTiles.transform;
        }
    }

    IEnumerator movePlayer(List<Vector2Int> path, float xScale, float yScale, float interval = 0.1f)
    {
        foreach(var item in path) {
            setTransformPosition(player.transform, item, xScale, yScale);
            yield return new WaitForSeconds(interval);
        }
   
        message = "reach goal !";
    }

    int[,] generateMapArray(int pwidth, int pheight)
    {
        var mapArray = new int[pwidth, pheight];
        for (int x = 0; x < pwidth; x++)
        {
            for (int y = 0; y < pheight; y++)
            {
                mapArray[x, y] = Random.Range(0, 100) < obstacleFillPercent ? (int)TileType.wall : (int)TileType.none;
            }
        }
        return mapArray;
    }

    Dictionary<Vector2Int, int> mapToDict4(int[,] mapArray)
    {
        Dictionary<Vector2Int, int> mapDict = new Dictionary<Vector2Int, int>();
        for (int x = 0; x < mapArray.GetLength(0); x++)
        {
            for (int y = 0; y < mapArray.GetLength(1); y++)
            {
                mapDict.Add(new Vector2Int(x, y), mapArray[x, y]);
            }
        }
        return mapDict;
    }

    Dictionary<Vector2Int, int> mapToDict6(int[,] mapArray, bool stretchRow)
    {
        Dictionary<Vector2Int, int> mapDict = new Dictionary<Vector2Int, int>();
        for (int x = 0; x < mapArray.GetLength(0); x++)
        {
            for (int y = 0; y < mapArray.GetLength(1); y++)
            {
                if (stretchRow)
                {
                    if (y % 2 == 0)
                    {
                        mapDict.Add(new Vector2Int(2 * x, y), mapArray[x, y]);
                    }
                    else
                    {
                        mapDict.Add(new Vector2Int(2 * x + 1, y), mapArray[x, y]);
                    }
                }
                else
                {
                    if (x % 2 == 0)
                    {
                        mapDict.Add(new Vector2Int(x, 2 * y), mapArray[x, y]);
                    }
                    else
                    {
                        mapDict.Add(new Vector2Int(x, 2 * y + 1), mapArray[x, y]);
                    }
                }

            }
        }
        return mapDict;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值