unity3d UILineRenderer

效果图如下:

unity3d UILineRenderer_第1张图片

绘画的点,可以是Vector2也可以是Transform。

代码如下:

1.

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

namespace ExtraFoundation.Components
{
    //===============================================================================
    [DisallowMultipleComponent]
    [RequireComponent(typeof(CanvasRenderer))]
    [AddComponentMenu("UI/Effects/UILineRenderer")]
    public class UILineRenderer : MaskableGraphic
    {
        #region Public Properties
        //---------------------------------------------------------------------------
        public Texture MainTexture
        {
            get { return texture; }
            set { texture = value; }
        }

        //---------------------------------------------------------------------------
        public float LineWidth
        {
            get { return lineWidth; }
            set { lineWidth = value; }
        }

        //---------------------------------------------------------------------------
        public PositionType Space
        {
            get { return posType; }
            set { posType = value; }
        }

        //---------------------------------------------------------------------------
        public bool LineList
        {
            get { return lineList; }
            set { lineList = value; }
        }

        //---------------------------------------------------------------------------
        public bool LineCaps
        {
            get { return lineCaps; }
            set { lineCaps = value; }
        }

        //---------------------------------------------------------------------------
        public JoinType LineJoin
        {
            get { return lineJoin; }
            set { lineJoin = value; }
        }

        //---------------------------------------------------------------------------
        public override Texture mainTexture
        {
            get
            {
                return texture == null ? s_WhiteTexture : texture;
            }
        }

        //---------------------------------------------------------------------------
        /// 
        /// Texture to be used.
        /// 
        public Texture Texture
        {
            get
            {
                return texture;
            }

            set
            {
                if (texture == value)
                {
                    return;
                }

                texture = value;
                SetVerticesDirty();
                SetMaterialDirty();
            }
        }

        //---------------------------------------------------------------------------
        public Rect UVRect
        {
            get
            {
                return uvRect;
            }

            set
            {
                if (uvRect == value)
                {
                    return;
                }

                uvRect = value;
                SetVerticesDirty();
            }
        }

        #endregion

        #region Public Methods
        //---------------------------------------------------------------------------
        public void AddVector2Point(Vector2 pos)
        {
            Point point = new Point();
            point.Position = pos;
            point.IsTarget = false;

            points.Add(point);
        }

        //---------------------------------------------------------------------------
        public void AddTransformPoint(Transform trans)
        {
            Point point = new Point();
            point.Target = trans;
            point.IsTarget = true;

            points.Add(point);
        }

        public void InsertVector2PointAt(int index, Vector2 pos)
        {
            Point point = new Point();
            point.Position = pos;
            point.IsTarget = false;

            points.Insert(index, point);
        }

        //---------------------------------------------------------------------------
        public void RemovePointAt(int index)
        {
            if(index >= 0 && points.Count > index)
            {
                points.RemoveAt(index);
            }
        }

        //---------------------------------------------------------------------------
        public void ClearPoints()
        {
            points.Clear();
        }
        #endregion

        #region UnityMethods
        //---------------------------------------------------------------------------
        private void Update()
        {
            if (Application.isEditor && transform.position != cachePos)
            {
                cachePos = transform.position;
                OnRebuildRequested();
            }
        }
        #endregion

        #region Internal Methods
        //---------------------------------------------------------------------------
        private List GetTruePoins()
        {
            List truePoins = new List();

            if (posType == PositionType.Absolute)
            {
                Vector2 pOffset = transform.position;
                for (int i = 0, count = points.Count; i < count; i++)
                {
                    Point point = points[i];

                    if (point.IsTarget)
                    {
                        if (point.Target)
                        {
                            truePoins.Add((Vector2)point.Target.position - pOffset);
                        }
                    }
                    else
                    {
                        truePoins.Add(point.Position - pOffset);
                    }

                }
            }
            else
            {
                for (int i = 0, count = points.Count; i < count; i++)
                {
                    Point point = points[i];

                    if (point.IsTarget)
                    {
                        if (point.Target)
                        {
                            truePoins.Add(point.Target.position - transform.position);
                        }
                    }
                    else
                    {
                        truePoins.Add(point.Position);
                    }

                }
            }

            return truePoins;
        }

