JPS(Jump Point Search) C# 跳点寻路算法完整代码实现

1.引言

        ①:本文不提供跳点寻路算法基本概念,因为别的博主有很多写的非常好,看本文之前最好先去了解一下跳点寻路算法的基本概念与思路。

        ②:代码写的是很基础的语法,适合初学者,不包含高级写法,因为跟我一样的初学者看那些高级语法代码,简直头疼,云里雾里,所以写完整的C#代码只是为初学者有个基本的代码过程概念,想深入研究还得靠其他博主的文章。

2.代码

  2.1 寻路类

//寻路类
class JpsSearch
{
    //模拟地图 初定地图大小  10*20
    //1:起点  9:终点    8:障碍     7:寻路的路经
    byte[,] map = new byte[,]
    {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0 },
        {0,0,0,0,1,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,8,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,8,0,8,0,0,8,0,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,8,0,8,0,0,8,9,8,0 },
        {0,0,0,0,0,0,0,0,0,0,0,8,0,8,8,0,0,8,0,0 },
        {0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0 },
    };

    //地图大小
    int mapX = 10;
    int mapY = 20;


    //起点和终点
    Point startPoint;
    Point endPoint;

    //开启表  
    List<Point> openList;
    //封闭表
    List<Point> closeList;

    public void FindMain()
    {
        //定义起点
        startPoint = new Point(4, 4);
        startPoint.G = 0;
        startPoint.H = 0;

        //定义终点
        endPoint = new Point(7, 17);
        endPoint.G = 0;
        endPoint.H = 0;

        //开启表
        openList = new List<Point>();
        //封闭表
        closeList = new List<Point>();


        //openList 链表中第一个
        Point firstPoint;

        //跳点
        Point jumpPoint;

        //邻居节点
        Point neighbourPoint;

        //当前点的邻居点
        List<Point> neighbourList = new List<Point>();

        //方向
        int xDIr, yDir;

        //将起点加入开启表中
        if(startPoint.x == endPoint.x && startPoint.y == endPoint.y )
        {
            startPoint = endPoint;
        }
        else
        {
            openList.Add(startPoint);
        }


        while(openList.Count >0)
        {
                
            //获取第总估计路程最少F的节点,默认以及从小到大排序
            firstPoint = openList.First();
            openList.RemoveAt(0);

            //放入封闭表中
            closeList.Add(firstPoint);

            //获取当前节点的邻居节点
            neighbourList = NeighboursList(firstPoint);
            //遍历所有邻居节点
            for (int i = 0; i < neighbourList.Count; i++)
            {
                //记录一个邻居节点
                neighbourPoint = neighbourList[i];
                //先检测当前邻居节点是否是终点
                if(neighbourPoint.x == endPoint.x && neighbourPoint.y == endPoint.y)
                {
                    endPoint.parentPoint = firstPoint;
                    openList.Clear();
                    break;
                }

                //方向
                xDIr = Math_Dir(neighbourPoint.x - firstPoint.x);
                yDir = Math_Dir(neighbourPoint.y - firstPoint.y);

                //找到跳跃点
                jumpPoint = SearchPoint(neighbourPoint.x,neighbourPoint.y,xDIr,yDir);
                   
                if(jumpPoint != null)
                {
                    //检测当前条约点是否是终点
                    if (jumpPoint.x == endPoint.x && jumpPoint.y == endPoint.y)
                    {
                        //设置父节点
                        jumpPoint.parentPoint = firstPoint;
                        endPoint.parentPoint = jumpPoint;
                        //清楚链表是为了跳出while循环
                        openList.Clear();
                        break;
                    }

                    //再次检测是否找到终点
                    if(openList.Count > 0)
                    {
                        if(openList[0].x == endPoint.x && openList[0].y == endPoint.y)
                        {
                            jumpPoint.parentPoint = firstPoint;
                            endPoint.parentPoint = jumpPoint;
                            openList.Clear();
                            break;
                        }
                    }

                    if(!CheckInCloseList(jumpPoint))
                    {
                        //获取jumpPoint 的G H
                        float jumpG = Get_G(jumpPoint, firstPoint); 
                        float jumpH = Get_H(jumpPoint);

                        jumpPoint.parentPoint = firstPoint;
                        jumpPoint.G = jumpG;
                        jumpPoint.H = jumpH;

                        if (CheckInOpneList(jumpPoint) && (jumpG + jumpH) < jumpPoint.Get_F())
                        {
                            // jumpPoint.parentPoint = firstPoint;
                            jumpPoint.G = jumpG;
                            jumpPoint.H = jumpH;
                        }
                        else
                        {
                            AddInOpenList(jumpPoint);
                        }

                    }
                        
                }

            }


        }

        //地图绘制
        DrawMap();
    }

    //获取传入节点的邻居节点,起点就是邻居八个节点,非起点就方向上的五个邻居节点
    List<Point> NeighboursList(Point point)
    {
        //存储当前节点邻居节点 point 的邻居
        List<Point> neighboursList = new List<Point>();

        //当前传入节点 point 的父节点
        Point parentPoint = point.parentPoint;

        //如果父节点是空的,说明是起点
        if(parentPoint == null)
        {
            //获取起点周围八个节点
            for(int x = point.x - 1;x <= point.x + 1;x++)
            {
                for(int y = point.y-1;y<= point.y+1;y++)
                {
                    //排除自身点
                    if(x== point.x && y== point.y)
                    {
                        continue;
                    }

                    //如果是可走节点,就存入邻居节点表中
                    if(IsCanWalk(x,y))
                    {
                        neighboursList.Add(new Point(x,y));
                    }
                }
            }

            return neighboursList;
        }

        //非起点获取方向上可行走的邻居

        //获取当前节点的前进方向  通过当前节点 - 父节点 计算
        int xDir = Math_Dir(point.x - parentPoint.x);
        int yDir = Math_Dir(point.y - parentPoint.y);

        //斜方向行走
        if (xDir != 0 && yDir != 0)
        {
            // point 点的四个方向是否可以行走  上下左右
            bool upWalk = IsCanWalk(point.x, point.y + yDir);
            bool downWalk = IsCanWalk(point.x, point.y - yDir);

            bool leftWalk = IsCanWalk(point.x - xDir, point.y);
            bool rightWalk = IsCanWalk(point.x + xDir, point.y);

            //如果行驶方向的上方可以行走
            if (upWalk)
            {
                //记录邻居点
                neighboursList.Add(new Point(point.x, point.y + yDir));

                //也记录强迫邻居点
                if (!leftWalk && IsCanWalk(point.x - xDir, point.y + yDir))
                {
                    neighboursList.Add(new Point(point.x - xDir, point.y + yDir));
                }
            }
            //如果行驶方向的右方方可以行走
            if (rightWalk)
            {
                //记录邻居点
                neighboursList.Add(new Point(point.x + xDir, point.y));

                //也记录强迫邻居点
                if (!downWalk && IsCanWalk(point.x + xDir, point.y - yDir))
                {
                    neighboursList.Add(new Point(point.x + xDir, point.y - yDir));
                }
            }

            //检测正前方行驶方向
            if (upWalk || rightWalk)
            {
                //如果正行驶方向可以行驶就记录节点
                if (IsCanWalk(point.x + xDir, point.y + yDir))
                {
                    neighboursList.Add(new Point(point.x + xDir, point.y + yDir));
                }
            }
        }
        //横向或纵向行驶
        else
        {
            //横向
            if (yDir == 0)
            {
                //如果正前方可以行驶
                if (IsCanWalk(point.x + xDir, point.y))
                {
                    neighboursList.Add(new Point(point.x + xDir, point.y));

                    //上下强迫邻居点
                    if (!IsCanWalk(point.x, point.y + 1) && IsCanWalk(point.x + xDir, point.y + 1))
                    {
                        neighboursList.Add(new Point(point.x + xDir, point.y + 1));
                    }
                    if (!IsCanWalk(point.x, point.y - 1) && IsCanWalk(point.x + xDir, point.y - 1))
                    {
                        neighboursList.Add(new Point(point.x + xDir, point.y - 1));
                    }

                }
            }
            //纵向
            else
            {
                //如果正上方可以行驶
                if (IsCanWalk(point.x,point.y + yDir))
                {
                    neighboursList.Add(new Point(point.x,point.y + yDir));

                    //左右强迫邻居点
                    if(!IsCanWalk(point.x + 1,point.y) && IsCanWalk(point.x + 1,point.y + yDir))
                    {
                        neighboursList.Add(new Point(point.x+1,point.y + yDir));
                    }

                    if(!IsCanWalk(point.x - 1,point.y) && IsCanWalk(point.x - 1,point.y + yDir))
                    {
                        neighboursList.Add(new Point(point.x - 1,point.y + yDir));
                    }
                }
            }
        }


        return neighboursList;
    }

    //找出的跳点
    Point SearchPoint(int curX,int curY,int xDir,int yDir)
    {
        //检测当前节点可否行走
        if (!IsCanWalk(curX, curY))
            return null;

        //检测是否达到终点
        if(curX == endPoint.x && curY == endPoint.y)
        {
            //在最前面插入终点,方便结束循环、递归
            openList.Insert(0,endPoint);
            return new Point(curX,curY);
        }

        //对角方向递归检测
        if(xDir !=0 && yDir !=0)
        {
            //检查当前节点是否有强迫邻居
            if((IsCanWalk(curX+xDir,curY-yDir) && !IsCanWalk(curX,curY-yDir))
                ||(IsCanWalk(curX-xDir,curY+yDir) && !IsCanWalk(curX-xDir,curY)))
            {
                return new Point(curX,curY);
            }

            //继续横向递归查找强迫邻居
            if(SearchPoint(curX+xDir,curY,xDir,0) != null)
            {
                return new Point(curX, curY);
            }

            //继续纵向递归查找强迫邻居
            if (SearchPoint(curX,curY+yDir,0,yDir) != null)
            {
                return new Point(curX,curY);
            }
                
        }
        //横向深度递归
        else if (yDir != 0)
        {
            //深度递归查找当前方向是否有强迫邻居
            if((IsCanWalk(curX +1,curY+yDir) && !IsCanWalk(curX+1,curY))
                || (IsCanWalk(curX-1,curY+yDir) && !IsCanWalk(curX-1,curY)))
            {
                return new Point(curX,curY);
            }
        }
        //纵向向深度递归
        else if (xDir != 0)
        {
            //深度递归查找当前方向是否有强迫邻居
            if((IsCanWalk(curX+xDir,curY+1) && !IsCanWalk(curX,curY+1))
                || (IsCanWalk(curX+xDir,curY-1) && !IsCanWalk(curX,curY-1)))
            {
                return new Point(curX,curY);
            }
        }

        //继续斜走递归
        return SearchPoint(curX+xDir,curY+yDir,xDir,yDir);
    }

    //检测当前位置是否可走
    bool IsCanWalk(int x,int y)
    {
        //检测坐标是否超过地图界限
        if (x < 0 || x > mapX-1 || y < 0 || y > mapY-1)
            return false;

        return map[x, y] != 8;
    }

    //获取单轴行驶的方向
    //clampNum 当前节点与父节点的一个轴差值
    int Math_Dir(int clampNum)
    {
        //差值限制计算
        if (clampNum <= -1)
            return -1;
        else if (clampNum >= 1)
            return 1;
        else
            return 0;
    }

    //检测当前节点是否在闭合表中
    bool CheckInCloseList(Point point)
    {
        Point tempPoint;
        for(int i = 0;i<closeList.Count;i++)
        {
            tempPoint = closeList[i];

            //是否在封闭表中
            if (tempPoint.x == point.x && tempPoint.y == point.y)
                return true;
        }


        return false;
    }

    //检测当前节点是否在开启表中
    bool CheckInOpneList(Point point)
    {
        Point tempPoin;
        for (int i = 0; i < openList.Count; i++)
        {
            tempPoin = closeList[i];
            if (point.x == tempPoin.x && point.y == tempPoin.y)
                return true;
        }
        return false;
    }

    //插入一个节点在开启表中,判断大小
    void AddInOpenList(Point point)
    {
        Point tempPoint;
        //检测原本是否包含相同位置的节点,有就去除
        for(int i = 0;i<openList.Count;i++)
        {
            tempPoint = openList[i];
            if (point.x == tempPoint.x && point.y == tempPoint.y)
            {
                openList.RemoveAt(i);
                break;
            }
        }

        //重新插入
        for(int i = 0;i<openList.Count;i++)
        {
            tempPoint = openList[i];
            if(tempPoint.Get_F() < tempPoint.Get_F())
            {
                openList.Insert(i,tempPoint);
                return;
            }
        }

        openList.Add(point);
    }

    //获取G
    float Get_G(Point point,Point parentPoint)
    {
        if (parentPoint == null)
        {
            return 0;
        }
        //如果是平行关系
        else if (point.x == parentPoint.x || point.y == parentPoint.y)
        {
            return parentPoint.G + (float)Math.Sqrt(Math.Pow(point.x - parentPoint.x, 2) + Math.Pow(point.y - parentPoint.y, 2));
        }
        //如果是对角关系
        else
        {
            return parentPoint.G + (float)Math.Sqrt(Math.Pow(point.x - parentPoint.x, 2) + Math.Pow(point.y - parentPoint.y, 2));
        }
    }

    //获取H
    float Get_H(Point point)
    {
        return  (float)Math.Sqrt(Math.Pow(point.x - endPoint.x, 2) + Math.Pow(point.y - endPoint.y, 2));
    }

    //地图绘制
    void DrawMap()
    {
        Point tempPoint = endPoint.parentPoint;
        Point parentPoint;

        int xDir;
        int yDir;
        int maxNum;
        while (tempPoint != null)
        {
            parentPoint = tempPoint.parentPoint;
            if (parentPoint == null)
            {
                break;
            }
            xDir = Math_Dir(tempPoint.x - parentPoint.x);
            yDir = Math_Dir(tempPoint.y - parentPoint.y);
            DrawRow(tempPoint.x,tempPoint.y,parentPoint,xDir,yDir);

            map[tempPoint.x, tempPoint.y] = 7;
            tempPoint = tempPoint.parentPoint;

        }


        Console.WriteLine("地图绘制");
        for (int i = 9; i >= 0; i--)
        {
            for (int j = 0; j <= 19; j++)
            {
                Console.Write(map[i, j] + "  ");

            }
            Console.WriteLine();
        }
    }

    //路线绘制
    void DrawRow(int x,int y,Point parentPoint,int xDir,int yDir)
    {
        if(parentPoint == null)
        {
            return;
        }
        int curX = x - xDir;
        int curY = y - yDir;
        //如果绘制到达目的地
        if(curX == parentPoint.x && curY == parentPoint.y)
        {
            return;
        }

        map[curX, curY] = 7;


        DrawRow(curX, curY, parentPoint, xDir, yDir);
    }
}

    2.2节点类

