游戏开发岗面试A*算法

Posted 头号理想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发岗面试A*算法相关的知识,希望对你有一定的参考价值。

A*寻路来解决什么问题?

A*寻路来计算玩家行进最短路径

A*基本原理

不停的找周围的点,选出一个点作为起点,再循环的找,知道找到终点

A*详细原理

1.寻路消耗公式
f(寻路消耗)=g(离起点距离 分为两种 上下左右为1 其他四个角为1.4 根号2)+ h(离终点距离 使用曼哈顿街区的思想)
2.开启列表(所有点的存放)
3.关闭列表(最优点的存放)
每次判断最优点是否为终点 如果是 停止寻路,否则继续寻路
4.格子对象的父对象(用来确定最终的路径)

需求分析

需要一个格子类 寻路管理器(管理所有的格子)
开启列表 关闭列表

格子类
成员变量:
寻路消耗 f g h
父亲格子father
坐标 x y
格子类型 type

方法:
构造函数

寻路管理器类
成员变量:
格子数组 nodes
开启列表 openlist
关闭列表 clostlist
地图的边界 mapx mapy

方法:
初始化地图格子方法
寻找路径方法(起点,终点)

源码

网格类

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

public enum E_node_type

    walk,//可以走
    stop//不能走


//A*格子类
public class AStarNode 

    public float f;//寻路消耗
    public float g;//距离起点距离
    public float h;//距离终点距离

    public AStarNode father;//父对象

    //格子对象坐标
    public int x;
    public int y;

    public E_node_type type;

    public AStarNode(int x,int y ,E_node_type type)
    
        //传入坐标和类型
        this.x = x;
        this.y = y;
        this.type = type;
    



网格管理器类

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

//管理器
public class AStarManager 

    private static AStarManager Instance;
    public static AStarManager instance
    
        get
        
            if(Instance == null)
                Instance = new AStarManager();
            
            return Instance;
        
    

    private AStarNode[,] nodes;//地图相关对象容器
    private List<AStarNode> openList;//开启列表
    private List<AStarNode> closeList;//关闭列表

    private int mapx;
    private int mapy;
    //宽高

    public void initmapinfo(int x,int y)
    
        //初始化地图信息
        //根据宽高 创建格子
        this.mapx = x;
        this.mapy = y;

        nodes = new AStarNode[x,y];//声明容器装多少个格子
        for(int i = 0; i < x; i++)
        
            for(int j = 0; i < y; j++)
            
                AStarNode node = new AStarNode(i, j, Random.Range(0, 100) < 20 ? E_node_type.stop : E_node_type.walk);
                //随机阻挡
            
        

    
    //寻路方法
    public List<AStarNode> findpath(Vector2 startpos,Vector2 endpos)
    
        //首先判断传入点是否合法 (不是阻挡点)
        //实际开发需要转换坐标
        if (startpos.x < 0 || startpos.x >= mapx || startpos.y < 0 || startpos.y > mapy||
            endpos.x < 0 || endpos.x >= mapx || endpos.y < 0 || endpos.y > mapy)
        
            Debug.LogError("由于在地图边界外");
            return null;//不合法
        
        
        AStarNode start = nodes[(int)startpos.x, (int)startpos.y];
        AStarNode end = nodes[(int)endpos.x, (int)endpos.y];
        if (start.type == E_node_type.stop || end.type == E_node_type.stop)
        
            Debug.LogError("该点是阻挡点");
            return null;//不合法
        
        openList.Clear();
        closeList.Clear();//清空数据 防止影响

        //把开始点初始化之后放入关闭列表中
        start.father = null;
        start.f = 0;
        start.g = 0;
        start.h = 0;

        closeList.Add(start);

        while (true)
        
            //寻找周围的所有点
            FindnearNodeToopenList(start.x - 1, start.y - 1, 1.4f, start, end);
            FindnearNodeToopenList(start.x - 1, start.y, 1, start, end);
            FindnearNodeToopenList(start.x - 1, start.y + 1, 1.4f, start, end);

            FindnearNodeToopenList(start.x, start.y - 1, 1f, start, end);
            FindnearNodeToopenList(start.x, start.y + 1, 1f, start, end);

            FindnearNodeToopenList(start.x + 1, start.y - 1, 1.4f, start, end);
            FindnearNodeToopenList(start.x + 1, start.y, 1f, start, end);
            FindnearNodeToopenList(start.x + 1, start.y - 1, 1.4f, start, end);

            if (openList.Count == 0)//开始列表为空 还没有找到终点
            
                Debug.LogError("死路!");
                return null;
            


            //开启列表放入上边的八个点
            openList.Sort(sortopenlist);//自定义排序

            //放入关闭列表
            closeList.Add(openList[0]);

            start = openList[0];//找到的这个点变成新的起点 进行新的计算
            openList.RemoveAt(0);//移除

            if (start == end)
            
                //寻路结束
                List<AStarNode> path = new List<AStarNode>();
                path.Add(end);
                while (end.father != null)
                
                    path.Add(end.father);
                    end = end.father;
                
                //翻转一下
                path.Reverse();
                return path;//找到路径
            
        

    
    private int sortopenlist(AStarNode a,AStarNode b)//自定义排序
    
        if (a.f > b.f)
        
            return 1;
        
        else
        
            return -1;
        
    

    //把邻近的点放入开启列表中去
    private void FindnearNodeToopenList(int x,int y,float g,AStarNode father,AStarNode end)
    
        //边间检测
        if (x < 0 || x >= mapx || y < 0 || y >= mapy)
        
            return;
        

        AStarNode node = nodes[x, y];
        //判断是否是边界,阻挡
        if (node == null||node.type==E_node_type.stop||openList.Contains(node)||closeList.Contains(node))
        
            return;
        

        //计算f值 f=g+h=起点距离+终点距离
        node.father = father;//记录父对象
        node.g = father.g + g;//父亲离起点距离 + 我离父亲距离=我离起点的距离
        node.h = Mathf.Abs(end.x - node.x) + Mathf.Abs(end.y - node.y);//曼哈顿街区算法
        node.f = node.g + node.h;


        openList.Add(node);//存入开启了列表中

    


