A*寻路算法——多人寻路、实时碰撞寻路、最近目的地

A* 路算法原理可以参考这个文章,已经写的很详细了http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx
这篇文章主要写写多人寻路的实时碰撞
先说说无法寻路的情况下,如何移动的离目的地最近的点
其实所有能到达的点都在"关闭列表中",当“开启列表”中所有的点都遍历完后,如果还未找到终点,则视为路径不通
这时候遍历“关闭列表”,找出其中离终点直线距离最短的点即可,见下面代码中findNearPointFromList函数
多人碰撞的大体思路就是

  1. 用A*找出一条路径
  2. 按该路径走,没移动一格检测是否发生碰撞
  3. 如果碰撞,调用A*重新寻路
  4. 如果未碰撞,按原来路径继续走
  5. 到目的地停止
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
  
namespace testAstar  
{  
    public class AstarNode  
    {  
        private AstarNode parent = null;  
        private int g;  
        private int h;  
        private int x;  
        private int y;  
  
        public AstarNode Parent  
        {  
            get  
            {  
                return parent;  
            }  
            set  
            {  
                parent = value;  
            }  
        }  
  
        public int G  
        {  
            get  
            {  
                return g;  
            }  
            set  
            {  
                g = value;  
            }  
        }  
  
        public int H  
        {  
            get  
            {  
                return h;  
            }  
            set  
            {  
                h = value;  
            }  
        }  
  
        public int F  
        {  
            get  
            {  
                return g + h;  
            }  
        }  
  
        public int X  
        {  
            get  
            {  
                return x;  
            }  
            set  
            {  
                x = value;  
            }  
        }  
  
        public int Y  
        {  
            get  
            {  
                return y;  
            }  
            set  
            {  
                y = value;  
            }  
        }  
  
        public AstarNode(int _x, int _y)  
        {  
            this.x = _x;  
            this.y = _y;  
            this.parent = null;  
            this.g = 0;  
            this.h = 0;  
        }  
    }  
  
    public class Astar  
    {  
        private List<AstarNode> openList = new List<AstarNode>();  
        private List<AstarNode> closeList = new List<AstarNode>();  
        private bool[,] mapData = null;  
        private int pixelFormat = 16;  
        private int mapWidth = 0;  
        private int mapHeight = 0;  
        private int endX = 0;  
        private int endY = 0;  
  
        public bool[,] MapData  
        {  
            get  
            {  
                return mapData;  
            }  
        }  
  
        public int PixelFormat  
        {  
            get  
            {  
                return pixelFormat;  
            }  
        }  
  
        public int MapWidth  
        {  
            get  
            {  
                return mapWidth;  
            }  
        }  
  
        public int MapHeight  
        {  
            get  
            {  
                return mapHeight;  
            }  
        }  
  
        public Astar()  
        {  
        }  
  
        private bool isValid(int x, int y)  
        {  
            if (x < 0 || x >= mapWidth)  
            {  
                return false;  
            }  
  
            if (y < 0 || y >= mapHeight)  
            {  
                return false;  
            }  
  
            return true;  
        }  
  
        private bool inList(List<AstarNode> list, int x, int y)  
        {  
            foreach (AstarNode node in list)  
            {  
                if (node.X == x && node.Y == y)  
                {  
                    return true;  
                }  
            }  
  
            return false;  
        }  
  
        private bool inOpenList(int x, int y)  
        {  
            return inList(openList, x, y);  
        }  
  
        private bool inCloseList(int x, int y)  
        {  
            return inList(closeList, x, y);  
        }  
  
        private AstarNode getBestNodeFromOpenList()  
        {  
            if (openList.Count == 0)  
            {  
                return null;  
            }  
  
            return openList[0];  
        }  
  
        private void openToClose(AstarNode node)  
        {  
            openList.Remove(node);  
            closeList.Add(node);  
        }  
  
        private AstarNode openToCloseWithBest()  
        {  
            AstarNode node = getBestNodeFromOpenList();  
  
            if (node == null)  
            {  
                return null;  
            }  
  
            openToClose(node);  
            return node;  
        }  
  
