using System.Collections; using System.Collections.Generic; using UnityEngine; #if UNITY_EDITOR using UnityEditor; [CustomEditor(typeof(ProjectorPlane))] public class ProjectorPlaneEditor : Editor { public override void OnInspectorGUI() { serializedObject.Update(); EditorGUI.BeginChangeCheck(); { EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Radius")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_DotPitch")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Suspend")); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CreateOnStart")); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } } if (Application.isPlaying && GUILayout.Button("Projector")) (target as ProjectorPlane).Projector(); } } #endif [RequireComponent(typeof(MeshFilter))] public class ProjectorPlane : MonoBehaviour { [SerializeField] private float m_Radius = 10;//半径 [SerializeField] private float m_DotPitch = 1;//点间距 [SerializeField] private float m_Suspend = 0.01f; [SerializeField] private bool m_CreateOnStart; private Mesh m_Mesh; public float Radius { get { return m_Radius; } set { m_Radius = Mathf.Max(0.1f, value); } } public float DotPitch { get { return m_DotPitch; } set { m_DotPitch = Mathf.Max(0.1f, value); } } public float Suspend { get { return m_Suspend; } set { m_Suspend = value; } } public bool CreateOnStart { get { return m_CreateOnStart; } set { m_CreateOnStart = value; } } public Mesh Mesh { get { return m_Mesh; } set { m_Mesh = value; } } private List tris; private List verties; private List uvs; private int vertCount; MeshCollider mc; private Ray ray = new Ray(); private RaycastHit hit; private Vector3 vertWorldPos; private bool needUpdate = false; private void Start() { Radius = Radius; DotPitch = DotPitch; if (CreateOnStart) CreateMesh(); //mc = GameSceneMgr.Instance.NavMeshCollider; } private void OnDestroy() { if (Mesh) Destroy(Mesh); } /// /// 生成一个投影面。 /// public void Projector() { if (Mesh) UpdateMesh(); else CreateMesh(); } /// /// 新建一个网格。 /// private void CreateMesh() { if (Mesh) Destroy(Mesh); Mesh = new Mesh(); Mesh.MarkDynamic(); Mesh.name = "ProjectorPlane"; GetComponent().mesh = Mesh; int[] vertCountInColumn; SetVerties(out vertCountInColumn); SetUVs(); SetTriangles(vertCountInColumn); UpdateMesh(); } /// /// 更新当前网格。 /// private void UpdateMesh() { if (UpdateMeshVerties()) { Mesh.SetVertices(verties); Mesh.SetUVs(0, uvs); Mesh.SetTriangles(tris, 0); } } /// /// 计算顶点位置。 /// /// 每列顶点数。 private void SetVerties(out int[] vertCountInColumn) { verties = new List(); float hUnit = Mathf.Cos(Mathf.PI / 6) * DotPitch; int vertVerticalMax = Mathf.FloorToInt(2 * Radius / DotPitch) + 1; int vertColOneSide = Mathf.FloorToInt(Radius / hUnit); vertCountInColumn = new int[vertColOneSide * 2 + 1]; float hOffset; for (int col = 0, cp = -vertColOneSide; cp <= vertColOneSide; col++, cp++)//从左至右 { vertCountInColumn[col] = vertVerticalMax - Mathf.Abs(cp);//每一竖列的点数目 hOffset = cp * hUnit; if (vertCountInColumn[col] % 2 == 0)//双数 { for (int i = 1, n = vertCountInColumn[col] / 2; i <= n; i++) { verties.Add(new Vector3(hOffset, 0, DotPitch * (i - 0.5f))); verties.Add(new Vector3(hOffset, 0, -DotPitch * (i - 0.5f))); } } else//单数 { verties.Add(new Vector3(hOffset, 0, 0)); for (int i = 1, n = (vertCountInColumn[col] - 1) / 2; i <= n; i++) { verties.Add(new Vector3(hOffset, 0, DotPitch * i)); verties.Add(new Vector3(hOffset, 0, -DotPitch * i)); } } } verties.Sort(CompareVert); vertCount = verties.Count; } /// /// 网格UV对齐。 /// private void SetUVs() { uvs = new List(); verties.ForEach(Vert2UV); } /// /// 多边形绘制顺序。 /// /// 每列顶点数。 private void SetTriangles(int[] vertCountInColumn) { tris = new List(); for (int col = 0, vid = 0, n = vertCountInColumn.Length; col < n - 1; col++) { if (col < n / 2)//在左半区时 { for (int i = 0; i < vertCountInColumn[col]; i++, vid++) { //有右侧上方点 tris.Add(vid);//当前点 tris.Add(vid + vertCountInColumn[col] + 1);//右列上方点 tris.Add(vid + vertCountInColumn[col]);//右列同位点 if (i < vertCountInColumn[col] - 1)//非每列最高点时 { tris.Add(vid);//当前点 tris.Add(vid + 1);//上方点 tris.Add(vid + vertCountInColumn[col] + 1);//右列上方点 } } } else { for (int i = 0; i < vertCountInColumn[col]; i++, vid++) { if (i < vertCountInColumn[col] - 1) { if (i > 0)//右半区的最低点和最高点都不做右侧绘制 { tris.Add(vid);//当前点 tris.Add(vid + vertCountInColumn[col]);//右列同位点 tris.Add(vid + vertCountInColumn[col] - 1);//右列下方点 } tris.Add(vid);//当前点 tris.Add(vid + 1);//上方点 tris.Add(vid + vertCountInColumn[col]);//右列同位点 } } } } } /// /// 更新网格点的高度。 /// /// 网格需要重绘。 private bool UpdateMeshVerties() { needUpdate = false; if (!mc) return false; for (int i = 0; i < vertCount; i++) { vertWorldPos = transform.TransformPoint(verties[i]); vertWorldPos.y = mc.bounds.max.y; ray.origin = vertWorldPos; ray.direction = Vector3.down; if (mc.Raycast(ray, out hit, mc.bounds.size.y)) { vertWorldPos.y = hit.point.y + Suspend; verties[i] = transform.InverseTransformPoint(vertWorldPos); needUpdate = true; } } return needUpdate; } private static int CompareVert(Vector3 a, Vector3 b) { int comp = a.x.CompareTo(b.x); if (comp == 0) comp = a.z.CompareTo(b.z); return comp; } private void Vert2UV(Vector3 v) { uvs.Add((new Vector2((v.x + Radius), v.z + Radius)) / (2 * Radius)); } }