关闭

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

3189人阅读 评论(1) 收藏 举报
分类:

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


工程文件:http://pan.baidu.com/s/1mg1tnKk


0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

A星寻路算法之RVO动态寻路

A星寻路算法之RVO动态寻路A星寻路算法之RVO动态寻路A星寻路算法之RVO动态寻路 未完待续
  • qq563129582
  • qq563129582
  • 2015-12-30 19:15
  • 3743

[OpenGL]游戏中的动态追踪算法

我们应该很熟悉游戏里这样的画面:         1.npc在地图上漫无目的的游走         2.玩家操控主角靠近npc         3.npc感受到玩家的存在...
  • ZJU_fish1996
  • ZJU_fish1996
  • 2016-08-12 03:59
  • 3470

一种高效的寻路算法——B*寻路算法

转:http://qinysong.iteye.com/blog/678941 在此把这个算法称作B* 寻路算法(Branch Star 分支寻路算法,且与A*对应),本算法适用于游戏中怪物...
  • bywuu
  • bywuu
  • 2015-06-23 15:38
  • 2217

unity寻路——一劳永逸地解决寻路问题

一劳永逸地解决寻路问题 作者:PaulT 译者:trcj 原文:http://www.ai-blog.net/archives/000152.html            通常我都会尽量避...
  • tchenjiant
  • tchenjiant
  • 2015-10-30 14:02
  • 13614

unity3d多人寻路问题方案

转自:http://tieba.baidu.com/p/3397037553 相信大家用unity3d自带navmeshagent寻路的时候,一定会碰到多人寻路相互挤压的问题。 这个我已经解决了,...
  • u011926026
  • u011926026
  • 2017-03-19 10:59
  • 589

[图形学] 《Real-Time Rendering》碰撞检测(一)

原文有35页,容我慢慢翻译,第一部分翻译了10页      reference:《Real-Time Rendering》 目录 17   前言 17.1 和射线的碰撞检测 17.2 使...
  • ZJU_fish1996
  • ZJU_fish1996
  • 2016-08-29 17:42
  • 3446

《实时碰撞检测算法技术》读书笔记(五):最近点计算(上)

点到面的最近距离 一点P以及法线n定义了一个平面π,所有该平面上的点X都满足方程n·(X - P) = 0(即从点P指向X的向量垂直于n)。现令Q为空间内任意一点,则面内距Q的最近点R为点QQ在该面上...
  • u010387196
  • u010387196
  • 2014-03-04 23:09
  • 1082

A*寻路算法的C#实现

详细讲述了A*寻路算法,有下列地方值得学习1. 不要用AStarPathNode来构造PathFinder的Matrix,Matrix仅仅是byte[,],需要对Matrix中某个元素进行处理时再构造...
  • kenkao
  • kenkao
  • 2010-04-13 12:34
  • 5922

Unity3D&AI:寻路(1):A星寻路,在Unity上用C#代码实现。

A星寻路原理:略,网上可搜到通过理解A星寻路的原理可以设计出以下流程图: 流程图通过processon制作Unity C#脚本实现代码:各个坐标节点为单独的gameObject,并自带脚本:usin...
  • liu_if_else
  • liu_if_else
  • 2016-03-18 21:26
  • 2411

一个高效的A-star寻路算法(八方向)(

原作者是http://www.codefans.net的JAROD 之所以说这个A-star算法高效,是因为它的open-list和close-list使用的完全是静态数组,这样就极大地降低了入栈出栈...
  • OnafioO
  • OnafioO
  • 2014-11-13 22:01
  • 6841
    个人资料
    • 访问:95982次
    • 积分:115
    • 等级:
    • 排名:千里之外
    • 原创:38篇
    • 转载:2篇
    • 译文:0篇
    • 评论:33条
    文章分类
    最新评论