        //---------------------------------------------------------------------------
        private List LineListSegments(List points)
        {
            var segments = new List();
            for (int i = 1, count = points.Count; i < count; i += 2)
            {
                var start = points[i - 1];
                var end = points[i];

                if (lineCaps)
                {
                    segments.Add(CreateLineCap(start, end, SegmentType.Start));
                }

                segments.Add(CreateLineSegment(start, end, SegmentType.Middle));

                if (lineCaps)
                {
                    segments.Add(CreateLineCap(start, end, SegmentType.End));
                }
            }

            return segments;
        }

        //---------------------------------------------------------------------------
        private List StraightLineSegments(List points)
        {
            var segments = new List();
            for (int i = 1, count = points.Count; i < count; i++)
            {
                var start = points[i - 1];
                var end = points[i];

                if (lineCaps && i == 1)
                {
                    segments.Add(CreateLineCap(start, end, SegmentType.Start));
                }

                segments.Add(CreateLineSegment(start, end, SegmentType.Middle));

                if (lineCaps && i == count - 1)
                {
                    segments.Add(CreateLineCap(start, end, SegmentType.End));
                }
            }

            return segments;
        }

        //---------------------------------------------------------------------------
        protected override void OnPopulateMesh(VertexHelper vh)
        {
            if (points == null)
            {
                return;
            }

            vh.Clear();

            List truePoins = GetTruePoins();
            List segments = null;

            if (lineList)
            {
                segments = LineListSegments(truePoins);
            }
            else
            {
                segments = StraightLineSegments(truePoins);
            }

            for (int i = 0, count = segments.Count; i < count; i++)
            {
                if (!lineList && i < segments.Count - 1)
                {
                    Vector3 vec1 = segments[i][1].position - segments[i][2].position;
                    Vector3 vec2 = segments[i + 1][2].position
                        - segments[i + 1][1].position;

                    float angle = Vector2.Angle(vec1, vec2) * Mathf.Deg2Rad;

                    // 转动方向
                    Vector3 cross = Vector3.Cross(vec1.normalized, vec2.normalized);
                    float sign = Mathf.Sign(cross.z);

                    // 计算斜点
                    float miterDistance = lineWidth * 0.5f / Mathf.Tan(angle * 0.5f);

                    Vector3 offset = vec1.normalized * miterDistance * sign;
                    Vector3 miterPointA = segments[i][2].position - offset;
                    Vector3 miterPointB = segments[i][3].position + offset;

                    var joinType = lineJoin;
                    if (joinType == JoinType.Miter)
                    {
                        if (miterDistance < vec1.magnitude * 0.5f
                            && miterDistance < vec2.magnitude * 0.5f
                            && angle > MinMiterJoin)
                        {
                            segments[i][2].position = miterPointA;
                            segments[i][3].position = miterPointB;
                            segments[i + 1][0].position = miterPointB;
                            segments[i + 1][1].position = miterPointA;
                        }
                        else
                        {
                            joinType = JoinType.Bevel;
                        }
                    }

                    if (joinType == JoinType.Bevel)
                    {
                        if (miterDistance < vec1.magnitude * 0.5f
                            && miterDistance < vec2.magnitude * 0.5f
                            && angle > MinBevelNiceJoin)
                        {
                            if (sign < 0)
                            {
                                segments[i][2].position = miterPointA;
                                segments[i + 1][1].position = miterPointA;
                            }
                            else
                            {
                                segments[i][3].position = miterPointB;
                                segments[i + 1][0].position = miterPointB;
                            }
                        }

                        var join = new UIVertex[]
                        {
                            segments[i][2],
                            segments[i][3],
                            segments[i + 1][0],
                            segments[i + 1][1]
                        };

                        vh.AddUIVertexQuad(join);
                    }
                }

                vh.AddUIVertexQuad(segments[i]);
            }
        }

