NavMesh只制作可移动的导航网,清除多余不可走区域

只制作可移动的导航网。它使存储文件大小减小并提高性能。它消除了迁移到随机区域的问题。添加链接描述
1.如何使用
**加粗样式**
2.创建一个包含“NavMeshCleaner”组件的对象。Andadd指向可定制区域。

按住控制键并单击添加点。如果要移动它,请按
输入上的control键并单击。您不需要在此处添加多点筛选
3.单击计算按钮
在这里插入图片描述
非步行问题表已取消。
在这里插入图片描述
4.重建一个无碱问题的NavMesh。完成。
在这里插入图片描述
提示
如果NvMesh的坡度较大,它仍可能成为不可步行的区域。在这种情况下,复制NavMeshCleaner对象并创建新的非行走问题表。

using System.Collections.Generic;
using UnityEngine;
#if UNITY_5_3_OR_NEWER
using UnityEngine.AI;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif

public class NavMeshCleaner : MonoBehaviour
{
    public List<Vector3> m_WalkablePoint = new List<Vector3>();
    public float m_Height = 1.0f;
    public float m_Offset = 0.0f;
    public int m_MidLayerCount = 3;

#if UNITY_EDITOR
    private void Awake()
    {
        SetMeshVisible(false);
    }

    List<GameObject> m_Child = new List<GameObject> ();

    void Reset()
    {
        Undo.RecordObject(this, "Reset");

        for (int i = 0; i < m_Child.Count; i++)
        {
            Undo.DestroyObjectImmediate(m_Child[i]);
        }
        m_Child.Clear();
    }

    void SetMeshVisible(bool visible)
    {
        for (int i = 0; i < m_Child.Count; i++)
            m_Child[i].SetActive(visible);
    }

    public bool HasMesh()
    {
        return m_Child.Count != 0 ? true : false;
    }

    public bool MeshVisible()
    {
        if (m_Child.Count > 0)
            return m_Child[0].activeSelf;
        return false;
    }

    void Build()
    {
        Mesh[] m = CreateMesh();

        Undo.RegisterCreatedObjectUndo(this, "build");

        for (int i = 0; i < m.Length || i == 0; i++)
        {
            GameObject o;
            if (i >= m_Child.Count)
            {
                o = new GameObject();

                //o.hideFlags = HideFlags.DontSave;
                o.name = gameObject.name + "_Mesh(DontSave)";
                o.AddComponent<MeshFilter>();

                MeshRenderer meshrenderer = o.AddComponent<MeshRenderer>();
                meshrenderer.sharedMaterial = AssetDatabase.GetBuiltinExtraResource<Material>("Default-Diffuse.mat");

                o.transform.parent = transform;
                o.transform.localScale = Vector3.one;
                o.transform.localPosition = Vector3.zero;
                o.transform.localRotation = Quaternion.identity;
                GameObjectUtility.SetStaticEditorFlags(o, GameObjectUtility.GetStaticEditorFlags(gameObject) | StaticEditorFlags.NavigationStatic);
                GameObjectUtility.SetNavMeshArea(o, 1);
                m_Child.Add(o);
                Undo.RegisterCreatedObjectUndo(o, "");
            }
            else
            {
                o = m_Child[i].gameObject;
            }

            o.hideFlags = i == 0 ? (HideFlags.DontSave | HideFlags.HideInHierarchy) : m_Child[0].gameObject.hideFlags;

            MeshFilter meshfilter = m_Child[i].GetComponent<MeshFilter>();
            Undo.RecordObject(meshfilter, "MeshUpdate");
            meshfilter.sharedMesh = m.Length == 0 ? null : m[i];
        }

        while (m_Child.Count > m.Length)
        {
            Undo.DestroyObjectImmediate(m_Child[m_Child.Count-1]);
            m_Child.RemoveAt(m_Child.Count-1);
        }
    }

    static int Find(Vector3[] vtx, int left, int right, Vector3 v, float key)
    {
        int center = (left + right) / 2;

        if (center == left)
        {
            for (int i = left; i < vtx.Length && vtx[i].x <= key + 0.002f; i++)
            {
                if (Vector3.Magnitude(vtx[i] - v) <= 0.01f)
                    return i;
            }
            return -1;
        }

        if (key <= vtx[center].x)
            return Find(vtx, left, center, v, key);
        else
            return Find(vtx, center, right, v, key);
    }

