Unity UGUI 效果 之 UI 元素 多边形UI (例如雷达图,圆形,不规则多边形 UI等)显示 的简单实现的几种方法整理

Unity UGUI 效果 之 UI 元素 多边形UI (例如雷达图,圆形,不规则多边形 UI等)显示 的简单实现的几种方法整理

 

目录

Unity UGUI 效果 之 UI 元素 多边形UI (例如雷达图,圆形,不规则多边形 UI等)显示 的简单实现的几种方法整理

一、简单介绍

二、可以自由调整形状的多边形 UI

1、功能说明

3、效果预览

3、 实现步骤

4、关键代码

三、调整对应边数,实现任意正多边形,甚至圆形 UI

1、功能介绍

2、效果预览

3、实现步骤

4、关键代码

四、类雷达图的多边形 UI

1、功能说明

2、效果预览

3、实现步骤

4、关键代码

五、类雷达图多边形UI(二)

1、功能介绍

2、效果预览

3、实现步骤

4、关键代码

六、雷达图 UGUI 案例

1、效果图

2、Unity 场景布局

3、关键代码


 

一、简单介绍

UGUI,是Unity自带的 GUI 系统,有别于 NGUI;使用 UGUI 也能制作出比较酷炫的效果 。

本节介绍,在 UGUI中 普通的 UI 元素中实现简单的多边形显示的若干方法的整理总结。

UGUI 的 UI 扩展,可参见 unity-ui-extensions 工程学习: https://bitbucket.org/ddreaper/unity-ui-extensions

接下来将逐一说明几种多边形UI的方法。

 

二、可以自由调整形状的多边形 UI

1、功能说明

1)添加脚本 PolygonUIMesh

2)在该脚本的组建下,添加子物体,作为 Polygon 多边形的点

3)任意移动子物体,Polygon 多边形都会穗子变化

 

3、效果预览

 

3、 实现步骤

1)编写好脚本 PolygonUIMesh

 

2)挂在 PolygonUIMesh 脚本到 Canvas 下的物体上

(注意:PolygonUIMesh 相当于 Button 的 Image,所以可以在其组建上添加 Button,实现按钮功能等)

 

3)在挂载 PolygonUIMesh 脚本组件下,添加子物体,会自动作为多变形的点,移动他们既可以跳帧多边形形状了

 

4、关键代码

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

public class PolygonUIMesh : Graphic
{

    private void OnGUI()
    {
        // 实时检测更新绘制 OnPopulateMesh 中 transform.child 位置
        SetAllDirty();
    }

    /// <summary>
    /// 根据 transform.child 位置 绘制 Mesh
    /// </summary>
    /// <param name="vh"></param>
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        if (transform.childCount <= 2)
        {
            return;
        }

        Color32 color32 = color;
        vh.Clear();

        // 几何图形的顶点,本例中根据子节点坐标确定顶点
        foreach (Transform child in transform)
        {
            vh.AddVert(child.localPosition, color32, new Vector2(0f, 0f));
        }

        for (int i = 0; i < (transform.childCount-2); i ++)
        {
            // 几何图形中的三角形
            vh.AddTriangle(i+1, i + 2, 0);

        } 

    }

    /// <summary>
    /// 点的辅助绘制
    /// </summary>
    private void OnDrawGizmos()
    {
        if (transform.childCount == 0)
        {
            return;
        }

        for (int i = 0; i < transform.childCount; i++)
        {
            Gizmos.DrawSphere(transform.GetChild(i).position, 2.55f);
            Gizmos.DrawIcon(transform.GetChild(i).position, "numbers/numbers_" + i+".PNG",false);
        }
        
    }
}

 

三、调整对应边数,实现任意正多边形,甚至圆形 UI

1、功能介绍

1)这里是继承 RawImage,调整其多边形的绘制

2)改变边数,实现 UI 多边形

3)调整 UI 元素宽高,效果会有不一样(局限性)

 

2、效果预览

 

3、实现步骤

1)编写脚本 UIPolygonRawImage ,继承 RawImage ,重写其方法,实现多边形UI

(其中:UIPolygonRawImageInspector 脚本是对 UIPolygonRawImage  变量显示的调整,放在 Editor 目录下即可 )