        private void addToOpenList(AstarNode parent, int x, int y)  
        {  
            if (!isValid(x, y))  
            {  
                return;  
            }  
  
            if (inOpenList(x, y) || inCloseList(x, y))  
            {  
                return;  
            }  
  
            if (!canWalk(x, y) && parent != null)  
            {  
                return;  
            }  
  
            AstarNode node = new AstarNode(x, y);  
            node.Parent = parent;  
  
            if (parent == null)  
            {  
                node.G = 0;  
                node.H = 0;  
            }  
            else  
            {  
                if (Math.Abs(parent.X - x) + Math.Abs(parent.Y - y) == 2)  
                {  
                    node.G = 14;  
                }  
                else  
                {  
                    node.G = 10;  
                }  
  
                node.H = Math.Abs(endX - x) * 10 + Math.Abs(endY - y) * 10;  
            }  
  
            openList.Add(node);  
            openList.Sort(delegate(AstarNode lhs, AstarNode rhs)  
            {  
                if (lhs.F < rhs.F)  
                {  
                    return -1;  
                }  
                else if (lhs.F > rhs.F)  
                {  
                    return 1;  
                }  
                return 0;  
            });  
        }  
  
        private void genAroundNode(AstarNode node)  
        {  
            //addToOpenList(node, node.X - 1, node.Y - 1);  
            addToOpenList(node, node.X - 1, node.Y);  
            //addToOpenList(node, node.X - 1, node.Y + 1);  
  
            addToOpenList(node, node.X, node.Y - 1);  
            addToOpenList(node, node.X, node.Y + 1);  
  
            //addToOpenList(node, node.X + 1, node.Y - 1);  
            addToOpenList(node, node.X + 1, node.Y);  
            //addToOpenList(node, node.X + 1, node.Y + 1);  
        }  
  
        private AstarNode findNearPointFromList(List<AstarNode> list, int x, int y)  
        {  
            AstarNode result = null;  
            int minDistance = int.MaxValue;  
  
            foreach (AstarNode node in list)  
            {  
                int dist = (int)Math.Sqrt(Math.Abs(node.X - x) * Math.Abs(node.X - x) + Math.Abs(node.Y - y) * Math.Abs(node.Y - y));  
  
                if (dist < minDistance)  
                {  
                    minDistance = dist;  
                    result = node;  
                }  
            }  
  
            return result;  
        }  
  
        public bool canWalk(int x, int y)  
        {  
            return mapData[x, y];  
        }  
  
        public bool canWalkPixel(int x, int y)  
        {  
            int px = x / pixelFormat;  
            int py = y / pixelFormat;  
  
            return canWalk(px, py);  
        }  
  
        public List<AstarNode> findPath(int _startX, int _startY, int _endX, int _endY)  
        {  
            this.endX = _endX;  
            this.endY = _endY;  
            this.openList.Clear();  
            this.closeList.Clear();  
            List<AstarNode> result = new List<AstarNode>();  
            AstarNode currNode = null;  
            bool findPathFlag = false;  
  
            addToOpenList(null, _startX, _startY);  
  
            while (openList.Count > 0)  
            {  
                currNode = openToCloseWithBest();  
  
                if (currNode == null)  
                {  
                    break;  
                }  
  
                if (currNode.X == _endX && currNode.Y == _endY)  
                {  
                    findPathFlag = true;  
                    break;  
                }  
  
                genAroundNode(currNode);  
            }  
  
            if (!findPathFlag)  
            {  
                currNode = findNearPointFromList(closeList, endX, endY);  
            }  
  
            if (currNode == null)  
            {  
                return null;  
            }  
  
            while (true)  
            {  
                result.Add(currNode);  
  
                if (currNode.X == _startX && currNode.Y == _startY)  
                {  
                    break;  
                }  
  
                currNode = currNode.Parent;  
            }  
  
            result.Reverse();  
  
            return result;  
        }  
  
        public List<AstarNode> findPathPixel(int startX, int startY, int endX, int endY)  
        {  
            int sx = startX / pixelFormat;  
            int sy = startY / pixelFormat;  
            int ex = endX / pixelFormat;  
            int ey = endY / pixelFormat;  
  
            List<AstarNode> result = findPath(sx, sy, ex, ey);  
  
            if (result == null)  
            {  
                return null;  
            }  
  
            for (int i = 0; i < result.Count; ++i)  
            {  
                result[i].X *= pixelFormat;  
                result[i].Y *= pixelFormat;  
            }  
  
            return result;  
        }  
  
        public void enableMapData(int x, int y, bool value)  
        {  
            mapData[x, y] = value;  
        }  
  
        public void enableMapDataPixel(int x, int y, bool value)  
        {  
            int px = x / pixelFormat;  
            int py = y / pixelFormat;  
  
            enableMapData(px, py, value);  
        }  
  