    class Tri
    {
        public Tri(int i1, int i2, int i3) { this.i1 = i1; this.i2 = i2; this.i3 = i3; min = Mathf.Min(i1, i2, i3); max = Mathf.Max(i1, i2, i3); }
        public int i1, i2, i3;
        public int min, max;
    };

    class Edge
    {
        public Edge(int i1, int i2) { this.i1 = i1; this.i2 = i2; }
        public int i1, i2;
    };

    static bool Find(Edge[] edge, int left, int right, int i1, int i2)
    {
        int center = (left + right) / 2;

        if (center == left)
        {
            for (int i = left; i < edge.Length && edge[i].i1 <= i1; i++)
            {
                if (edge[i].i1 == i1 && edge[i].i2 == i2)
                    return true;
            }
            return false;
        }

        if (i1 <= edge[center].i1)
            return Find(edge, left, center, i1, i2);
        else
            return Find(edge, center, right, i1, i2);
    }

    Mesh[] CreateMesh()
    {
        NavMeshTriangulation triangulatedNavMesh = NavMesh.CalculateTriangulation();

        Vector3[] navVertices = triangulatedNavMesh.vertices;
        List<Vector3> vertices = new List<Vector3>();
        vertices.AddRange(navVertices);
        vertices.Sort(delegate (Vector3 v1, Vector3 v2) { return v1.x == v2.x ? (v1.z == v2.z ? 0 : (v1.z < v2.z ? -1 : 1)) : (v1.x < v2.x ? -1 : 1); });

        Vector3[] v = vertices.ToArray();

        int[] table = new int[triangulatedNavMesh.vertices.Length];
        
        for (int i = 0; i < table.Length; i++)
        {
            table[i] = Find(v, 0, vertices.Count, navVertices[i], navVertices[i].x - 0.001f);
            if ((i % 100) == 0)
                EditorUtility.DisplayProgressBar(string.Format("Export Nav-Mesh (Phase #1/3) {0}/{1}", i, table.Length), "Weld Vertex", Mathf.InverseLerp(0, table.Length, i));
        }

        int[] navTriangles = triangulatedNavMesh.indices;

        List <Tri> tri = new List <Tri> ();
        for (int i = 0; i < navTriangles.Length; i += 3)
            tri.Add(new Tri(table[navTriangles[i + 0]], table[navTriangles[i + 1]], table[navTriangles[i + 2]]));
        tri.Sort(delegate (Tri t1, Tri t2) { return t1.min == t2.min ? 0 : t1.min < t2.min ? -1 : 1; });

        int[] boundmin = new int[(tri.Count + 127) / 128];
        int[] boundmax = new int[boundmin.Length];

        for (int i = 0, c=0; i < tri.Count; i += 128, c++)
        {
            int min = tri[i].min;
            int max = tri[i].max;
            for (int j = 1; j < 128 && i + j < tri.Count; j++)
            {
                min = Mathf.Min(tri[i + j].min, min);
                max = Mathf.Max(tri[i + j].max, max);
            }
            boundmin[c] = min;
            boundmax[c] = max;
        }

        int[] triangles = new int[navTriangles.Length];
        for (int i = 0; i < triangles.Length; i += 3)
        {
            triangles[i+0] = tri[i/3].i1;
            triangles[i+1] = tri[i/3].i2;
            triangles[i+2] = tri[i/3].i3;
        }
        
        List<int> groupidx = new List<int>();
        List<int> groupcount = new List<int>();

        int[] group = new int[triangles.Length / 3];

        for (int i = 0; i < triangles.Length; i += 3)
        {
            int groupid = -1;
            int max = Mathf.Max(triangles[i], triangles[i + 1], triangles[i + 2]);
            int min = Mathf.Min(triangles[i], triangles[i + 1], triangles[i + 2]);

            for (int b=0, c=0; b < i; b+=3*128, c++)
            {
                if (boundmin[c] > max || boundmax[c] < min)
                    continue;

                for (int j=b; j < i && j < b+3*128; j+=3)
                {
                    if (tri[j / 3].min > max)
                        break;

                    if (tri[j / 3].max < min)
                        continue;

                    if (groupidx[group[j / 3]] == groupid)
                        continue;

                    for (int k = 0; k < 3; k++)
                    {
                        int vi = triangles[j + k];
                        if (triangles[i] == vi || triangles[i+1] == vi || triangles[i+2] == vi)
                        {
                            if (groupid == -1)
                            {
                                groupid = groupidx[group[j / 3]];
                                group[i/3] = groupid;
                            }
                            else
                            {
                                int curgroup = groupidx[group[j / 3]];
                                for (int l = 0; l < groupidx.Count; l++)
                                {
                                    if (groupidx[l] == curgroup)
                                        groupidx[l] = groupid;
                                }
                            }
                            break;
                        }
                    }
                }
            }

            if (groupid == -1)
            {
                groupid = groupidx.Count;
                group[i/3] = groupid;
                groupidx.Add(groupid);
                groupcount.Add(0);
            }

            if (((i / 3) % 100) == 0)
                EditorUtility.DisplayProgressBar("Collect (Phase #2/3)", "Classification Group", Mathf.InverseLerp(0, triangles.Length, i));
        }

        for (int i = 0; i < triangles.Length; i += 3)
        {
            group[i / 3] = groupidx[group[i / 3]];
            groupcount[group[i / 3]]++;
        }

        List<Mesh> result = new List<Mesh>();

        List<Vector3> vtx = new List<Vector3>();
        List<int> indices = new List<int>();

        int[] newtable = new int[vertices.Count];
        for (int i = 0; i < newtable.Length; i++)
            newtable[i] = -1;

        Vector3[] walkpoint = m_WalkablePoint.ToArray();

        for (int g = 0; g < groupcount.Count; g++)
        {
            if (groupcount[g] == 0)
                continue;

            List<Vector3> isolatevtx = new List<Vector3>();
            List<int> iolateidx = new List<int>();

            for (int i = 0; i < triangles.Length; i += 3)
            {
                if (group[i / 3] == g)
                {
                    for (int j = 0; j < 3; j++)
                    {
                        int idx = triangles[i + j];
                        if (newtable[idx] == -1)
                        {
                            newtable[idx] = isolatevtx.Count;
                            isolatevtx.Add(transform.InverseTransformPoint(vertices[idx] + Vector3.up * m_Offset));
                        }
                    }
                    iolateidx.Add(newtable[triangles[i + 0]]);
                    iolateidx.Add(newtable[triangles[i + 1]]);
                    iolateidx.Add(newtable[triangles[i + 2]]);
                }
            }

            if (Contains(isolatevtx.ToArray(), iolateidx.ToArray(), walkpoint) == true)
                continue;

            int maxvertex = 32768;

            if (vtx.Count > maxvertex || vtx.Count + isolatevtx.Count*(2+m_MidLayerCount) >= 65536)
            {
                result.Add(CreateMesh(vtx.ToArray(), indices.ToArray()));
                vtx.Clear();
                indices.Clear();
            }

            Vector3 h = transform.InverseTransformVector(Vector3.up * m_Height);
            int vtxoffset = vtx.Count;
            int layer = 2 + m_MidLayerCount;
            for (int i = 0; i < isolatevtx.Count; i++)
            {
                for(int j=0; j<layer; j++)
                    vtx.Add(isolatevtx[i] + h * ((float)j / (layer-1)));
            }
            for (int i = 0; i < iolateidx.Count; i += 3)
            {
                for (int j = 0; j < layer; j++)
                {
                    if (j == 0)
                        indices.AddRange(new int[] { vtxoffset + iolateidx[i] * layer + j, vtxoffset + iolateidx[i + 2] * layer + j, vtxoffset + iolateidx[i + 1] * layer + j });
                    else
                        indices.AddRange(new int[] { vtxoffset + iolateidx[i] * layer + j, vtxoffset + iolateidx[i + 1] * layer + j, vtxoffset + iolateidx[i + 2] * layer + j });
                }
            }

            if (m_Height > 0)
            {
                List<Edge> edge = new List<Edge>();
                for (int i = 0; i < iolateidx.Count; i += 3)
                {
                    edge.Add(new Edge(iolateidx[i+0], iolateidx[i+1]));
                    edge.Add(new Edge(iolateidx[i+1], iolateidx[i+2]));
                    edge.Add(new Edge(iolateidx[i+2], iolateidx[i+0]));
                }
                edge.Sort(delegate (Edge e1, Edge e2) { return e1.i1 == e2.i1 ? 0 : (e1.i1 < e2.i1 ? -1 : 1); });
                Edge[] e = edge.ToArray();

                for (int i = 0; i < iolateidx.Count; i += 3)
                {
                    for (int i1 = 2, i2 = 0; i2 < 3; i1 = i2++)
                    {
                        int v1 = iolateidx[i + i1];
                        int v2 = iolateidx[i + i2];

                        if (!Find(e, 0, edge.Count, v2, v1))
                        {
                            if (vtx.Count + 4 >= 65536)
                            {
                                result.Add(CreateMesh(vtx.ToArray(), indices.ToArray()));
                                vtx.Clear();
                                indices.Clear();
                            }

                            indices.AddRange(new int[] { vtx.Count, vtx.Count + 1, vtx.Count + 3, vtx.Count, vtx.Count + 3, vtx.Count + 2 });
                            vtx.AddRange(new Vector3[] { isolatevtx[v1], isolatevtx[v2], isolatevtx[v1]+h, isolatevtx[v2]+h });
                        }
                    }

                    if ((i%600) == 0)
                        EditorUtility.DisplayProgressBar("Collect (Phase #3/3)", "Create Mesh", Mathf.InverseLerp(0, groupcount.Count*100, g*100 + i * 100 / (i - iolateidx.Count)));
                }
            }

            EditorUtility.DisplayProgressBar("Collect (Phase #3/3)", "Create Mesh", Mathf.InverseLerp(0, groupcount.Count, g));
        }

        if (vtx.Count > 0)
        {
            result.Add(CreateMesh(vtx.ToArray(), indices.ToArray()));
        }

        EditorUtility.ClearProgressBar();
        return result.ToArray();
    }