(UIPolygonRawImageInspector  可以根据需要可要可不要,不影响功能)

 

2)在 Canvas 下添加组件,并挂载脚本 UIPolygonRawImage ,对应添加图片

(跟使用 RawImage差不多,只是多了个边数)

 

3)调整边数,实现多边形 UI

 

4、关键代码

参考博文:https://www.xuanyusong.com/archives/4375

1)UIPolygonRawImage

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

public class UIPolygonRawImage : RawImage
{
    const int FILL_PERCENT = 100;
    float thickness = 5;

    [SerializeField]
    [Range(4, 360)]
    int _segments = 36;

    public int segments
    {
        get { return _segments; }
        set
        {
            if (_segments != value)
            {
                _segments = value;
                SetVerticesDirty();
#if UNITY_EDITOR
                UnityEditor.EditorUtility.SetDirty(transform);
#endif
            }
        }
    }


    protected override void OnRectTransformDimensionsChange()
    {
        base.OnRectTransformDimensionsChange();
        this.thickness = (float)Mathf.Clamp(this.thickness, 0, rectTransform.rect.width / 2);
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        float outer = -rectTransform.pivot.x * rectTransform.rect.width;
        float inner = -rectTransform.pivot.x * rectTransform.rect.width + this.thickness;

        vh.Clear();

        Vector2 prevX = Vector2.zero;
        Vector2 prevY = Vector2.zero;
        Vector2 uv0 = new Vector2(0, 0);
        Vector2 uv1 = new Vector2(0, 1);
        Vector2 uv2 = new Vector2(1, 1);
        Vector2 uv3 = new Vector2(1, 0);
        Vector2 pos0;
        Vector2 pos1;
        Vector2 pos2;
        Vector2 pos3;

        float tw = rectTransform.rect.width;
        float th = rectTransform.rect.height;

        float angleByStep = (FILL_PERCENT / 100f * (Mathf.PI * 2f)) / segments;
        float currentAngle = 0f;
        for (int i = 0; i < segments + 1; i++)
        {

            float c = Mathf.Cos(currentAngle);
            float s = Mathf.Sin(currentAngle);

            StepThroughPointsAndFill(outer, inner, ref prevX, ref prevY, out pos0, out pos1, out pos2, out pos3, c, s);

            uv0 = new Vector2(pos0.x / tw + 0.5f, pos0.y / th + 0.5f);
            uv1 = new Vector2(pos1.x / tw + 0.5f, pos1.y / th + 0.5f);
            uv2 = new Vector2(pos2.x / tw + 0.5f, pos2.y / th + 0.5f);
            uv3 = new Vector2(pos3.x / tw + 0.5f, pos3.y / th + 0.5f);

            vh.AddUIVertexQuad(SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 }));

            currentAngle += angleByStep;
        }
    }

    private void StepThroughPointsAndFill(float outer, float inner, ref Vector2 prevX, ref Vector2 prevY, out Vector2 pos0, out Vector2 pos1, out Vector2 pos2, out Vector2 pos3, float c, float s)
    {
        pos0 = prevX;
        pos1 = new Vector2(outer * c, outer * s);

        pos2 = Vector2.zero;
        pos3 = Vector2.zero;

        prevX = pos1;
        prevY = pos2;
    }

    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;
    }
}

 

2)UIPolygonRawImageInspector

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

[CustomEditor(typeof(UIPolygonRawImage), true)]
[CanEditMultipleObjects]
public class UIPolygonRawImageInspector : RawImageEditor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();
		UIPolygonRawImage polygon = target as UIPolygonRawImage;
		polygon.segments = Mathf.Clamp(EditorGUILayout.IntField("UICircle多边形", polygon.segments), 4, 360);

	}
}

 

四、类雷达图的多边形 UI

1、功能说明

1)查看UGUI的代码可以得知,绘制ui图的方法其实是绘制mesh,然后对其进行填充。正常一个Image图片,绘制有四个顶点。为了实现多边形,继承于MaskableGraphic,重写OnPopulateMesh,重新绘制顶点。

2)能调整边数,点的比例,是否填充,旋转等,控制显示雷达图的样式

 

