Representing the world with a self-made navigation mesh

Sometimes, a custom navigation mesh is necessary for dealing with difficult situations such as different types of graphs, but placing the graph's vertices manually is troublesome because it requires a lot of time to cover large areas.

We will learn how to use a model's mesh in order to generate a navigation mesh based on its triangles' centroids as vertices, and then leverage the heavy lifting from the previous recipe we learned.

Getting ready

This recipe requires some knowledge of custom editor scripting and understanding and implementing the points of visibility in the graph representation. Also, it is worth mentioning that the script instantiates a CustomNavMesh game object automatically in the scene and requires a prefab assigned, just like any other graph representation.

Finally, it's important to create the following class, deriving from GraphVisibility:

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

public class CustomNavMesh : GraphVisibility
{
    public override void Start()
    {
        instIdToId = new Dictionary<int, int>();
    }   
}

How to do it...

We will create an editor window for easily handling the automation process without weighing down the graph's Start function, delaying the scene loading.

  1. Create the CustomNavMeshWindow class and place it in a directory called Editor:
    using UnityEngine;
    using UnityEditor;
    using System.Collections;
    using System.Collections.Generic;
    
    public class CustomNavMeshWindow : EditorWindow
    {
        // next steps here
    }
  2. Add the attributes to the editor window:
    static bool isEnabled = false;
    static GameObject graphObj;
    static CustomNavMesh graph;
    static CustomNavMeshWindow window;
    static GameObject graphVertex;
  3. Implement the function for initializing and showing the window:
    [MenuItem("UAIPC/Ch02/CustomNavMeshWindow")]
    static void Init()
    {
        window = EditorWindow.GetWindow<CustomNavMeshWindow>();
        window.title = "CustomNavMeshWindow";
        SceneView.onSceneGUIDelegate += OnScene;
        graphObj = GameObject.Find("CustomNavMesh");
        if (graphObj == null)
        {
            graphObj = new GameObject("CustomNavMesh");
            graphObj.AddComponent<CustomNavMesh>();
            graph = graphObj.GetComponent<CustomNavMesh>();
        }
        else
        {
            graph = graphObj.GetComponent<CustomNavMesh>();
            if (graph == null)
                graphObj.AddComponent<CustomNavMesh>();
            graph = graphObj.GetComponent<CustomNavMesh>();
        }
    }
  4. Define the OnDestroy function:
    void OnDestroy()
    {
        SceneView.onSceneGUIDelegate -= OnScene;
    }
  5. Implement the OnGUI function for drawing the window's interior:
    void OnGUI()
    {
        isEnabled = EditorGUILayout.Toggle("Enable Mesh Picking", isEnabled);
        if (GUILayout.Button("Build Edges"))
        {
            if (graph != null)
                graph.LoadGraph();
        }
    }
  6. Implement the first half of the OnScene function for handling the left-click on the scene window:
    private static void OnScene(SceneView sceneView)
    {
        if (!isEnabled)
            return;
        if (Event.current.type == EventType.MouseDown)
        {
            graphVertex = graph.vertexPrefab;
            if (graphVertex == null)
            {
                Debug.LogError("No Vertex Prefab assigned");
                return;
            }
            Event e = Event.current;
            Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
            RaycastHit hit;
            GameObject newV;
            // next step
        }
    }
  7. Implement the second half for implementing the behavior when clicking on the mesh:
    if (Physics.Raycast(ray, out hit))
    {
        GameObject obj = hit.collider.gameObject;
        Mesh mesh = obj.GetComponent<MeshFilter>().sharedMesh;
        Vector3 pos;
        int i;
        for (i = 0; i < mesh.triangles.Length; i += 3)
        {
            int i0 = mesh.triangles[i];
            int i1 = mesh.triangles[i + 1];
            int i2 = mesh.triangles[i + 2];
            pos = mesh.vertices[i0];
            pos += mesh.vertices[i1];
            pos += mesh.vertices[i2];
            pos /= 3;
            newV = (GameObject)Instantiate(graphVertex, pos, Quaternion.identity);
            newV.transform.Translate(obj.transform.position);
            newV.transform.parent = graphObj.transform;
            graphObj.transform.parent = obj.transform;
        }
    }

How it works...

We create a custom editor window and set up the delegate function OnScene for handling events on the scene window. Also, we create the graph nodes by traversing the mesh vertex arrays, computing each triangle's centroid. Finally, we make use of the graph's LoadGraph function in order to compute neighbors.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.172.115