    static Mesh CreateMesh(Vector3[] vtx, int[] indices)
    {
        Mesh m = new Mesh();
        m.hideFlags = HideFlags.DontSave;
        m.vertices = vtx;
        m.SetIndices(indices, MeshTopology.Triangles, 0);
        m.RecalculateNormals();
        m.RecalculateBounds();
        return m;
    }

    static bool Contains(Vector3[] vtx, int[] indices, Vector3[] points)
    {
        for (int j = 0; j < points.Length; j++)
        {
            Vector3 p = points[j];

            for (int i = 0; i < indices.Length; i += 3)
            {
                if (indices[i] == indices[i + 1] || indices[i] == indices[i + 2] || indices[i + 1] == indices[i + 2])
                    continue;

                if (PointInTriangle(vtx[indices[i]], vtx[indices[i + 2]], vtx[indices[i + 1]], p))
                    return true;
            }
        }
        return false;
    }

    static bool PointInTriangle(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 p)
    {
        Vector3 up = Vector3.Cross(v3 - v1, v2 - v1);

        if (Vector3.Dot(Vector3.Cross(p - v1, v2 - v1), up) > 0 &&
            Vector3.Dot(Vector3.Cross(p - v2, v3 - v2), up) > 0 &&
            Vector3.Dot(Vector3.Cross(p - v3, v1 - v3), up) > 0)
            return true;

        return false;
    }