2、效果预览

 

3、实现步骤

1)编写脚本,实现类雷达图的功能

 

2)把脚本挂载到Canvas 的组建下

 

3)调整脚本参数,即可根据需要调整成自己想要的样式

 

4、关键代码

参考博文:https://blog.csdn.net/u013012420/article/details/105956884


using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 雷达图
/// </summary>
public class RadarMapUIMesh : MaskableGraphic
{
    public bool fill = true;
    public float thickness = 5;
    [Range(3, 360)]
    public int sides = 3;
    [Range(0, 360)]
    public float rotation = 0;
    [Range(0, 1)]
    public float[] VerticesDistances = new float[3];
    private float size = 0;

    public void DrawPolygon(int _sides)
    {
        sides = _sides;
        VerticesDistances = new float[_sides + 1];
        for (int i = 0; i < _sides; i++) VerticesDistances[i] = 1; ;
        rotation = 0;
    }
    public void DrawPolygon(int _sides, float[] _VerticesDistances)
    {
        sides = _sides;
        VerticesDistances = _VerticesDistances;
        rotation = 0;
    }
    public void DrawPolygon(int _sides, float[] _VerticesDistances, float _rotation)
    {
        sides = _sides;
        VerticesDistances = _VerticesDistances;
        rotation = _rotation;
    }
    void Update()
    {
        size = rectTransform.rect.width;
        if (rectTransform.rect.width > rectTransform.rect.height)
            size = rectTransform.rect.height;
        else
            size = rectTransform.rect.width;
        thickness = (float)Mathf.Clamp(thickness, 0, size / 2);
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();

        Vector2 prevX = Vector2.zero;
        Vector2 prevY = Vector2.zero;
        Vector2 uv0 = new Vector2(0, 0);
        Vector2 uv1 = new Vector2(0, 1);
        Vector2 uv2 = new Vector2(1, 1);
        Vector2 uv3 = new Vector2(1, 0);
        Vector2 pos0;
        Vector2 pos1;
        Vector2 pos2;
        Vector2 pos3;
        float degrees = 360f / sides;
        int vertices = sides + 1;
        if (VerticesDistances.Length != vertices)
        {
            VerticesDistances = new float[vertices];
            for (int i = 0; i < vertices - 1; i++) VerticesDistances[i] = 1;
        }
        // last vertex is also the first!
        VerticesDistances[vertices - 1] = VerticesDistances[0];
        for (int i = 0; i < vertices; i++)
        {
            float outer = -rectTransform.pivot.x * size * VerticesDistances[i];
            float inner = -rectTransform.pivot.x * size * VerticesDistances[i] + thickness;
            float rad = Mathf.Deg2Rad * (i * degrees + rotation);
            float c = Mathf.Cos(rad);
            float s = Mathf.Sin(rad);
            uv0 = new Vector2(0, 1);
            uv1 = new Vector2(1, 1);
            uv2 = new Vector2(1, 0);
            uv3 = new Vector2(0, 0);
            pos0 = prevX;
            pos1 = new Vector2(outer * c, outer * s);
            if (fill)
            {
                pos2 = Vector2.zero;
                pos3 = Vector2.zero;
            }
            else
            {
                pos2 = new Vector2(inner * c, inner * s);
                pos3 = prevY;
            }
            prevX = pos1;
            prevY = pos2;
            SetVbo(new[] { pos0, pos1, pos2, pos3 }, new[] { uv0, uv1, uv2, uv3 });
            vh.AddUIVertexQuad(vbo);
        }
    }
    private UIVertex[] vbo = new UIVertex[4];
    private void SetVbo(Vector2[] vertices, Vector2[] uvs)
    {
        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;
        }
    }
}

 

五、类雷达图多边形UI(二)

参考博文:http://www.lsngo.net/2017/10/26/unity_polygonalui/

1、功能介绍

1)可以添加图线

2)可以调整边数,和点的比例,改变样式

 

2、效果预览

 

3、实现步骤

1)编写脚本 PolygonImage 等,实现功能

(注意有些脚本最好放在Editor 文件夹下)

 

2)把脚本 挂载到 Canvas 下的组件上

 