        public void syncMapData(int x, int y)  
        {  
            mapData[x, y] = !mapData[x, y];  
        }  
  
        public void syncMapDataPixel(int x, int y)  
        {  
            int px = x / pixelFormat;  
            int py = y / pixelFormat;  
  
            syncMapData(px, py);  
        }  
  
        public void enableMapDataAll(bool value)  
        {  
            for (int w = 0; w < mapWidth; ++w)  
            {  
                for (int h = 0; h < mapHeight; ++h)  
                {  
                    mapData[w, h] = value;  
                }  
            }  
        }  
  
        public void initMapData(int _widthPixel, int _heightPixel, int _pixelFormat)  
        {  
            int width = _widthPixel / _pixelFormat;  
            int height = _heightPixel / _pixelFormat;  
  
            pixelFormat = _pixelFormat;  
            mapData = new bool[width, height];  
            mapWidth = width;  
            mapHeight = height;  
  
            enableMapDataAll(true);  
        }  
    }  
}
using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Data;  
using System.Drawing;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using System.Windows.Forms;  
  
namespace testAstar  
{  
    public class Player  
    {  
        public int ID;  
        public int X;  
        public int Y;  
        public int TargetX;  
        public int TargetY;  
        public List<AstarNode> Paths;  
        public int PathIndex;  
        public Color PathColor;  
        public Color ShowColor;  
  
        public void delete(Astar astar)  
        {  
            astar.enableMapDataPixel(X, Y, true);  
        }  
  
        public void update(Astar astar)  
        {  
            if (Paths != null)  
            {  
                if (PathIndex < Paths.Count)  
                {  
                    int tx = Paths[PathIndex].X;  
                    int ty = Paths[PathIndex].Y;  
  
                    if (astar.canWalkPixel(tx, ty))  
                    {  
                        astar.enableMapDataPixel(X, Y, true);  
                        X = tx;  
                        Y = ty;  
                        astar.enableMapDataPixel(X, Y, false);  
                        PathIndex++;  
                    }  
                    else  
                    {  
                        astar.enableMapDataPixel(X, Y, true);  
                        Paths = astar.findPathPixel(X, Y, TargetX, TargetY);  
                        PathIndex = 0;  
                    }  
                }  
                else  
                {  
                    astar.enableMapDataPixel(X, Y, true);  
                    Paths = null;  
                    PathIndex = 0;  
                }  
            }  
            else  
            {  
                int x = Form1.rand.Next(0, Form1.MapWidth);  
                int y = Form1.rand.Next(0, Form1.MapHeight);  
  
                if (astar.canWalkPixel(x, y))  
                {  
                    TargetX = x;  
                    TargetY = y;  
                    Paths = astar.findPathPixel(X, Y, x, y);  
                    PathIndex = 0;  
                }  
            }  
        }  
  
        public void render(Astar astar, Graphics g)  
        {  
            if (Paths != null)  
            {  
                for (int i = PathIndex; i < Paths.Count; ++i)  
                {  
                    g.FillRectangle(new SolidBrush(PathColor), new Rectangle(Paths[i].X, Paths[i].Y, astar.PixelFormat, astar.PixelFormat));  
                }  
            }  
  
            g.FillRectangle(new SolidBrush(ShowColor), new Rectangle(X, Y, astar.PixelFormat, astar.PixelFormat));  
  
            g.DrawString(ID.ToString(), new Font("楷体", 14, FontStyle.Bold), Brushes.Black, X, Y);  
        }  
    }  
  
    public partial class Form1 : Form  
    {  
        public static int MapWidth = 640;  
        public static int MapHeight = 480;  
  
        public static Random rand = new Random((int)DateTime.Now.Ticks);  
  
        private Astar astar = new Astar();  
        private Bitmap surface = null;  
        private Graphics g = null;  
  
        private List<Player> players = new List<Player>();  
  
        private bool[] keys = new bool[256];  
  
        private void init()  
        {  
            pictureBox1.Location = Point.Empty;  
            pictureBox1.ClientSize = new System.Drawing.Size(MapWidth, MapHeight);  
  
            surface = new Bitmap(MapWidth, MapHeight);  
            g = Graphics.FromImage(surface);  
  
            astar.initMapData(MapWidth, MapHeight, 16);  
  
            for (int i = 0; i < keys.Length; ++i)  
            {  
                keys[i] = false;  
            }  
        }  
  
        private void update()  
        {  
            foreach (Player p in players)  
            {  
                p.update(astar);  
            }  
        }  
  