    [UnityEditor.CustomEditor(typeof(NavMeshCleaner))]
    public class NavMeshCleanerEditor : Editor
    {
        NavMeshCleaner m_Target;

        void OnEnable()
        {
            m_Target = (NavMeshCleaner)target;

            Undo.undoRedoPerformed += OnUndoOrRedo;
        }

        void OnDisable()
        {
            Undo.undoRedoPerformed -= OnUndoOrRedo;
        }

        void OnUndoOrRedo()
        {
            Repaint();
        }

        public override void OnInspectorGUI()
        {
            EditorGUILayout.HelpBox(m_OverPoint != -1 ? "Press Control and click to remove the point." : "Press Control and click to add a walkable point.", m_Target.m_WalkablePoint.Count == 0 ? MessageType.Warning : MessageType.Info);

            base.OnInspectorGUI();

            NavMeshCleaner t = (NavMeshCleaner)target;

            if (t.m_Child.Count > 0)
            {
                EditorGUI.BeginChangeCheck();
                bool hideInHierarchy = EditorGUILayout.Toggle("Hide Temp Mesh Object In Hierarchy", (t.m_Child[0].gameObject.hideFlags & HideFlags.HideInHierarchy) != 0 ? true : false);
                if (EditorGUI.EndChangeCheck())
                {
                    for (int i = 0; i < t.m_Child.Count; i++)
                        t.m_Child[i].gameObject.hideFlags = hideInHierarchy ? (t.m_Child[i].gameObject.hideFlags | HideFlags.HideInHierarchy) : (t.m_Child[i].gameObject.hideFlags & (~HideFlags.HideInHierarchy));
                    try
                    {
                        EditorApplication.RepaintHierarchyWindow();
                        EditorApplication.DirtyHierarchyWindowSorting();
                    }
                    catch { }
                }
            }

            if (GUILayout.Button(t.HasMesh() ? "Recalculate" : "Calculate", GUILayout.Height(30.0f)))
            {
                t.Build();
                t.SetMeshVisible(true);
                SceneView.RepaintAll();
            }

            if (t.HasMesh() && GUILayout.Button(t.MeshVisible() ? "Hide Mesh" : "Show Mesh", GUILayout.Height(30.0f)))
            {
                //StaticEditorFlags flags = GameObjectUtility.GetStaticEditorFlags(t.gameObject);
                bool enabled = !t.MeshVisible();
                t.SetMeshVisible(enabled);
                SceneView.RepaintAll();
            }
            if (t.HasMesh() && GUILayout.Button("Reset Mesh", GUILayout.Height(30.0f)))
            {
                t.Reset();
                SceneView.RepaintAll();
            }
            if (t.HasMesh() && GUILayout.Button("Reset WalkablePoints", GUILayout.Height(30.0f)))
            {
                Undo.RecordObject(target, "reset");
                m_Target.m_WalkablePoint.Clear();
                SceneView.RepaintAll();
            }

        }