3)根据需要调整效果如上

 

 

4、关键代码

1)PolygonImage

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

public class PolygonImage : MaskableGraphic, ISerializationCallbackReceiver, ICanvasRaycastFilter
{
    [SerializeField]
    Texture m_Texture;

    public PolygonImageEdge edgeWeights;

    public override Texture mainTexture
    {
        get
        {
            if (m_Texture == null)
            {
                if (material != null && material.mainTexture != null)
                {
                    return material.mainTexture;
                }
                return s_WhiteTexture;
            }

            return m_Texture;
        }
    }

    /// <summary>
    /// Texture to be used.
    /// </summary>
    public Texture texture
    {
        get
        {
            return m_Texture;
        }
        set
        {
            if (m_Texture == value)
                return;

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

    public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
    {
        if (raycastTarget)
        {
            Vector2 local;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out local);

            int edgeCount = edgeWeights.EdgeCount;

            float deltaAngle = 360f/edgeCount;

            for (int i = 0; i < edgeCount; i++)
            {
                bool result = IsInPolygon(i, deltaAngle, local);
                if (result)
                    return true;
            }
        }
        return false;
    }

    public virtual void OnAfterDeserialize()
    {
       
    }

    public virtual void OnBeforeSerialize()
    {
     
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        if (edgeWeights == null || edgeWeights.EdgeCount <= 2)
        {
            base.OnPopulateMesh(vh);
            return;
        }
        int edgeCount = edgeWeights.EdgeCount;

        float deltaAngle = 360f/edgeCount;

        vh.Clear();
        
        for (int i = 0; i < edgeCount; i++)
        {
            GetTriangle(vh, i, deltaAngle);
        }
    }

    private void GetTriangle(VertexHelper vh, int index, float deltaAngle)
    {
        float edgeLength = Mathf.Min(rectTransform.rect.width, rectTransform.rect.height)*0.5f;
        var color32 = color;
        Vector3 cent = new Vector3(0, 0);
        float angle1 = 90+(index + 1)*deltaAngle;
        float angle2 = 90+(index)*deltaAngle;
        float radius1 = (index == edgeWeights.EdgeCount - 1 ? edgeWeights.Weights[0] : edgeWeights.Weights[index + 1])* edgeLength;
        float radius2 = edgeWeights.Weights[index]*edgeLength;

        Vector3 p1 = new Vector3(radius1*Mathf.Cos(angle1*Mathf.Deg2Rad), radius1*Mathf.Sin(angle1*Mathf.Deg2Rad));
        Vector3 p2 = new Vector3(radius2 * Mathf.Cos(angle2 * Mathf.Deg2Rad), radius2 * Mathf.Sin(angle2 * Mathf.Deg2Rad));

        vh.AddVert(cent, color32, Vector2.zero);
        vh.AddVert(p1, color32, new Vector2(0,1));
        vh.AddVert(p2, color32, new Vector2(1,0));

        vh.AddTriangle(index*3, index*3 + 1, index*3 + 2);
    }

    private bool IsInPolygon(int index, float deltaAngle, Vector2 point)
    {
        float edgeLength = Mathf.Min(rectTransform.rect.width, rectTransform.rect.height)*0.5f;
        Vector2 cent = new Vector2(0, 0);
        float angle1 = 90+(index + 1) * deltaAngle;
        float angle2 = 90+(index) * deltaAngle;
        float radius1 = (index == edgeWeights.EdgeCount - 1 ? edgeWeights.Weights[0] : edgeWeights.Weights[index + 1])*edgeLength;
        float radius2 = edgeWeights.Weights[index]*edgeLength;

        Vector2 p1 = new Vector2(radius1 * Mathf.Cos(angle1 * Mathf.Deg2Rad), radius1 * Mathf.Sin(angle1 * Mathf.Deg2Rad));
        Vector2 p2 = new Vector2(radius2 * Mathf.Cos(angle2 * Mathf.Deg2Rad), radius2 * Mathf.Sin(angle2 * Mathf.Deg2Rad));

        return IsInTriangle(cent, p1, p2, point);
    }

    private bool IsInTriangle(Vector2 vertex1, Vector2 vertex2, Vector2 vertex3, Vector2 point)
    {
        Vector2 v0 =vertex3 - vertex1;
        Vector2 v1 = vertex2 - vertex1;
        Vector2 v2 = point - vertex1;

        float dot00 = Vector2.Dot(v0, v0);
        float dot01 = Vector2.Dot(v0, v1);
        float dot02 = Vector2.Dot(v0, v2);
        float dot11 = Vector2.Dot(v1, v1);
        float dot12 = Vector2.Dot(v1, v2);

        float inverDeno = 1 / (dot00 * dot11 - dot01 * dot01);

        float u = (dot11 * dot02 - dot01 * dot12) * inverDeno;
        if (u < 0 || u > 1) 
        {
            return false;
        }

        float v = (dot00 * dot12 - dot01 * dot02) * inverDeno;
        if (v < 0 || v > 1) 
        {
            return false;
        }

        return u + v <= 1;
    }
}

 

2)PolygonImageEdge

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

