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

 

最后,感谢各位博主。

 

### 回答1: Mybatis通用Mapper是一个基于Mybatis框架的插件,它可以帮助我们快速地进行数据库操作,避免了手写SQL的繁琐和容易出错的问题。使用Mybatis通用Mapper,我们只需要定义好实体类和Mapper接口,就可以直接调用通用的增删改查方法,非常方便。 具体使用方法如下: 1. 引入Mybatis通用Mapper的依赖包,可以通过Maven或者Gradle进行引入。 2. 定义实体类,需要注意的是实体类的属性名要和数据库表的字段名一致,或者使用@ColumnName注解进行映射。 3. 定义Mapper接口,继承通用Mapper接口,例如: public interface UserMapper extends Mapper<User> {} 4. 在Mybatis的配置文件中配置通用Mapper插件,例如: <plugins> <plugin interceptor="tk.mybatis.mapper.plugin.MapperInterceptor"> <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/> </plugin> </plugins> 5. 在代码中调用通用Mapper的方法,例如: @Autowired private UserMapper userMapper; public void addUser(User user) { userMapper.insert(user); } 以上就是Mybatis通用Mapper的使用方法,它可以大大简化我们的开发工作,提高开发效率。 ### 回答2: Mybatis通用Mapper是一个基于Mybatis的通用Mapper插件。它提供了单表的增删改查操作,同时还支持通用的批量操作和条件查询等功能。在使用Mybatis通用Mapper时,我们不需要编写繁琐的Mapper接口和对应的XML文件,只需要使用注解即可完成对数据库的操作。 首先,我们需要在pom.xml文件中添加Mybatis通用Mapper的依赖: ``` <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>4.1.5</version> </dependency> ``` 接下来,我们需要在Mybatis的配置文件中添加通用Mapper的配置: ``` <plugins> <plugin interceptor="tk.mybatis.mapper.common.MapperInterceptor"> <property name="mappers" value="tk.mybatis.mapper.common.BaseMapper"/> </plugin> </plugins> ``` 完成以上操作后,我们就可以在项目中使用Mybatis通用Mapper了。例如,我们要对一个用户表进行操作,可以创建一个User实体类: ``` public class User { private Integer id; private String username; private String password; private String email; // 省略getter和setter方法 } ``` 然后,我们通过注解在UserMapper接口中定义对用户表的操作: ``` public interface UserMapper extends Mapper<User> { } ``` 这里的Mapper是Mybatis通用Mapper提供的一个接口,通过继承该接口,我们可以直接使用其中定义好的单表操作方法。 接下来,我们就可以直接在代码中使用UserMapper了。例如,我们要插入一条用户记录,可以使用以下代码: ``` User user = new User(); user.setUsername("test"); user.setPassword("123456"); user.setEmail("test@example.com"); userMapper.insert(user); ``` 如果我们需要查询一条用户记录,可以使用以下代码: ``` User user = new User(); user.setId(1); User result = userMapper.selectOne(user); ``` 以上就是使用Mybatis通用Mapper的简单示例。在实际使用中,我们还可以通过注解实现复杂的批量操作和条件查询等功能,具体请参考Mybatis通用Mapper的官方文档。 ### 回答3: Mybatis通用mapper是基于Mybatis的一个工具,简化了Mybatis中XML配置的工作量,提供了一些通用的方法,能够简化我们的增删改查操作。下面简单介绍一下Mybatis通用mapper的使用方法。 一、引入依赖和配置 在pom文件中引入mybatis和通用mapper的依赖 ``` <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>${mybatis-mapper.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> ``` 在application.yml中进行相关配置 ``` mybatis: #配置别名 type-aliases-package: com.xxxx.entity #配置xml映射文件路径 mapper-locations: classpath:mapper/*.xml mapper: #配置通用mapper的包路径 mappers: tk.mybatis.mapper.common.Mapper #配置实体类主键的生成策略 identity: mysql #配置mysql的方言,不配置通用mapper的分页插件会出错 dialect: mysql ``` 二、编写实体类和Mapper接口 在编写实体类时,需要给实体类的属性加上@TableId和@Column注解,用来指明主键和列名。例如: ``` @Data @Table(name = "user") public class User { @Id @GeneratedValue(generator = "JDBC") @Column(name = "id") private Long id; @Column(name = "username") private String username; @Column(name = "password") private String password; @Column(name = "age") private Integer age; @Column(name = "email") private String email; } ``` 在编写Mapper接口时,直接继承Mapper<T>接口即可,T为对应的实体类。例如: ``` @Repository public interface UserMapper extends Mapper<User> { } ``` 三、编写通用方法 Mybatis通用mapper提供了一些通用方法,例如插入数据、更新数据、删除数据和查询数据等。这些方法都可以直接调用或者根据需要进行封装。 (1)插入数据 使用通用mapper的插入方法,代码如下: ``` User user = new User(); user.setUsername("testUser"); user.setPassword("123456"); user.setAge(20); user.setEmail("testUser@xx.com"); userMapper.insert(user); ``` 使用通用mapper的批量插入方法,代码如下: ``` List<User> userList = new ArrayList<>(); User user1 = new User(); user1.setUsername("testUser1"); user1.setPassword("123456"); user1.setAge(20); user1.setEmail("testUser@xx.com"); userList.add(user1); User user2 = new User(); user2.setUsername("testUser2"); user2.setPassword("123456"); user2.setAge(21); user2.setEmail("testUser@xx.com"); userList.add(user2); userMapper.insertList(userList); ``` (2)更新数据 使用通用mapper的更新方法,代码如下: ``` User user = new User(); user.setId(1L); user.setUsername("newTestUser"); userMapper.updateByPrimaryKey(user); ``` (3)删除数据 使用通用mapper的删除方法,代码如下: ``` userMapper.deleteByPrimaryKey(1L); ``` (4)查询数据 使用通用mapper的查询方法,代码如下: ``` User user = userMapper.selectByPrimaryKey(1L); ``` 使用通用mapper的查询所有数据方法,代码如下: ``` List<User> userList = userMapper.selectAll(); ``` 使用通用mapper的条件查询方法,代码如下: ``` Condition condition = new Condition(User.class); condition.createCriteria().andEqualTo("username", "testUser"); List<User> userList = userMapper.selectByExample(condition); ``` 以上就是使用Mybatis通用mapper的一些常用方法,它们都能够简化我们的开发工作。当然,如果有需要,我们还可以自己封装一些方法实现更加灵活和高效的操作。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

仙魁XAN

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

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

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

打赏作者

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

抵扣说明:

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

余额充值