        static class Styles
        {
            public static GUIStyle Get(string id)
            {
                GUIStyle style;
                if (!texture.TryGetValue(id, out style))
                {
                    style = new GUIStyle(id);
                    texture.Add(id, style);
                }
                return style;
            }
            static Dictionary<string, GUIStyle> texture = new Dictionary<string, GUIStyle>();
        }

        void DrawDisc(Vector3 p, Vector3 n, float radius)
        {
            Vector3[] v = new Vector3[20];
            Matrix4x4 tm = Matrix4x4.TRS(p, Quaternion.LookRotation(n), Vector3.one * radius);
            for (int i = 0; i < 20; i++)
            {
                v[i] = tm.MultiplyPoint3x4(new Vector3(Mathf.Cos(Mathf.PI * 2 * i / (20 - 1)), Mathf.Sin(Mathf.PI * 2 * i / (20 - 1)), 0));
            }
            Handles.DrawAAPolyLine(v);
        }

        void OnSceneGUI()
        {
            SceneView sceneview = SceneView.currentDrawingSceneView;

            Event guiEvent = Event.current;

            if (guiEvent.type == EventType.Repaint)
            {
                // draw
                for (int i = 0; i < m_Target.m_WalkablePoint.Count; i++)
                {
                    Vector3 p = m_Target.transform.TransformPoint(m_Target.m_WalkablePoint[i]);
                    float unitsize = WorldSize(1.0f, sceneview.camera, p);

                    Handles.color = Color.black;
                    DrawDisc(p, Vector3.up, unitsize * 15);

                    Handles.color = i == m_OverPoint ? Color.red : Color.green;
                    Handles.DrawSolidDisc(p, Vector3.up, unitsize * 10);
                    Handles.DrawLine(p, p + Vector3.up * (unitsize * 200.0f));
                }
            }

            if (guiEvent.type == EventType.Layout && guiEvent.control == true)
            {
                HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive));
            }
            if (guiEvent.control == true)
            {
                EditorGUIUtility.AddCursorRect(new Rect(0, 0, Screen.width, Screen.height), m_OverPoint == -1 ? MouseCursor.ArrowPlus : MouseCursor.ArrowMinus);
            }