[System.Serializable]
public class PolygonImageEdge
{
    public int EdgeCount
    {
        get
        {
            if (m_Weights == null)
                return 0;
            return m_Weights.Count;
        }
    }

    public List<float> Weights
    {
        get { return m_Weights; }
    }

    [SerializeField] private List<float> m_Weights;

}

 

3)PolygonImageEditor

using UnityEngine;
using UnityEditor.UI;
using UnityEditor;
using UnityEditor.UI;
using System.Collections;

[CustomEditor(typeof(PolygonImage))]
[CanEditMultipleObjects]
public class PolygonImageEditor : GraphicEditor
{

    private SerializedProperty m_Texture;
    private SerializedProperty m_EdgeWeights;

    protected override void OnEnable()
    {
        base.OnEnable();
        m_Texture = serializedObject.FindProperty("m_Texture");
        m_EdgeWeights = serializedObject.FindProperty("edgeWeights");
    }

    protected override void OnDisable()
    {
        
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        EditorGUILayout.PropertyField(m_Texture);
        EditorGUILayout.PropertyField(m_Color);
        EditorGUILayout.PropertyField(m_Material);
        EditorGUILayout.PropertyField(m_RaycastTarget);
        EditorGUILayout.PropertyField(m_EdgeWeights);

        serializedObject.ApplyModifiedProperties();
    }
}

 

4)PolygonImageEdgeDrawer

using UnityEngine;
using UnityEditor;
using System.Collections;
using UnityEditorInternal;

[CustomPropertyDrawer(typeof(PolygonImageEdge))]
public class PolygonImageEdgeDrawer : PropertyDrawer
{
    private ReorderableList m_ReorderableList;

    private void Init(SerializedProperty property)
    {
        if (m_ReorderableList == null)
        {
            m_ReorderableList = new ReorderableList(property.serializedObject,
                property.FindPropertyRelative("m_Weights"));
            m_ReorderableList.drawElementCallback = DrawEdgeWeight;
            m_ReorderableList.drawHeaderCallback = DrawHeader;
        }
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        Init(property);
        var val = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;
        m_ReorderableList.DoList(position);
        EditorGUI.indentLevel = val;
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        Init(property);

        return m_ReorderableList.GetHeight();
    }

    private void DrawEdgeWeight(Rect rect, int index, bool isActive, bool isFocused)
    {
        SerializedProperty itemData = m_ReorderableList.serializedProperty.GetArrayElementAtIndex(index);
        EditorGUI.Slider(rect, itemData, 0, 1);
    }

    private void DrawHeader(Rect rect)
    {
        EditorGUI.LabelField(rect, "边权重");
    }
}

 

六、雷达图 UGUI 案例

参考博文:Unity---UGUI案例学习---雷达图

1、效果图

 

2、Unity 场景布局

(这里的方法是通过改变黄色区域UI元素的外围顶点位置,来实现出我们需要的渲染效果。)

 

3、关键代码

(Point是蓝色区域最外的顶点,只需执行一次获得其具体位置就行。Handler是红色的顶点,每次更新时都要同步更新)

1)Boke_RadarChart(黄色区域雷达图类)

public class Boke_RadarChart : Image
	
