Unity – 雷达图

【Balabalabala】

雷达图在游戏中十分常见,多用于表达人物属性,能力。

大概长这个样子:

 

【实现】

1.首先定义一下雷达图每个点的数据结构。

[System.Serializable]
public struct RadarPointsData
{
    public string name;
    [Range(0, 1)] public float percent;
    [Range(0, 1)] public float targetPercent;
    public Text nameText;
    public Text percentText;
}

2.计算每个点的位置。

polygon

正多边形的顶点必定都在一个圆上,已知半径为R,那么其余的顶点都是01向量经过旋转得到。

向量旋转公式:

x’ = xcos(theta) – ysin(theta);

y’ = xsin(theta) + ycos(theta);

具体实现:

public void refresh()
{
    if (m_RadarPoints == null || m_RadarPoints.Length == 0)
        m_Vertexes = null;
    else
    {
        RectTransform targetArea = GetComponent();
        m_Vertexes = new Vector3[m_RadarPoints.Length + 1];
        Vector2 rectSize = targetArea.rect.size;
        Vector2 centerPos = targetArea.rect.center;
        Vector3 offsetPos = new Vector3(centerPos.x, centerPos.y, 0);
        float radius = Mathf.Min(rectSize.x, rectSize.y) / 2.0f;
        float angle = 360.0f / m_RadarPoints.Length;
        Vector3 startPos = Vector3.up;
        Quaternion rotate = Quaternion.Euler(0, 0, -angle);
        Vector3 nextPos = rotate * startPos;
        m_Vertexes[0] = Vector3.zero + offsetPos;
        m_Vertexes[1] = startPos;
        for (int i = 1; i < m_RadarPoints.Length; i++)
        {
            m_Vertexes[i + 1] = nextPos;
            nextPos = rotate * nextPos;
        }
        for (int i = 0; i < m_RadarPoints.Length; i++)
        {
            m_RadarPoints[i].percent = Mathf.Lerp(m_RadarPoints[i].percent, m_RadarPoints[i].targetPercent, 0.1f);
            if (m_RadarPoints[i].percent != m_RadarPoints[i].targetPercent)
                isDirty = true;
        }
        for (int i = 1, j = 0; i < m_Vertexes.Length; i ++, j++)
            m_Vertexes[i] = m_Vertexes[i] * radius * Mathf.Clamp01(m_RadarPoints[j].percent) + offsetPos;
    }

    SetAllDirty();
}

3.画出多边形。

如上图所示,这个正五边形由 012、023、034、045、051, 5个三角形组成。

多边形都可以由多个三角形组成。

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

    if (m_Vertexes != null && m_Vertexes.Length >= 2)
    {
        vh.AddVert(m_Vertexes[0], color, Vector2.zero);
        for (int i = 1; i < m_Vertexes.Length; i++)
            vh.AddVert(m_Vertexes[i], color, Vector2.zero);

        for (int i = 1; i < m_Vertexes.Length; i++)
        {
            if (i == m_Vertexes.Length - 1)
                vh.AddTriangle(0, m_Vertexes.Length - 1, 1);
            else
                vh.AddTriangle(0, i, i + 1);
        }
        vh.FillMesh(s_Mesh);
    }
}

4.于是我们可以通过修改顶点个数,填写相应顶点的具体数据来获得想要的多边形。 将!

345

 

 

 

 

 

【给多边形添加外框】

第一反应就是给每个顶点再加一个点,延伸出线宽的距离, 再把这些点连起来。

但是一个点只能指定一个颜色,emmm,没办法,再多一个点呗。

1.再加上线框顶点的位置计算。