            if ((guiEvent.type == EventType.MouseDown || guiEvent.type == EventType.MouseDrag || guiEvent.type == EventType.MouseMove || guiEvent.type == EventType.MouseUp) && guiEvent.button == 0)
            {
                MouseEvent(guiEvent.type, guiEvent.mousePosition, guiEvent.modifiers == EventModifiers.Control ? true : false);
            }
        }

        int m_OverPoint = -1;

        void MouseEvent(EventType type, Vector2 mouseposition, bool controldown)
        {
            SceneView sceneview = SceneView.currentDrawingSceneView;
            
            Ray mouseRay = HandleUtility.GUIPointToWorldRay(mouseposition);            

            if (type == EventType.MouseMove)
            {
                int pointindex = -1;

                for (int i = 0; i < m_Target.m_WalkablePoint.Count; i++)
                {
                    Vector3 p = m_Target.transform.TransformPoint(m_Target.m_WalkablePoint[i]);
                    float size = WorldSize(10.0f, sceneview.camera, p) * 1.5f;
                    if (DistanceRayVsPoint(mouseRay, p) < size)
                    {
                        pointindex = i;
                        break;
                    }
                }

                if (pointindex != m_OverPoint)
                {
                    m_OverPoint = pointindex;
                    HandleUtility.Repaint();
                }
            }

            if (type == EventType.MouseDown && controldown == true)
            {
                if (m_OverPoint != -1)
                {
                    Undo.RecordObject(m_Target, "Remove Point");
                    m_Target.m_WalkablePoint.RemoveAt(m_OverPoint);
                    m_OverPoint = -1;
                }
                else
                {
                    float mint = 1000.0f;
                    RaycastHit hit;

                    if (Physics.Raycast(mouseRay, out hit, mint))
                    {
                        Undo.RecordObject(m_Target, "Add Point");
                        m_Target.m_WalkablePoint.Add(m_Target.transform.InverseTransformPoint(hit.point));
                    }
                    else
                    {
                        NavMeshTriangulation triangulatedNavMesh = NavMesh.CalculateTriangulation();

                        Vector3[] navVertices = triangulatedNavMesh.vertices;
                        int[] indices = triangulatedNavMesh.indices;

                        Vector3 outNormal = Vector3.up;
                        for (int i = 0; i < indices.Length; i += 3)
                            mint = IntersectTest(mouseRay, navVertices[indices[i]], navVertices[indices[i + 1]], navVertices[indices[i + 2]], mint, ref outNormal);

                        if (mint < 1000.0f)
                        {
                            Undo.RecordObject(m_Target, "Add Point");
                            Vector3 point = mouseRay.origin + mouseRay.direction * mint;
                            m_Target.m_WalkablePoint.Add(m_Target.transform.InverseTransformPoint(point));
                        }
                    }
                }
                HandleUtility.Repaint();
            }
        }

        static float kEpsilon = 0.000001f;
        // https://en.wikipedia.org/wiki/Möller–Trumbore_intersection_algorithm
        static float IntersectTest(Ray ray, Vector3 v0, Vector3 v1, Vector3 v2, float mint, ref Vector3 outNormal)
        {
            // edges from v1 & v2 to v0.     
            Vector3 e1 = v1 - v0;
            Vector3 e2 = v2 - v0;

            Vector3 h = Vector3.Cross(ray.direction, e2);
            float a = Vector3.Dot(e1, h);
            if ((a > -kEpsilon) && (a < kEpsilon))
                return mint;

            float f = 1.0f / a;
            Vector3 s = ray.origin - v0;
            float u = f * Vector3.Dot(s, h);
            if ((u < 0.0f) || (u > 1.0f))
                return mint;

            Vector3 q = Vector3.Cross(s, e1);
            float v = f * Vector3.Dot(ray.direction, q);
            if ((v < 0.0f) || (u + v > 1.0f))
                return mint;

            float t = f * Vector3.Dot(e2, q);
            if (t > kEpsilon && t < mint)
            {
                outNormal = Vector3.Normalize(Vector3.Cross(e1.normalized, e2.normalized));
                return t;
            }
            return mint;
        }

        static float WorldSize(float screensize, Camera camera, Vector3 p)
        {
            if (!camera.orthographic)
            {
                Vector3 localPos = camera.transform.InverseTransformPoint(p);
                float height = Mathf.Tan(camera.fieldOfView * Mathf.Deg2Rad * 0.5f) * localPos.z;
                return height * screensize / camera.pixelHeight;
            }
            else
            {
                return camera.orthographicSize * screensize / camera.pixelHeight;
            }
        }

        static float DistanceRayVsPoint(Ray mouseRay, Vector3 pos)
        {
            Vector3 v = pos - mouseRay.origin;
            return Mathf.Sqrt(Vector3.Dot(v, v) - Vector3.Dot(mouseRay.direction, v) * Vector3.Dot(mouseRay.direction, v));
        }

        static Vector3 IntersectPlane(Vector3 inNormal, Vector3 inPoint, Ray mouseRay)
        {
            Plane p = new Plane(inNormal, inPoint);
            float dstToDrawPlane = p.GetDistanceToPoint(mouseRay.origin);
            return mouseRay.origin + mouseRay.direction * (dstToDrawPlane / Vector3.Dot(-p.normal, mouseRay.direction));
        }
    }