{
	
    //顶点个数、顶点位置、顶点大小、顶点显示图片、顶点颜色
	
    [SerializeField]
	
    private int _pointCount;
	
    [SerializeField]
	
    private List<RectTransform> _points;
	
    [SerializeField]
	
    private Vector2 _pointSize = new Vector2(10, 10);
	
    [SerializeField]
	
    private Sprite _pointSprite;
	
    [SerializeField]
	
    private Color _pointColor = Color.white;
	
 
	
    //每个方向上各个点的的比例、存储handler
	
    [SerializeField]
	
    private float[] _handlerRadio;
	
    [SerializeField]
	
    private List<S5RadarChartHandler> _handlers;
	
 
	
    private void Update()
	
    {
	
        //刷新
	
        SetVerticesDirty();
	
    }
	
 
	
    protected override void OnPopulateMesh(VertexHelper vh)
	
    {
	
        //修改原来的空image,让他填满我们的雷达图。
	
        //清空原来顶点,自己添加
	
        vh.Clear();
	
        AddVerts(vh);
	
        //AddVertsTemplete(vh);
	
        AddTriangle(vh);
	
    }
	
 
	
    /// <summary>
	
    /// 添加顶点
	
    /// </summary>
	
    private void AddVerts(VertexHelper vh)
	
    {
	
        //添加轴心点
	
        vh.AddVert(Vector3.zero, color, Vector2.zero);
	
        foreach (S5RadarChartHandler handler in _handlers)
	
        {
	
            vh.AddVert(handler.transform.localPosition, color, Vector2.zero);
	
        }
	
    }
	
 
	
    /*
	
    /// <summary>
	
    /// 如果需要用贴图。就要每个顶点单独设置uv坐标
	
    /// </summary>
	
    private void AddVertsTemplete(VertexHelper vh)
	
    {
	
        vh.AddVert(_handlers[0].transform.localPosition, color, new Vector2(0.5f, 1));
	
        vh.AddVert(_handlers[1].transform.localPosition, color, new Vector2(0f, 1));
	
        vh.AddVert(_handlers[2].transform.localPosition, color, new Vector2(0f, 0));
	
        vh.AddVert(_handlers[3].transform.localPosition, color, new Vector2(1f, 0));
	
        vh.AddVert(_handlers[4].transform.localPosition, color, new Vector2(1f, 1));
	
    }
	
    */
	
 
	
    /// <summary>
	
    /// 添加三角形
	
    /// </summary>
	
    private void AddTriangle(VertexHelper vh)
	
    {
	
        //围绕中心点生成5个面
	
        for (int i = 1; i < _pointCount; i++)
	
        {
	
            //用三个顶点组成一个三角面
	
            //顺时针
	
            vh.AddTriangle(0, i + 1, i);
	
        }
	
        vh.AddTriangle(0, _pointCount, 1);
	
    }
	
 
	
    public void InitPoint()
	
    {
	
        ClearPoints();
	
        _points = new List<RectTransform>();
	
        SpawnPoint();
	
        SetPointPos();
	
    }
	
 
	
    private void ClearPoints()
	
    {
	
        if (_points == null)
	
            return;
	
 
	
        foreach (RectTransform point in _points)
	
        {
	
            if (point != null)
	
                //编辑模式下要用这个
	
                DestroyImmediate(point);
	
        }
	
    }
	
 
	
    private void SpawnPoint()
	
    {
	
        //5个最边角的顶点
	
        for (int i = 0; i < _pointCount; i++)
	
        {
	
            GameObject point = new GameObject("Point" + i);
	
            point.transform.SetParent(transform);
	
            _points.Add(point.AddComponent<RectTransform>());
	
        }
	
    }
	
 
	
    private void SetPointPos()
	
    {
	
        //每个角的弧度
	
        float radian = 2 * Mathf.PI / _pointCount;
	
        //半径
	
        float radius = 100;
	
 
	
        //从最上面一个顶点开始算。以中心为原点。
	
        float curRadian = 2 * Mathf.PI / 4.0f;
	
        for (int i = 0; i < _pointCount; i++)
	
        {
	
            float x = Mathf.Cos(curRadian) * radius;
	
            float y = Mathf.Sin(curRadian) * radius;
	
            curRadian += radian;
	
            _points[i].anchoredPosition = new Vector2(x, y);
	
        }
	
    }
	
 
	