public void refresh()
{
    if (m_RadarPoints == null || m_RadarPoints.Length == 0)
        m_Vertexes = null;
    else
    {
        RectTransform targetArea = GetComponent();
        m_Vertexes = new Vector3[m_RadarPoints.Length * 3 + 1];
        Vector2 rectSize = targetArea.rect.size;
        Vector2 centerPos = targetArea.rect.center;
        Vector3 offsetPos = new Vector3(centerPos.x, centerPos.y, 0);
        float radius = Mathf.Min(rectSize.x, rectSize.y) / 2.0f;
        float angle = 360.0f / m_RadarPoints.Length;
        Vector3 startPos = Vector3.up;
        Quaternion rotate = Quaternion.Euler(0, 0, -angle);
        Vector3 nextPos = rotate * startPos;
        m_Vertexes[0] = Vector3.zero + offsetPos;
        m_Vertexes[1] = startPos;
        m_Vertexes[2] = startPos;
        m_Vertexes[3] = startPos;
        for (int i = 1; i < m_RadarPoints.Length * 3 - 2; i += 3)
        {
            m_Vertexes[i + 3] = nextPos;
            m_Vertexes[i + 4] = nextPos;
            m_Vertexes[i + 5] = nextPos;
            nextPos = rotate * nextPos;
        }
        for (int i = 0; i < m_RadarPoints.Length; i++)
        {
            m_RadarPoints[i].percent = Mathf.Lerp(m_RadarPoints[i].percent, m_RadarPoints[i].targetPercent, 0.1f);
            if (m_RadarPoints[i].percent != m_RadarPoints[i].targetPercent)
                isDirty = true;
        }
        for (int i = 1, j = 0; i < m_Vertexes.Length; i += 3, ++j)
        {
            m_Vertexes[i] = m_Vertexes[i] * radius * Mathf.Clamp01(m_RadarPoints[j].percent) + offsetPos;
            m_Vertexes[i + 1] = m_Vertexes[i + 1] * radius * Mathf.Clamp01(m_RadarPoints[j].percent) + offsetPos;
            m_Vertexes[i + 2] = m_Vertexes[i + 2] * (radius - m_LineWidth) * Mathf.Clamp01(m_RadarPoints[j].percent) + offsetPos;
        }
    }

    SetAllDirty();
}

2.将这些线框顶点连起来。

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

    if (m_Vertexes != null && m_Vertexes.Length >= 2)
    {
        vh.AddVert(m_Vertexes[0], color, Vector2.zero);
        for (int i = 1; i < m_Vertexes.Length; i += 3)
        {
            vh.AddVert(m_Vertexes[i], color, Vector2.zero);
            vh.AddVert(m_Vertexes[i + 1], m_LineColor, Vector2.zero);
            vh.AddVert(m_Vertexes[i + 2], m_LineColor, Vector2.zero);
        }

        for (int i = 1; i < m_Vertexes.Length; i += 3)
        {
            if (i == m_Vertexes.Length - 3)
            {
                vh.AddTriangle(0, m_Vertexes.Length - 3, 1);
                vh.AddTriangle(m_Vertexes.Length - 3 + 1, m_Vertexes.Length - 3 + 2, 2);
                vh.AddTriangle(m_Vertexes.Length - 3 + 2, 2, 3);
            }
            else
            {
                vh.AddTriangle(0, i, i + 3);
                vh.AddTriangle(i + 1, i + 2, i + 4);
                vh.AddTriangle(i + 2, i + 4, i + 5);
            }
        }
        vh.FillMesh(s_Mesh);
    }
}

3.于是我们可以得到带有线框的多边形啦。将!

xiankuang

4.缺点

这种实现方法,在percent较小的时候,线条会变得很细,当percent为0时,甚至看不到线框。sad

(emmmm 太懒了 来日再寻找方法改进吧

xiankuang1xiankuang2

 

 

 

 

 

 

【Balabalabala】

闲着无聊,发现给Radar再加个Mask组件,还挺好玩的。

(忽略这个AV画质…

radar-gif

【脚本】

Radar

One Comment

Add a Comment

您的电子邮箱地址不会被公开。 必填项已用*标注