[Unity]利用Mesh在Unity中繪製扇形圖片

背景

最近碰到個功能, 要畫一個扇形圖案, 如下圖:
角色背後的攻擊範圍標識
美術原圖:
美術原圖

需求是這個圖形跟隨角色, 在角色背後, 並且每個角色的扇形角度可能不同。
So, NGUI和UGUI很好用的FilledType是用不了了..

解決過程

去網上搜了下Unity繪製扇形
找到這麼篇文章http://blog.csdn.net/awnuxcvbn/article/details/44039819
很好, 前人已經造好了輪子, 我拿來用就好
結果:
遊戲場景
Scene

網格的扇形是有了, 但是遊戲裏的顯示是什麼鬼!所以說人是不能偷懶的, 還是老老實實的看看前人的輪子是怎麼造的吧.

我的理解與改造

既然看了, 就爲自己的需求做了些改造, 加了起始角度, 並把源代碼畫在x,z軸平面的扇形改到了x,y軸平面
還是上代碼:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class SectorProgressBar : MonoBehaviour
{


    public float radius = 2;
    public float startAngleDegree = 0;
    public float angleDegree = 100;
    public int segments = 10;
    public int angleDegreePrecision = 1000;
    public int radiusPrecision = 1000;

    private MeshFilter meshFilter;

    private SectorMeshCreator creator = new SectorMeshCreator();

    [ExecuteInEditMode]
    private void Awake()
    {
        meshFilter = GetComponent<MeshFilter>();
    }

    private void Update()
    {
        meshFilter.mesh = creator.CreateMesh(radius, startAngleDegree, angleDegree, segments, angleDegreePrecision, radiusPrecision);
    }

    //在Scene界面畫輔助線
    void OnDrawGizmos()
    {
        Gizmos.color = Color.gray;
        DrawMesh();
    }

    //在Scene界面畫輔助線
    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.green;
        DrawMesh();
    }

    private void DrawMesh()
    {
        Mesh mesh = creator.CreateMesh(radius, startAngleDegree, angleDegree, segments, angleDegreePrecision, radiusPrecision);
        int[] tris = mesh.triangles;
        for (int i = 0; i < tris.Length; i += 3)
        {
            Gizmos.DrawLine(convert2World(mesh.vertices[tris[i]]), convert2World(mesh.vertices[tris[i + 1]]));
            Gizmos.DrawLine(convert2World(mesh.vertices[tris[i]]), convert2World(mesh.vertices[tris[i + 2]]));
            Gizmos.DrawLine(convert2World(mesh.vertices[tris[i + 1]]), convert2World(mesh.vertices[tris[i + 2]]));
        }
    }

    private Vector3 convert2World(Vector3 src)
    {
        return transform.TransformPoint(src);
    }

    private class SectorMeshCreator
    {
        private float radius;
        private float startAngleDegree;
        private float angleDegree;
        private int segments;

        private Mesh cacheMesh;

        /// <summary>  
        /// 創建一個扇形Mesh  
        /// </summary>  
        /// <param name="radius">扇形半徑</param>  
        /// <param name="startAngleDegree">扇形開始角度</param> 
        /// <param name="angleDegree">扇形角度</param>  
        /// <param name="segments">扇形弧線分段數</param>  
        /// <param name="angleDegreePrecision">扇形角度精度(在滿足精度範圍內,認爲是同個角度)</param>  
        /// <param name="radiusPrecision">  
        /// <pre>  
        /// 扇形半價精度(在滿足半價精度範圍內,被認爲是同個半價)。  
        /// 比如:半價精度爲1000,則:1.001和1.002不被認爲是同個半徑。因爲放大1000倍之後不相等。  
        /// 如果半價精度設置爲100,則1.001和1.002可認爲是相等的。  
        /// </pre>  
        /// </param>  
        /// <returns></returns>  
        public Mesh CreateMesh(float radius, float startAngleDegree, float angleDegree, int segments, int angleDegreePrecision, int radiusPrecision)
        {
            if (checkDiff(radius, startAngleDegree, angleDegree, segments, angleDegreePrecision, radiusPrecision))
            {//參數有改變才需要重新畫mesh
                Mesh newMesh = Create(radius, startAngleDegree, angleDegree, segments);
                if (newMesh != null)
                {
                    cacheMesh = newMesh;
                    this.radius = radius;
                    this.startAngleDegree = startAngleDegree;
                    this.angleDegree = angleDegree;
                    this.segments = segments;
                }
            }
            return cacheMesh;
        }

        private Mesh Create(float radius, float startAngleDegree, float angleDegree, int segments)
        {
            if (segments == 0)
            {
                segments = 1;
#if UNITY_EDITOR
                Debug.Log("segments must be larger than zero.");
#endif
            }

            Mesh mesh = new Mesh();
            Vector3[] vertices = new Vector3[3 + segments - 1];
            vertices[0] = new Vector3(0, 0, 0);//第一個點是圓心點

            //uv是網格上的點對應到紋理上的某個位置的像素, 紋理是一張圖片, 所以是二維
            //理解以後才發現, 之前顯示出錯的原因是原來的代碼uv很隨意的拿了頂點的計算結果
            Vector2[] uvs = new Vector2[vertices.Length];
            uvs[0] = new Vector2(0.5f, 0.5f);//紋理的圓心在中心

            float angle = Mathf.Deg2Rad * angleDegree;
            float startAngle = Mathf.Deg2Rad * startAngleDegree;
            float currAngle = angle + startAngle; //第一個三角形的起始角度
            float deltaAngle = angle / segments; //根據分段數算出每個三角形在圓心的角的角度
            for (int i = 1; i < vertices.Length; i++)
            {   
                //圓上一點的公式: x = r*cos(angle), y = r*sin(angle)
                //根據半徑和角度算出弧度上的點的位置
                float x = Mathf.Cos(currAngle);
                float y = Mathf.Sin(currAngle);
                //這裏爲了我的需求改到了把點算到了(x,y,0), 如果需要其他平面, 可以改成(x,0,y)或者(0,x,y)
                vertices[i] = new Vector3(x  * radius, y * radius, 0);
                //紋理的半徑就是0.5, 圓心在0.5f, 0.5f的位置
                uvs[i] = new Vector2(x * 0.5f + 0.5f, y * 0.5f + 0.5f);
                currAngle -= deltaAngle;
            }

            int[] triangles = new int[segments * 3];
            for (int i = 0, vi = 1; i < triangles.Length; i += 3, vi++)
            {//每個三角形都是由圓心點+兩個相鄰弧度上的點構成的
                triangles[i] = 0;
                triangles[i + 1] = vi;
                triangles[i + 2] = vi + 1;
            }

            mesh.vertices = vertices;
            mesh.triangles = triangles;
            mesh.uv = uvs;

            return mesh;
        }

        private bool checkDiff(float radius, float startAngleDegree, float angleDegree, int segments, int angleDegreePrecision, int radiusPrecision)
        {
            return segments != this.segments || (int)(startAngleDegree - this.startAngleDegree) != 0 || (int)((angleDegree - this.angleDegree) * angleDegreePrecision) != 0 ||
                   (int)((radius - this.radius) * radiusPrecision) != 0;
        }
    }
}

後記

基本功能實現了~
其實還有個問題, 半徑(radius)比較大的情況, 會發現圖片失真.
這種圖其實是可以做到不失真的, 因爲, 內部是純透明的, 只有外圍一圈有顏色, 所以如果只縮放內部半段, 保持外部半段的半徑, 就可以實現.
示意圖:
這裏寫圖片描述

不過這次的需求並不需要放大圖片, 以後需要的時候再實現吧~

=========================
這篇文章出來後,和同事討論以後,對扇形繪製又做了優化:[Unity]利用Mesh在Unity中繪製扇形圖片2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章