Unity3D A* 寻路算法

来源:互联网 发布:淘宝口碑店铺 编辑:程序博客网 时间:2024/06/06 17:01

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

A*寻路算法通常是应用于大型网络游戏中,A*算法通常应用在服务器中,在移动端游戏开发中,A*算法也可以在Unity端实现,下面给读者介绍一个插件A* PathFindingProject,它的下载地址:https://arongranberg.com/astar/download#,可以直接导入到Unity工程中,它目前支持直接寻路和网格寻路。在官网上有免费的版本和收费的版本,作为学习者可以下载免费的版本原理是一样的。下面通过它们提供的Demo,给读者介绍一下:

在导入到Unity工程后,使用时首选建立一个空的GameObject,然后将AstarPath脚本挂接上去。如下图所示:


这个AstarPath是整个A*寻路的核心组件,它计算了整个地面的寻路路径以及保存信息,它保存的是二进制文件格式,操作如下所示:

该类提供了保存,加载等函数,详情可以查看类AstarPathEditor,函数接口如下所示:

void DrawSerializationSettings () {serializationSettingsArea.Begin();GUILayout.BeginHorizontal();if (GUILayout.Button("Save & Load", level0LabelStyle)) {serializationSettingsArea.open = !serializationSettingsArea.open;}if (script.data.cacheStartup && script.data.file_cachedStartup != null) {GUIUtilityx.PushTint(Color.yellow);GUILayout.Label("Startup cached", thinHelpBox, GUILayout.Height(15));GUILayout.Space(20);GUIUtilityx.PopTint();}GUILayout.EndHorizontal();// This displays the serialization settingsif (serializationSettingsArea.BeginFade()) {script.data.cacheStartup = EditorGUILayout.Toggle(new GUIContent("Cache startup", "If enabled, will cache the graphs so they don't have to be scanned at startup"), script.data.cacheStartup);script.data.file_cachedStartup = EditorGUILayout.ObjectField(script.data.file_cachedStartup, typeof(TextAsset), false) as TextAsset;if (script.data.cacheStartup && script.data.file_cachedStartup == null) {EditorGUILayout.HelpBox("No cache has been generated", MessageType.Error);}if (script.data.cacheStartup && script.data.file_cachedStartup != null) {EditorGUILayout.HelpBox("All graph settings will be replaced with the ones from the cache when the game starts", MessageType.Info);}GUILayout.BeginHorizontal();if (GUILayout.Button("Generate cache")) {var serializationSettings = new Pathfinding.Serialization.SerializeSettings();serializationSettings.nodes = true;if (EditorUtility.DisplayDialog("Scan before generating cache?", "Do you want to scan the graphs before saving the cache.\n" +"If the graphs have not been scanned then the cache may not contain node data and then the graphs will have to be scanned at startup anyway.", "Scan", "Don't scan")) {MenuScan();}// Save graphsvar bytes = script.data.SerializeGraphs(serializationSettings);// Store it in a filescript.data.file_cachedStartup = SaveGraphData(bytes, script.data.file_cachedStartup);script.data.cacheStartup = true;}if (GUILayout.Button("Load from cache")) {if (EditorUtility.DisplayDialog("Are you sure you want to load from cache?", "Are you sure you want to load graphs from the cache, this will replace your current graphs?", "Yes", "Cancel")) {script.data.LoadFromCache();}}GUILayout.EndHorizontal();if (script.data.data_cachedStartup != null && script.data.data_cachedStartup.Length > 0) {EditorGUILayout.HelpBox("Storing the cached starup data on the AstarPath object has been deprecated. It is now stored " +"in a separate file.", MessageType.Error);if (GUILayout.Button("Transfer cache data to separate file")) {script.data.file_cachedStartup = SaveGraphData(script.data.data_cachedStartup);script.data.data_cachedStartup = null;}}GUILayout.Space(5);GUILayout.BeginHorizontal();if (GUILayout.Button("Save to file")) {string path = EditorUtility.SaveFilePanel("Save Graphs", "", "graph.bytes", "bytes");if (path != "") {var serializationSettings = Pathfinding.Serialization.SerializeSettings.Settings;if (EditorUtility.DisplayDialog("Include node data?", "Do you want to include node data in the save file. " +"If node data is included the graph can be restored completely without having to scan it first.", "Include node data", "Only settings")) {serializationSettings.nodes = true;}if (serializationSettings.nodes && EditorUtility.DisplayDialog("Scan before saving?", "Do you want to scan the graphs before saving? " +"\nNot scanning can cause node data to be omitted from the file if the graph is not yet scanned.", "Scan", "Don't scan")) {MenuScan();}uint checksum;var bytes = SerializeGraphs(serializationSettings, out checksum);Pathfinding.Serialization.AstarSerializer.SaveToFile(path, bytes);EditorUtility.DisplayDialog("Done Saving", "Done saving graph data.", "Ok");}}if (GUILayout.Button("Load from file")) {string path = EditorUtility.OpenFilePanel("Load Graphs", "", "");if (path != "") {byte[] bytes;try {bytes = Pathfinding.Serialization.AstarSerializer.LoadFromFile(path);} catch (System.Exception e) {Debug.LogError("Could not load from file at '"+path+"'\n"+e);bytes = null;}if (bytes != null) DeserializeGraphs(bytes);}}GUILayout.EndHorizontal();}serializationSettingsArea.End();}

该函数调用了JsonSerializer类中的两个函数如下所示:

public static void SaveToFile (string path, byte[] data) {#if NETFX_COREthrow new System.NotSupportedException("Cannot save to file on this platform");#elseusing (var stream = new FileStream(path, FileMode.Create)) {stream.Write(data, 0, data.Length);}#endif}/** Load the specified data from the specified path */public static byte[] LoadFromFile (string path) {#if NETFX_COREthrow new System.NotSupportedException("Cannot load from file on this platform");#elseusing (var stream = new FileStream(path, FileMode.Open)) {var bytes = new byte[(int)stream.Length];stream.Read(bytes, 0, (int)stream.Length);return bytes;}#endif}

场景通过AstarPath脚本布置完后,接下来开始寻路了,需要挂接如下的脚本:


其中AI脚本是用于查询物体的,它会调用Seeker脚本中的函数,同时Seeker会对场景做一些标识操作,这样AI寻路基本完成,其中AI脚本是继承AIPath类,非常方便我们自己编写,扩展功能,它是通过递归的方式进行路径查询,锁定目标的,它运行的效果如下所示:






0 0
原创粉丝点击