        //---------------------------------------------------------------------------
        /// 
        /// 创建线段链接处过度
        /// 
        /// 
        /// 
        /// 
        /// 
        private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)
        {
            if (type == SegmentType.Start)
            {
                var capStart = start - ((end - start).normalized * lineWidth * 0.5f);

                return CreateLineSegment(capStart, start, SegmentType.Start);
            }
            else if (type == SegmentType.End)
            {
                var capEnd = end + ((end - start).normalized * lineWidth * 0.5f);

                return CreateLineSegment(end, capEnd, SegmentType.End);
            }

            Debug.LogError("Bad SegmentType passed in to CreateLineCap. Must be "
                + "SegmentType.Start or SegmentType.End");

            return null;
        }

        //---------------------------------------------------------------------------
        /// 
        /// 构建矩形
        /// 
        /// 开始点
        /// 结束点
        /// 线段类型
        /// 矩形顶点数据
        private UIVertex[] CreateLineSegment(Vector2 start, Vector2 end,
            SegmentType type)
        {
            var uvs = MiddleUvs;
            if (type == SegmentType.Start)
            {
                uvs = StartUvs;
            }
            else if (type == SegmentType.End)
            {
                uvs = EndUvs;
            }

            Vector2 nDir = new Vector2(start.y - end.y, end.x - start.x).normalized;
            Vector2 offset = nDir * lineWidth * 0.5f;


            var v1 = start - offset;
            var v2 = start + offset;
            var v3 = end + offset;
            var v4 = end - offset;

            return SetVbo(new[] { v1, v2, v3, v4 }, uvs);
        }

        //---------------------------------------------------------------------------
        /// 
        /// 构建矩形
        /// 
        /// 顶点
        /// UV
        /// 矩形UI顶点数据
        protected UIVertex[] SetVbo(Vector2[] vertices, Vector2[] uvs)
        {
            UIVertex[] vbo = new UIVertex[4];
            for (int i = 0; i < vertices.Length; i++)
            {
                var vert = UIVertex.simpleVert;
                vert.color = color;
                vert.position = vertices[i];
                vert.uv0 = uvs[i];
                vbo[i] = vert;
            }

            return vbo;
        }
        #endregion
        
        #region Internal Fields
        //---------------------------------------------------------------------------
        private const float MinMiterJoin = 15 * Mathf.Deg2Rad;
        private const float MinBevelNiceJoin = 0 * Mathf.Deg2Rad;
        [SerializeField]
        private Texture texture;
        [SerializeField]
        private Rect uvRect = new Rect(0f, 0f, 1f, 1f);
        [SerializeField]
        private float lineWidth = 2;
        //[SerializeField]
        //private bool useMargins;
        //[SerializeField]
        //private Vector2 margin;
        [SerializeField]
        private List points = new List();
        [SerializeField]
        private bool lineList = false;
        [SerializeField]
        private bool lineCaps = false;
        [SerializeField]
        private JoinType lineJoin = JoinType.Bevel;
        [SerializeField]
        private PositionType posType = PositionType.Relative;
        private Vector3 cachePos;

        #region UV
        //---------------------------------------------------------------------------
        private static readonly Vector2 UVTopLeft = Vector2.zero;
        private static readonly Vector2 UVBottomLeft = new Vector2(0, 1);
        private static readonly Vector2 UVTopCenter = new Vector2(0.5f, 0);
        private static readonly Vector2 UVBottomCenter = new Vector2(0.5f, 1);
        private static readonly Vector2 UVTopRight = new Vector2(1, 0);
        private static readonly Vector2 UVBottomRight = new Vector2(1, 1);
        private static readonly Vector2[] StartUvs = new[]
        {
            UVTopLeft,
            UVBottomLeft,
            UVBottomCenter,
            UVTopCenter
        };
        private static readonly Vector2[] MiddleUvs = new[]
        {
            UVTopCenter,
            UVBottomCenter,
            UVBottomCenter,
            UVTopCenter
        };
        private static readonly Vector2[] EndUvs = new[]
        {
            UVTopCenter,
            UVBottomCenter,
            UVBottomRight,
            UVTopRight
        };
        #endregion