#endif
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/900330.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

flashback database 闪回数据库

1.修改闪回区大小&#xff0c;路径&#xff0c;保留时间 SQL> show parameter db_recovery_file_dest SQL> show parameter db_flashback_retention_targetSQL> alter system set db_recovery_file_dest_size20G scopeboth;System altered.SQL> alter system set …

ffmpeg视频滤镜: 裁剪-crop

滤镜简述 crop官网链接 > FFmpeg Filters Documentation crop滤镜可以对视频进行裁剪&#xff0c;并且这个滤镜可以接受一些变量比如时间和帧数&#xff0c;这样我们实现动态裁剪&#xff0c;从而实现一些特效。 滤镜使用 参数 out_w <string> ..…

云电脑使用教程标准版

云电脑&#xff0c;也称为云桌面&#xff0c;是一种通过互联网连接远程服务器&#xff0c;使用虚拟桌面环境来执行计算任务的技术。川翔云电脑通过创建软件镜像&#xff0c;让用户能够快速启动并使用预配置的软件和资料&#xff0c;提供高效且经济的云服务。相较于公有云服务&a…

83.【C语言】数据结构之顺序表的尾部插入和删除

目录 3.操作顺序表 2."伪"插入顺序表的元素 分析尾部插入函数SLPushBack 代码示例 SeqList.h main.c free(指针)出错的几种可能的原因 3."伪"删除顺序表元素 2.分析尾部删除函数SLPopBack 代码示例 错误检查 两种解决办法 1.判断size是否为负…

【Linux系统】页表的存在位 与 OS的按需加载策略

一、引入 加载程序会将程序代码全部从磁盘中加载进内存吗&#xff1f; 为什么你的电脑的运存只有16GB&#xff0c;但你可以运行上百GB的游戏&#xff0c;如黑神话马喽&#xff1f; 这就涉及到 操作系统的按需加载策略 二、页表的存在位 页表的一个标志位&#xff1a;存在位 …

webpack 老项目升级记录:从 node-sass 限制的的 node v8 提升至支持 ^node v22

老项目简介 技术框架 vue 2.5.17webpack 4.16.5"webpack-cli": "3.1.0""node-sass": "^4.7.2" 几个阶段 第一步&#xff1a;vue2 升级到最新 第一步&#xff1a;升级 vue2 至最新版本&#xff0c;截止到目前&#xff08;2024-10-…

【vim】手动安装 Leader-F

LeaderF 是一个功能强大的 Vim 插件&#xff0c;主要用于快速导航和搜索。它可以帮助用户在 Vim 中高效地查找文件、缓冲区、标签、函数等各种元素&#xff0c;极大地提高了编辑效率。 LeaderF 的安装如果按照仓库中的教程来的话可以很方便的实现安装&#xff0c;这里介绍一下…

面试官:常见的网络攻击手段有哪些?解决方案了解吗?零基础入门到精通,收藏这一篇就够了

引言&#xff1a;由于互联网和信息技术的高速发展&#xff0c;网络安全变得尤为重要&#xff0c;如果不熟悉常见的网络攻击手段&#xff0c;就会造成数据泄漏、信息安全问题、乃至国家安全问题&#xff0c;本文就来介绍下常见的网络攻击手段和一些防范措施。 题目 面试官&…

深入理解值类型和引用类型的存储

目录 内存 存储 1&#xff09;栈区 2&#xff09;堆区 C#的编译过程 1&#xff09;源代码 2&#xff09;公共语言规范(Common Language Specification&#xff0c;CLS) 编译 3&#xff09;通用中间语言(Microsoft Intermediate Language&#xff0c;CIL或MSIL) 4&…

从陌生到信赖,3款AI写作助手教会了我何为真诚的表达