    public void InitHandlers()
	
    {
	
        ClearHandlers();
	
        _handlers = new List<S5RadarChartHandler>();
	
        SpawnHandlers();
	
        SetHandlerPos();
	
    }
	
 
	
    private void ClearHandlers()
	
    {
	
        if (_handlers == null)
	
            return;
	
 
	
        foreach (S5RadarChartHandler handler in _handlers)
	
        {
	
            if (handler != null)
	
                DestroyImmediate(handler.gameObject);
	
        }
	
    }
	
 
	
    private void SpawnHandlers()
	
    {
	
        S5RadarChartHandler handler = null;
	
        for (int i = 0; i < _pointCount; i++)
	
        {
	
            GameObject point = new GameObject("Handler" + i);
	
            point.AddComponent<RectTransform>();
	
            point.AddComponent<Image>();
	
 
	
            handler = point.AddComponent<S5RadarChartHandler>();
	
            handler.SetParent(transform);
	
            handler.ChangeSprite(_pointSprite);
	
            handler.ChangeColor(_pointColor);
	
            handler.SetSize(_pointSize);
	
            handler.SetScale(Vector3.one);
	
            _handlers.Add(handler);
	
        }
	
    }
	
 
	
    private void SetHandlerPos()
	
    {
	
        //默认的handler位置,就是5项属性全满。
	
        if (_handlerRadio == null || _handlerRadio.Length != _pointCount)
	
        {
	
            for (int i = 0; i < _pointCount; i++)
	
            {
	
                _handlers[i].SetPos(_points[i].anchoredPosition);
	
            }
	
        }
	
        else
	
        {
	
            for (int i = 0; i < _pointCount; i++)
	
            {
	
                _handlers[i].SetPos(_points[i].anchoredPosition * _handlerRadio[i]);
	
            }
	
        }
	
    }
	
}

 

2)Boke_RadarChartHandler(红色顶点类)

public class Boke_RadarChartHandler : MonoBehaviour
	
{
	
    private Image _image;
	
    private Image Image
	
    {
	
        get
	
        {
	
            if (_image == null)
	
                _image = GetComponent<Image>();
	
 
	
            return _image;
	
        }
	
    }
	
 
	
    private RectTransform _rect;
	
    private RectTransform Rect
	
    {
	
        get
	
        {
	
            if (_rect == null)
	
                _rect = GetComponent<RectTransform>();
	
 
	
            return _rect;
	
        }
	
    }
	
 
	
    #region 封装一层
	
    public void SetParent(Transform parent)
	
    {
	
        transform.SetParent(parent);
	
    }
	
    public void ChangeSprite(Sprite sprite)
	
    {
	
        Image.sprite = sprite;
	
    }
	
    public void ChangeColor(Color color)
	
    {
	
        Image.color = color;
	
    }
	
    public void SetPos(Vector2 pos)
	
    {
	
        Rect.anchoredPosition = pos;
	
    }
	
    public void SetSize(Vector2 size)
	
    {
	
        Rect.sizeDelta = size;
	
    }
	
    public void SetScale(Vector3 scale)
	
    {
	
        Rect.localScale = scale;
	
    }
	
    private float GetScale()
	
    {
	
        return Rect.lossyScale.x;
	
    }
	
    #endregion
	
}

 

3)Boke_RadarChartEditor

(编辑器类,用于把Boke_RadarChart类中的可变属性显示到面板中,方便我们更改调试。)

using System.Collections;
	
using System.Collections.Generic;
	
using UnityEngine;
	
using UnityEditor;
	
 
	
[CustomEditor(typeof(Boke_RadarChart), true)]
	
[CanEditMultipleObjects]
	
public class Boke_RadarChartEditor : UnityEditor.UI.ImageEditor
	
{
	
    SerializedProperty _pointCount;
	
    SerializedProperty _pointSprite;
	
    SerializedProperty _pointColor;
	
    SerializedProperty _pointSize;
	
    SerializedProperty _handlerRadio;
	
 
	
    protected override void OnEnable()
	
    {
	
        base.OnEnable();
	
        _pointCount = serializedObject.FindProperty("_pointCount");
	
        _pointSprite = serializedObject.FindProperty("_pointSprite");
	
        _pointColor = serializedObject.FindProperty("_pointColor");
	
        _pointSize = serializedObject.FindProperty("_pointSize");
	
        _handlerRadio = serializedObject.FindProperty("_handlerRadio");
	
    }
	
 
	
    public override void OnInspectorGUI()
	
    {
	
        base.OnInspectorGUI();
	
 
	
        serializedObject.Update();
	
        
	
        EditorGUILayout.PropertyField(_pointCount);
	
        EditorGUILayout.PropertyField(_pointSprite);
	
        EditorGUILayout.PropertyField(_pointColor);
	
        EditorGUILayout.PropertyField(_pointSize);
	
        EditorGUILayout.PropertyField(_handlerRadio,true);
	
 
	
        Boke_RadarChart radar = target as Boke_RadarChart;
	
        if (radar != null)
	
        {
	
            if (GUILayout.Button("生成雷达图顶点"))
	
            {
	
                radar.InitPoint();
	
            }
	
 
	
            if (GUILayout.Button("生成内部可操作顶点"))
	
            {
	
                radar.InitHandlers();
	
            }
	
        }
	
        serializedObject.ApplyModifiedProperties();
	
        if (GUI.changed)
	
        {
	
            EditorUtility.SetDirty(target);
	
        }
	
 
	
    }
	
}

 

最后,感谢各位博主。

 

  • 10
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
实现多页翻页和多页任意翻页的方式有很多种,下面介绍一种较为简单实现方式。 1. 创建一个 ScrollRect 对象,并将 Content 对象的 Rect Transform 的 Anchor Presets 设置为 Stretch。 2. 在 Content 下创建多个 Page 对象,并将它们的 Rect Transform 的 Anchor Presets 设置为 Stretch。每个 Page 对象都应该放置一个要显示UI 元素。 3. 为 ScrollRect 添加一个 Horizontal Layout Group 组件,并将 Child Force Expand 和 Child Control Height 设置为 true。 4. 在代码中添加以下代码,实现多页翻页和多页任意翻页的效果: ```csharp using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections.Generic; public class ScrollRectPage : MonoBehaviour, IBeginDragHandler, IEndDragHandler { public ScrollRect scrollRect; public float smoothTime = 0.5f; public float sensitivity = 0.2f; private List<float> pages = new List<float>(); private int currentPage = 0; private float targetPage = 0; private bool isDragging = false; void Start() { int pageCount = scrollRect.content.childCount; for (int i = 0; i < pageCount; i++) { float page = i * scrollRect.viewport.rect.width / (pageCount - 1); pages.Add(-page); } } void Update() { if (!isDragging) { float nearestPage = pages[currentPage]; for (int i = 0; i < pages.Count; i++) { if (Mathf.Abs(pages[i] - scrollRect.horizontalNormalizedPosition) < Mathf.Abs(nearestPage - scrollRect.horizontalNormalizedPosition)) { nearestPage = pages[i]; targetPage = i; } } if (currentPage != targetPage) { currentPage = (int)targetPage; } scrollRect.horizontalNormalizedPosition = Mathf.Lerp(scrollRect.horizontalNormalizedPosition, nearestPage, smoothTime * Time.deltaTime); } } public void OnBeginDrag(PointerEventData eventData) { isDragging = true; } public void OnEndDrag(PointerEventData eventData) { isDragging = false; float delta = eventData.delta.x / scrollRect.viewport.rect.width; if (Mathf.Abs(delta) > sensitivity) { if (delta > 0) { currentPage--; } else { currentPage++; } } currentPage = Mathf.Clamp(currentPage, 0, pages.Count - 1); targetPage = currentPage; } } ``` 5. 将 ScrollRectPage 脚本挂载到 ScrollRect 对象上,并将 scrollRect 属性设置为 ScrollRect 对象。 6. 运行程序,就可以实现多页翻页和多页任意翻页的效果了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仙魁XAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值