        #endregion

        #region Public Declarations
        //===========================================================================
        public enum PositionType
        {
            Relative,
            Absolute
        }

        //===========================================================================
        /// 
        /// 连接处处理
        /// 
        public enum JoinType
        {
            Bevel,
            Miter
        }

        //===========================================================================
        [System.Serializable]
        private struct Point
        {
            #region Public Properties
            //-----------------------------------------------------------------------
            public Vector2 Position
            {
                get { return position; }
                set { position = value; }
            }

            //-----------------------------------------------------------------------
            public Transform Target
            {
                get { return target; }
                set { target = value; }
            }

            //-----------------------------------------------------------------------
            public bool IsTarget
            {
                get { return isTarget; }
                set { isTarget = value; }
            }
            #endregion

            #region Internal Fields
            //-----------------------------------------------------------------------
            [SerializeField]
            private Vector2 position;

            //-----------------------------------------------------------------------
            [SerializeField]
            private Transform target;

            //-----------------------------------------------------------------------
            [SerializeField]
            private bool isTarget;
            #endregion

        }
        #endregion

        #region Internal Declarations
        //===========================================================================
        private enum SegmentType
        {
            Start,
            Middle,
            End,
        }
        #endregion
    }
}
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using ExtraFoundation.Components;
using System;

namespace ExtraFoundationEditor.Inspector
{
    //===============================================================================
    [CustomEditor(typeof(UILineRenderer), true)]
    public class UILineRendererInspector : Editor
    {
        #region Unity Methods
        //---------------------------------------------------------------------------
        private void OnEnable()
        {
            lineRender = target as UILineRenderer;

            if (serializedObject != null)
            {
                var pointProperty = serializedObject.FindProperty("points");
                reorderList = new ReorderableList(
                    serializedObject,
                    pointProperty,
                    true,
                    true,
                    true,
                    true);

                reorderList.drawElementCallback = OnDrawElementCallback;
                reorderList.onSelectCallback = OnSelectCallBack;
                reorderList.onRemoveCallback = OnRemoveCallBack;
                reorderList.drawHeaderCallback = OnDrawHeader;
            }
        }

        //---------------------------------------------------------------------------
        private void OnSceneGUI()
        {
            if (selectPoint == null)
            {
                return;
            }

            var isTarget = selectPoint.FindPropertyRelative("isTarget");

            if (!isTarget.boolValue)
            {
                var value = selectPoint.FindPropertyRelative("position");
                Transform trans = lineRender.transform;

                if (lineRender.Space == UILineRenderer.PositionType.Absolute)
                {
                    value.vector2Value = Handles.PositionHandle(
                        value.vector2Value, Quaternion.identity);
                }
                else
                {
                    Vector3 pos = new Vector3(
                        value.vector2Value.x,
                        value.vector2Value.y,
                        0);

                    pos = Handles.PositionHandle(
                        pos + trans.position, Quaternion.identity);

                    pos -= trans.position;
                    value.vector2Value = pos;
                }
            }
            else
            {
                var targetProperty = selectPoint.FindPropertyRelative("target");
                Transform target = targetProperty.objectReferenceValue as Transform;

                if (target != null)
                {
                    Vector3 oldPos = target.position;
                    target.position = Handles.PositionHandle(target.position,
                        Quaternion.identity);

                    if (oldPos != target.position)
                    {
                        lineRender.OnRebuildRequested();
                    }
                }
            }
            
            serializedObject.ApplyModifiedProperties();
        }

        //---------------------------------------------------------------------------
        public override void OnInspectorGUI()
        {
            if (serializedObject == null)
            {
                return;
            }

            serializedObject.Update();

            EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Color"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("texture"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Material"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("lineWidth"));

            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(serializedObject.FindProperty("posType"));
            bool spaceChanged = EditorGUI.EndChangeCheck();

            //var maring = serializedObject.FindProperty("useMargins");
            //EditorGUILayout.PropertyField(maring);