现在信息多得不得了&#xff0c;写作已经不是只有人才能干的事了。人工智能技术发展得特别快&#xff0c;AI写作助手也开始帮我们写东西了。一开始&#xff0c;我对这些AI助手挺好奇的&#xff0c;后来用着用着就越来越信任它们&#xff0c;甚至有点离不开了。我用的那三款AI写…

ONLYOFFICE 文档8.2版本已发布:PDF 协作编辑、改进界面、性能优化等更新

ONLYOFFICE 在线编辑器最新版本已经发布&#xff0c;其中包含30多个新功能和500多个错误修复。阅读本文了解所有更新。 关于 ONLYOFFICE 文档 ONLYOFFICE 是一个开源项目&#xff0c;专注于高级和安全的文档处理。坐拥全球超过 1500 万用户&#xff0c;ONLYOFFICE 是在线办公领…

Python日志记录库——loguru

知识星球&#xff1a;知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff0c;知识变现的工具知识星球是创作者连接铁杆粉丝&#xff0c;实现知识变现的工具。任何从事创作或艺术的人&#xff0c;例如艺术家、工匠、教师、学术研究、科普等&#xff0c;只要能获得一…

数学建模与优化算法:从基础理论到实际应用

数学建模和优化算法&#xff0c;它们不仅帮助我们理解和描述复杂系统的行为&#xff0c;还能找到系统性能最优化的解决方案。本文将从基础的数学理论出发&#xff0c;逐步深入到各种优化算法&#xff0c;并探讨它们在实际问题中的应用。 思维导图文件可获取&#xff1a;https:…

如何指定 Maven 的 JDK 版本?

maven 路径&#xff1a;/data/maven/ jdk 路径&#xff1a;/data/jdk_1.8 1、修改 mvn 可执行文件并指定 JDK 版本 vim /data/maven/bin/mvn # 在开头新增即可... # zhurs add JAVA_HOME PATH JAVA_HOME/data/jdk_1.8 ...保存退出即可&#xff01; 为什么在此处新增&#x…

C/C++(六)多态

本文将介绍C的另一个基于继承的重要且复杂的机制&#xff0c;多态。 一、多态的概念 多态&#xff0c;就是多种形态&#xff0c;通俗来说就是不同的对象去完成某个行为&#xff0c;会产生不同的状态。 多态严格意义上分为静态多态与动态多态&#xff0c;我们平常说的多态一般…

【博客节选】Unity角色异常抖动问题排查

本文截取自本人文章 &#xff1a;【Unity实战笔记】第二一 基于状态模式的角色控制——以UnityChan为例 发现出现角色抖动问题 尝试解决方法&#xff1a; 跳跃的loop time不要勾选&#xff1b; 相机aim添加垂直阻尼 还是不行&#xff0c;仔细查看是位移时震颤。 UnityCha…

两个mp3音频怎么合成一个?音频合成的多个好用方法教程

两个mp3音频怎么合成一个&#xff1f;在数字音频时代&#xff0c;随着各类音频内容的日益丰富&#xff0c;合并音频文件的需求也愈发突出。无论是为了制作连贯的音乐集&#xff0c;还是为了解决某些场合下音频播放的便利性&#xff0c;将两个或多个MP3音频合并在一起&#xff0…

【C++面试刷题】快排(quick_sort)和堆排(priority_queue)的细节问题

一、快排的快速选择算法两种思路&#xff08;面试会考&#xff09;O(N) 快排的三数取中思路&#xff1a; 重要的是将它三个数进行排序最左为最小&#xff0c;中间为次小&#xff0c;最右为最大的数。&#xff08;错误原因&#xff1a;我刚开始没有将这三个数进行排序&#xff…

Notepad++通过自定义语言实现日志按照不同级别高亮

借助Notepad的自定义语言可以实现日志的按照不同级别的高亮&#xff1b; 参考&#xff1a; https://blog.csdn.net/commshare/article/details/131208656 在此基础上做了一点修改效果如下&#xff1a; xml文件&#xff1a; <NotepadPlus><UserLang name"Ansibl…

洞察数据之美:用可视化探索销售与温度的关系

目录 数据可视化1.气温数据可视化图片展示将最高和最低气温合并绘制折线图&#xff1a;将最高和最低气温合并绘制散点图&#xff1a; 2.销售数据可视化几种常见的销售数据可视化方法及其适用场景&#xff1a;图片展示通过热力图和堆叠柱状图的直观展示&#xff0c;可以得出以下…