之后的测试实例我还会更新的 希望以上所写对大家有所帮助

2021.3.11更新

在上一场面试中 我和面试官聊到了很多A*算法相关的问题
其中有一个 面试官问 如果地图中有两种地形,一种是草地,一种是沙地
然后有一个角色优先考虑走沙地 问我怎么实现

我当时很快想到的就是 在A*算法中每个格子设置一个值 就作为草地和沙地的权重
优先选择权重较大的 比如设置草地为1 沙地为2 优先选择沙地

然后面试官还问了我一个问题:如何优化A*算法中角色移动的轨迹(使得路径尽量保持类似直线)
当时我没想到好的方法 只想到了将格子扩大 这样可以稍微优化
但是很明显这不是面试官想要的结果
然后面试结束之后 我回想起来 我们可以设置上一次路径的方向 优先选择方向相同的
比如上一步是向上走的 我们就给各个方向设置一个权重 离上一次的路线角度越小 权重越大 反之依然
最后我们还是找权重大的路线

以上是关于游戏开发岗面试A*算法的主要内容,如果未能解决你的问题,请参考以下文章

游戏开发面经我在阿里HRG面这关跪掉了,游戏客户端开发岗,总结一下(阿里 | 游戏 | 凉面面经)

游戏开发面经我在阿里HRG面这关跪掉了,游戏客户端开发岗,总结一下(阿里 | 游戏 | 凉面面经)

游戏开发面经我在阿里HRG面这关跪掉了,游戏客户端开发岗,总结一下(阿里 | 游戏 | 凉面面经)

字节跳动面试——算法岗

最新整理:字节跳动历年Java中高级面试题全收录!后台开发JAVA岗

字节跳动面经——实习算法岗