Unity3D A* 寻路算法
发表于2017-05-17
A*算法在游戏领域是最常见的算法,通常是应用于大型网络游戏中,在移动端游戏开发中,A*算法也可以在Unity端实现。下面给大家介绍一个插件A* PathFindingProject,可以直接导入到Unity工程中,它目前支持直接寻路和网格寻路。再通过一个Demo,给大家详细介绍下。
在导入到Unity工程后,使用时首选建立一个空的GameObject,然后将AstarPath脚本挂接上去。如下图所示:
这个AstarPath是整个A*寻路的核心组件,它计算了整个地面的寻路路径以及保存信息,它保存的是二进制文件格式,操作如下所示:
该类提供了保存,加载等函数,详情可以查看类AstarPathEditor,函数接口如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | 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 settings if (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 graphs var bytes = script.data.SerializeGraphs(serializationSettings); // Store it in a file script.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类中的两个函数如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public static void SaveToFile ( string path, byte [] data) { f NETFX_CORE throw new System.NotSupportedException( "Cannot save to file on this platform" ); lse using (var stream = new FileStream(path, FileMode.Create)) { stream.Write(data, 0, data.Length); } ndif } /** Load the specified data from the specified path */ public static byte [] LoadFromFile ( string path) { f NETFX_CORE throw new System.NotSupportedException( "Cannot load from file on this platform" ); lse using (var stream = new FileStream(path, FileMode.Open)) { var bytes = new byte [( int )stream.Length]; stream.Read(bytes, 0, ( int )stream.Length); return bytes; } ndif } |
场景通过AstarPath脚本布置完后,接下来开始寻路了,需要挂接如下的脚本:
其中AI脚本是用于查询物体的,它会调用Seeker脚本中的函数,同时Seeker会对场景做一些标识操作,这样AI寻路基本完成,其中AI脚本是继承AIPath类,非常方便我们自己编写,扩展功能,它是通过递归的方式进行路径查询,锁定目标的,它运行的效果如下所示: