Unity3D A* 寻路算法

发表于2017-05-17
评论1 1.13w浏览

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类,非常方便我们自己编写,扩展功能,它是通过递归的方式进行路径查询,锁定目标的,它运行的效果如下所示:

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引