//节点类
class Point
{
    //坐标位置
    public int x;
    public int y;

    //父节点
    public Point parentPoint;

    //寻路预算
    public float G;         //以及行驶的预算
    public float H;         //当前格子移动到目标点的预算

        
    //构造函数  初始化位置
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }


    //获取估计总路程(G+H)
    public float Get_F()
    {
        return G + H;
    }
}

2.3 Main类

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

class Program
{
    static void Main(string[] args)
    {
        JpsSearch jpsSearch = new JpsSearch();
        jpsSearch.FindMain();


        Console.Read();
    }
}

3运行结果

0:空地  1: 起点    7:路线     8:障碍物

4.结语

        代码可能有点多和啰嗦,但是初学者跟着敲一遍可能会有很深刻的理解,不想敲的可以直接复制粘贴到编辑器上运行看看。

代码有很多不好的地方请谅解,代码如有错误,提出我纠正,大家的共同学习。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JPSJump Point Search)是一种基于A*算法寻路算法,用于寻找最短路径。它通过剪枝操作来减少搜索节点的数量,从而提高搜索效率。下面是使用C语言实现JPS寻路算法的一般步骤: 1. 定义节点结构体Node,包括坐标、距离和父节点等属性。 2. 定义地图数组,用于表示可通过和不可通过的区域。0表示可通过,1表示不可通过。 3. 实现启发式函数,用于估计从当前节点到目标节点的距离。 4. 实现检测节点是否在地图范围内的函数。 5. 实现检测节点是否可以跳跃的函数。通过观察判断节点的相邻节点中是否存在跳跃点。 6. 实现生成可跳跃点的函数。先找到当前节点的有效相邻节点,然后根据具体情况判断并返回可跳跃的节点。 7. 实现JPS算法的主函数。使用一个优先队列来存储待搜索的节点,初始化起点并将其添加到队列中。不断从队列中取出节点,检测相邻节点是否为目标节点,如果是则返回最短路径;否则,根据已知信息进行推导,生成可跳跃点并将其添加到队列中。 8. 实现回溯函数,用于从目标节点开始通过父节点信息回溯生成最短路径。 除了上述步骤外,还可以进行一些优化,如使用位运算代替乘除、设置限制条件避免重复计算等,以提高算法的执行效率。 以上是一个简要的JPS寻路算法的C语言实现步骤,具体实现还需根据具体场景和需求进行调整和完善。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值