            //if (maring.boolValue)
            //{
            //    EditorGUILayout.PropertyField(serializedObject.FindProperty("margin"));
            //}

            EditorGUILayout.PropertyField(serializedObject.FindProperty("lineList"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("lineCaps"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("lineJoin"));
            
            if (reorderList != null)
            {
                scrollViewPos = EditorGUILayout.BeginScrollView(scrollViewPos,
                    GUILayout.MaxHeight(200));
                reorderList.DoLayoutList();
                EditorGUILayout.EndScrollView();
            }
            
            serializedObject.ApplyModifiedProperties();

            if (spaceChanged)
            {
                OnSpaceChanged();
            }

            if (GUILayout.Button("Refresh"))
            {
                lineRender.OnRebuildRequested();
            }
        }
        #endregion

        #region Internal Methods
        //---------------------------------------------------------------------------
        private void OnDrawHeader(Rect rect)
        {
            EditorGUI.LabelField(rect, new GUIContent("Points"));
        }

        //---------------------------------------------------------------------------
        private void OnDrawElementCallback(Rect rect,
            int index, bool isActive, bool isFocused)
        {
            var element = reorderList.serializedProperty.GetArrayElementAtIndex(index);

            float typeWidth = 30;
            Rect typeRect = new Rect(
                rect.position.x + rect.width - typeWidth + 5,
                rect.position.y,
                typeWidth - 5,
                rect.height - 5);

            Rect valueRect = new Rect(
                rect.position.x,
                rect.position.y + 1,
                rect.width - typeWidth,
                rect.height - 5
                );

            var isTrans = element.FindPropertyRelative("isTarget");
            //EditorGUI.PropertyField(typeRect, isTrans, GUIContent.none);
            Color oldColor = GUI.color;
            GUI.color = Color.green;
            if(GUI.Button(typeRect, new GUIContent(isTrans.boolValue ? "T" : "P")))
            {
                isTrans.boolValue = !isTrans.boolValue;
            }

            GUI.color = oldColor;

            SerializedProperty valueProperty = null;

            if (!isTrans.boolValue)
            {
                valueProperty = element.FindPropertyRelative("position");
            }
            else
            {
                valueProperty = element.FindPropertyRelative("target");
            }

            if (valueProperty != null)
            {
                EditorGUI.PropertyField(valueRect, valueProperty, GUIContent.none);
            }
        }

        //---------------------------------------------------------------------------
        private void OnRemoveCallBack(ReorderableList list)
        {
            list.serializedProperty.DeleteArrayElementAtIndex(list.index);
            selectPoint = null;
        }

        //---------------------------------------------------------------------------
        private void OnSelectCallBack(ReorderableList list)
        {
            selectPoint = list.serializedProperty.GetArrayElementAtIndex(list.index);
            SceneView.RepaintAll();
        }

        //---------------------------------------------------------------------------
        private void OnSpaceChanged()
        {
            var points = serializedObject.FindProperty("points");

            Vector2 lineRenderPos = (Vector2)lineRender.transform.position;

            for (int i = 0, count = points.arraySize; i < count; i++)
            {
                var point = points.GetArrayElementAtIndex(i);

                var isTrans = point.FindPropertyRelative("isTarget");
                if (isTrans.boolValue)
                {
                    continue;
                }

                var value = point.FindPropertyRelative("position");

                if (lineRender.Space == UILineRenderer.PositionType.Absolute)
                {
                    value.vector2Value = value.vector2Value + lineRenderPos;
                }
                else
                {
                    value.vector2Value = value.vector2Value - lineRenderPos;
                }
            }

            serializedObject.ApplyModifiedProperties();
        }
        #endregion

        #region Internal Fields
        //---------------------------------------------------------------------------
        private ReorderableList reorderList;
        private Vector2 scrollViewPos;
        private SerializedProperty selectPoint = null;
        private UILineRenderer lineRender;
        #endregion
    }
}
需要注意的是:带这个组件的节点,需要和UIRoot保持相对的Scale的缩放。

你可能感兴趣的:(unity3d工具)