        private void render()  
        {  
            g.Clear(Color.White);  
  
            bool[,] mapData = astar.MapData;  
  
            for (int w = 0; w < astar.MapWidth; ++w)  
            {  
                for (int h = 0; h < astar.MapHeight; ++h)  
                {  
                    if (!mapData[w, h])  
                    {  
                        g.FillRectangle(Brushes.Black, new Rectangle(w * astar.PixelFormat, h * astar.PixelFormat, astar.PixelFormat, astar.PixelFormat));  
                    }  
                }  
            }  
  
            foreach (Player p in players)  
            {  
                p.render(astar, g);  
            }  
  
            pictureBox1.Image = surface;  
        }  
  
        public Form1()  
        {  
            InitializeComponent();  
        }  
  
        private void Form1_Load(object sender, EventArgs e)  
        {  
            this.Text = "A*寻路算法(动态碰撞与寻路演示)A:增加 D:减少 左键:障碍设置 右键+数字键:对应编号物体的寻路";  
            init();  
  
            Timer gameTimer = new Timer();  
            gameTimer.Tick += gameTimer_Tick;  
            gameTimer.Interval = 100;  
            gameTimer.Start();  
        }  
  
        void gameTimer_Tick(object sender, EventArgs e)  
        {  
            update();  
            render();  
        }  
  
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)  
        {  
            if (e.Button == System.Windows.Forms.MouseButtons.Left)  
            {  
                astar.syncMapDataPixel(e.X, e.Y);  
            }  
            else if (e.Button == System.Windows.Forms.MouseButtons.Right)  
            {  
                /*endX = e.X; 
                endY = e.Y; 
                paths = astar.findPathPixel(px, py, endX, endY); 
                pathIndex = 0;*/  
  
                int pi = 0;  
                for (int i = 0; i < 256; ++i)  
                {  
                    if (keys[i])  
                    {  
                        pi = i - (int)Keys.D1;  
  
                        if (pi < 0 || pi >= players.Count)  
                        {  
                            return;  
                        }  
  
                        Player p = players[pi];  
  
                        p.TargetX = e.X;  
                        p.TargetY = e.Y;  
                        p.Paths = astar.findPathPixel(players[pi].X, players[pi].Y, e.X, e.Y);  
                        p.PathIndex = 0;  
  
                        players[pi] = p;  
  
                        return;  
                    }  
                }  
            }  
        }  
  
        private void Form1_KeyDown(object sender, KeyEventArgs e)  
        {  
            keys[e.KeyValue] = true;  
  
            if (e.KeyCode == Keys.A)  
            {  
                Player p = new Player();  
                p.ID = players.Count + 1;  
                p.X = 0;  
                p.Y = 0;  
                p.TargetX = 0;  
                p.TargetY = 0;  
                p.Paths = null;  
                p.PathIndex = 0;  
                p.ShowColor = Color.FromArgb(rand.Next(0, 255), rand.Next(0, 255), rand.Next(0, 255));  
                p.PathColor = Color.FromArgb(64, p.ShowColor);  
  
                players.Add(p);  
            }  
  
            if (e.KeyCode == Keys.D)  
            {  
                if (players.Count > 0)  
                {  
                    players[players.Count - 1].delete(astar);  
                    players.RemoveAt(players.Count - 1);  
                }  
            }  
        }  
  
        private void Form1_KeyUp(object sender, KeyEventArgs e)  
        {  
            keys[e.KeyValue] = false;  
        }  
    }  
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 文章转自http://www.cnblogs.com/technology/archive/2011/05/26/...
    MrPurussaurus阅读 1,201评论 0 3
  • 当然寻路算法不止 A* 这一种, 还有递归, 非递归, 广度优先, 深度优先, 使用堆栈等等, 有兴趣的可以研究研...
    胤醚貔貅阅读 574评论 1 1
  • 秋信 “树叶拎着信,匆匆地追赶秋天,来自天堂的慈祥,没有彷徨……” 恐惧 “生活的恐惧就是越来越难看……” 说种 ...
    通灵石阅读 267评论 0 1
  • 1、添加镜像文件 2、查看是否添加成功 3、安装cocoaPods并初始化 –––––––––––那么问题来了——...
    会编程的男神俊阅读 600评论 0 0
  • 八月的天空似乎没有了浪漫 没有了秋气宜人的温柔 门口的花花草草奄奄一息 鸟鸣也不再清脆 季节诚然忘记归途 找不到来...
    唐春元ok阅读 402评论 28 22