Create a Pie Chart in Unity

Hi guys, well I’m creating some charts to show the scores in my game, so far I have coded a bar chart like this one:

1193831--47212--$barcharts2.png

But now I want to create pie charts and I quite don’t know where to start from. Any ideas? Thanks in advance!

Two ways I can think of is:
1: have a simple narrow piece of 3d ‘pie’ in a suitable angle, which could be instantiated to the needed value.
2: make a spritesheet of a 360 animation and offset to the frame needed.

Btw your bar chart is not showing.

It does show, but not in all browsers. Seems to be a forum bug. The best/most flexible way would be to create pie chart meshes using the Mesh class, though that does require an understanding of how to build meshes programmatically.

–Eric

Thanks guys, @Oparallel, I’m a programmer, not a 3d artist so I don’t want to get messy creating a 3d animation (since that’s not my field). @Eric5h5 do you know where can I find some information about creating meshes from code? I would really REALLY appreciate it :stuck_out_tongue: thanks!

The docs, to start with… :wink:

–Eric

I’ve already read them anyway I found this one I think it looks fine, anyone who may need it, here is the link: http://blog.nobel-joergensen.com/2010/12/25/procedural-generated-mesh-in-unity/

Hi!

Here is a test project for a procedural PieChart: 1194223–47255–$PieChart.zip (141 KB)

BR,
Juha

1 Like

@Jfo, Wow! thanks a lot! Where did you get it? I’ve been searching like crazy all day haha thanks!!!

No problem. It’s an old test project made by me. Feel free to use it.

Wow thanks a lot! :slight_smile:

Hey JFo!
Thanks for providing the only usable pie chart script I could find available online.
It generates the pie charts perfect - the only issue is that I can get the Pie to be visible in Scene View, but not in Game View.
Do I have to attach anything specific to a camera to make it visible?

EDIT: I’ve been playing around in the source view - and it appears that if I place the Pie object in a canvas with other panels, etc, it will always go to the back.

Hi JFo!
Thanks for provide great pie chart. It works for me.
But I have a problem: How to display text (like 20%, 30%) to each pie of chart?

Thanks alot!

Liked the script, made some modifications so drawing is animated and can use any number of segments and material, randomly generating the materials color at runtime.

using UnityEngine;
using System.Collections;

public class PieChartMesh : MonoBehaviour
{

    float[] mData;

    int mSlices;
    float mRotationAngle;
    float mRadius;
    Material[] mMaterials;

    Vector3[] mVertices;
    Vector3[] mNormals;
    Vector3 mNormal = new Vector3(0f, 0f, -1f);
    Vector2[] mUvs;
    int[] mTriangles;
    MeshRenderer mMeshRenderer;
    float delay=0.1f;

    public void Init(float[] data, int slices, float rotatioAngle, float radius, Material[] materials, float speed)
    {
        mData = data;
        mSlices = slices;
        mRotationAngle = rotatioAngle;
        mRadius = radius;
        delay = speed;

        // Get Mesh Renderer
        mMeshRenderer = gameObject.GetComponent("MeshRenderer") as MeshRenderer;
        if (mMeshRenderer == null)
        {
            gameObject.AddComponent<MeshRenderer>();
            mMeshRenderer = gameObject.GetComponent("MeshRenderer") as MeshRenderer;
        }
       
        mMeshRenderer.materials = materials;

        mMaterials = materials;


        Init(data);
    }

    public void Init(float[] data)
    {
        mSlices = 100;
        mRotationAngle = 90f;
        mRadius = 0.3f;

        mData = data;
    }

    public void Draw(float[] data)
    {
        mData = data;
        StopAllCoroutines ();
        StartCoroutine(Draw());
    }

    public IEnumerator Draw()
    {

        //Check data validity for pie chart...
        while  (mData == null)
        {
            print("PieChart: Data null");
            yield return null;
        }

        for (int i = 0; i < mData.Length; i++)
        {
            if (mData[i] < 0)
            {
                print("PieChart: Data < 0");
                yield return null;
            }
        }

        // Calculate sum of data values
        float sumOfData = 0;
        foreach (float value in mData)
        {
            sumOfData += value;
        }
        if (sumOfData <= 0)
        {
            print("PieChart: Data sum <= 0");
            yield return null;
        }
        // Determine how many triangles in slice
        int[] slice = new int[mData.Length];
        int numOfTris = 0;
        int numOfSlices = 0;
        int countedSlices = 0;

        // Caluclate slice size
        for (int i = 0; i < mData.Length; i++)
        {
            numOfTris = (int)((mData[i] / sumOfData) * mSlices);
            slice[numOfSlices++] = numOfTris;
            countedSlices += numOfTris;
        }
        // Check that all slices are counted.. if not -> add/sub to/from biggest slice..
        int idxOfLargestSlice = 0;
        int largestSliceCount = 0;
        for (int i = 0; i < mData.Length; i++)
        {
            if (largestSliceCount < slice[i])
            {
                idxOfLargestSlice = i;
                largestSliceCount = slice[i];
            }
        }

        // Check validity for pie chart
        if (countedSlices == 0)
        {
            print("PieChart: Slices == 0");
            yield return null;
        }

        // Adjust largest dataset to get proper slice
        slice[idxOfLargestSlice] += mSlices - countedSlices;

        // Check validity for pie chart data
        if (slice[idxOfLargestSlice] <= 0)
        {
            print("PieChart: Largest pie <= 0");
            yield return null;
        }

        // Init vertices and triangles arrays
        mVertices = new Vector3[mSlices * 3];
        mNormals = new Vector3[mSlices * 3];
        mUvs = new Vector2[mSlices * 3];
        mTriangles = new int[mSlices * 3];

        //gameObject.AddComponent("MeshFilter");
        //gameObject.AddComponent("MeshRenderer");

        Mesh mesh = ((MeshFilter)GetComponent("MeshFilter")).mesh;

        mesh.Clear();

        mesh.name = "Pie Chart Mesh";

        // Roration offset (to get star point to "12 o'clock")
        float rotOffset = mRotationAngle / 360f * 2f * Mathf.PI;

        // Calc the points in circle
        float angle;
        float[] x = new float[mSlices];
        float[] y = new float[mSlices];

        for (int i = 0; i < mSlices; i++)
        {
            angle = i * 2f * Mathf.PI / mSlices;
            x[i] = (Mathf.Cos(angle + rotOffset) * mRadius);
            y[i] = (Mathf.Sin(angle + rotOffset) * mRadius);
        }

        // Generate mesh with slices (vertices and triangles)
        for (int i = 0; i < mSlices; i++)
        {
            mVertices[i * 3 + 0] = new Vector3(0f, 0f, 0f);
            mVertices[i * 3 + 1] = new Vector3(x[i], y[i], 0f);
            // This will ensure that last vertex = first vertex..
            mVertices[i * 3 + 2] = new Vector3(x[(i + 1) % mSlices], y[(i + 1) % mSlices], 0f);

            mNormals[i * 3 + 0] = mNormal;
            mNormals[i * 3 + 1] = mNormal;
            mNormals[i * 3 + 2] = mNormal;

            mUvs[i * 3 + 0] = new Vector2(0f, 0f);
            mUvs[i * 3 + 1] = new Vector2(x[i], y[i]);
            // This will ensure that last uv = first uv..
            mUvs[i * 3 + 2] = new Vector2(x[(i + 1) % mSlices], y[(i + 1) % mSlices]);

            mTriangles[i * 3 + 0] = i * 3 + 0;
            mTriangles[i * 3 + 1] = i * 3 + 1;
            mTriangles[i * 3 + 2] = i * 3 + 2;
        }


        // Assign verts, norms, uvs and tris to mesh and calc normals
        mesh.vertices = mVertices;
        mesh.normals = mNormals;
        mesh.uv = mUvs;
        //mesh.triangles = triangles;

        mesh.subMeshCount = mData.Length;

        int[][] subTris = new int[mData.Length][];

        countedSlices = 0;

        // Set sub meshes
        for (int i = 0; i < mData.Length; i++)
        {
            // Every triangle has three veritces..
            subTris[i] = new int[slice[i] * 3];

            // Add tris to subTris
            for (int j = 0; j < slice[i]; j++)
            {
                subTris[i][j * 3 + 0] = mTriangles[countedSlices * 3 + 0];
                subTris[i][j * 3 + 1] = mTriangles[countedSlices * 3 + 1];
                subTris[i][j * 3 + 2] = mTriangles[countedSlices * 3 + 2];

                if ( j%5 == 0 )
                    yield return new WaitForSeconds(delay);

                mesh.SetTriangles(subTris[i], i);
                countedSlices++;
            }
           
        }
    }

    // Properties
    public float[] Data { get { return mData; } set { mData = value; } }

    public int Slices { get { return mSlices; } set { mSlices = value; } }

    public float RotationAngle { get { return mRotationAngle; } set { mRotationAngle = value; } }

    public float Radius { get { return mRadius; } set { mRadius = value; } }

    public Material[] Materials { get { return mMaterials; } set { mMaterials = value; } }

}
using UnityEngine;

public class PieChartMeshController : MonoBehaviour
{
    PieChartMesh mPieChart;
    float[] mData;
    public float delay=0.1f;
    public Material mainMaterial;
    public Material[] materials;
    public int segments;
    private int randomBuffer;

    void Start()
    {
        randomBuffer = Random.seed;
        Random.seed = 10;

        materials = new Material[segments];
        for (int i=0; i<segments; i++) {
            materials[i]= new Material(mainMaterial);
            materials[i].color=new Color32((byte)Random.Range(0,256),(byte)Random.Range(0,256),(byte)Random.Range(0,256),(byte)255);
            Debug.LogError(materials[i].color);
        }

        mPieChart = gameObject.AddComponent<PieChartMesh>() as PieChartMesh;
        if (mPieChart != null)
        {
            mPieChart.Init(mData, 100, 0, 100, materials,delay);
            mData = GenerateRandomValues(segments);
            mPieChart.Draw(mData);
        }
        Random.seed = randomBuffer;
    }

    void Update()
    {
        if (Input.GetKeyDown("a"))
        {
            randomBuffer = Random.seed;
            Random.seed = 10;
            mData = GenerateRandomValues(segments);
            mPieChart.Draw(mData);
            Random.seed = randomBuffer;
        }
    }

    float[] GenerateRandomValues(int length)
    {
        float[] targets = new float[length];

        for (int i = 0; i < length; i++)
        {
            targets[i] = Random.Range(0f, 100f);
        }
        return targets;
    }
}

Hello,
I used the pie chart project but it doesnot take decimal values , it only shows whole values in graph.
How to solve this issue ??

@JFo your project works like charm, but why the Pie Chart is not scaling in Z-Axis ? and how can I do it?

IF YOU WANT THINGS IN 3D here is code for “PieChartMeshController.cs”

using UnityEngine;

public class PieChartMeshController : MonoBehaviour
{
    PieChartMesh mPieChart;
    public float[] mData;
    public float delay = 0.1f;
    public Material mainMaterial;
    public Material[] materials;
    public int segments;
    private int randomBuffer;

    public GameObject Meshes,Cube;

    void Start()
    {
        randomBuffer = Random.seed;
        Random.seed = 10;

        materials = new Material[segments + 1];
        for (int i = 0; i < segments + 1; i++)
        {
            materials[i] = new Material(mainMaterial)
            {
                color = new Color32((byte)Random.Range(0, 256), (byte)Random.Range(0, 256), (byte)Random.Range(0, 256), (byte)255)
            };
        }

        mPieChart = gameObject.AddComponent<PieChartMesh>() as PieChartMesh;
        if (mPieChart != null)
        {
            mPieChart.Init(mData, 1000, 0, 100, materials, delay,Meshes);
            mData = GenerateRandomValues(segments);
            mPieChart.Draw(mData);
        }
        Random.seed = randomBuffer;
    }

    void Update()
    {
        if (Input.GetKeyDown("a"))
        {
            randomBuffer = Random.seed;
            Random.seed = 10;
            mData = GenerateRandomValues(segments);
            mPieChart.Draw(mData);
            Random.seed = randomBuffer;
        }
    }

    float[] GenerateRandomValues(int length)
    {
        float[] targets = new float[length];
        for (int i = 0; i < length; i++)
        {
            targets[i] = Random.Range(0f, 100f);
        }
        return targets;
    }
}

CODE FOR PieChartMesh.cs

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

public class PieChartMesh : MonoBehaviour
{

    float[] mData;

    int mSlices;
    float mRotationAngle;
    float mRadius;
    Material[] mMaterials;

    public Vector3[] mVertices, WRVertices;
    public Vector3[] FinalVerticesArray;
    Vector3[] mNormals, WRNormals;
    Vector3 mNormal = new Vector3(0, 0, -1);
    public Vector3 mWRNormal = new Vector3(1, 1, 1);
    Vector2[] mUvs;
    public int[] mTriangles;
    MeshRenderer mMeshRenderer;
    float delay = 0.1f;
    GameObject TempObject;

    public void Init(float[] data, int slices, float rotatioAngle, float radius, Material[] materials, float speed, GameObject otherobject)
    {
        TempObject = otherobject;
        mData = data;
        mSlices = slices;
        mRotationAngle = rotatioAngle;
        mRadius = radius;
        delay = speed;

        // Get Mesh Renderer
        mMeshRenderer = gameObject.GetComponent("MeshRenderer") as MeshRenderer;
        if (mMeshRenderer == null)
        {
            gameObject.AddComponent<MeshRenderer>();
            mMeshRenderer = gameObject.GetComponent("MeshRenderer") as MeshRenderer;
        }

        mMeshRenderer.materials = materials;

        mMaterials = materials;


        Init(data);
    }

    public void Init(float[] data)
    {
        mSlices = 100;
        mRotationAngle = 90f;
        mRadius = 0.3f;

        mData = data;
    }

    public void Draw(float[] data)
    {
        mData = data;
        StopAllCoroutines();
        StartCoroutine(Draw());
    }

    public IEnumerator Draw()
    {

        //Check data validity for pie chart...
        while (mData == null)
        {
            print("PieChart: Data null");
            yield return null;
        }

        for (int i = 0; i < mData.Length; i++)
        {
            if (mData[i] < 0)
            {
                print("PieChart: Data < 0");
                yield return null;
            }
        }

        // Calculate sum of data values
        float sumOfData = 0;
        foreach (float value in mData)
        {
            sumOfData += value;
        }
        if (sumOfData <= 0)
        {
            print("PieChart: Data sum <= 0");
            yield return null;
        }
        // Determine how many triangles in slice
        int[] slice = new int[mData.Length];
        int numOfTris = 0;
        int numOfSlices = 0;
        int countedSlices = 0;

        // Caluclate slice size
        for (int i = 0; i < mData.Length; i++)
        {
            numOfTris = (int)((mData[i] / sumOfData) * mSlices);
            slice[numOfSlices++] = numOfTris;
            countedSlices += numOfTris;
        }
        // Check that all slices are counted.. if not -> add/sub to/from biggest slice..
        int idxOfLargestSlice = 0;
        int largestSliceCount = 0;
        for (int i = 0; i < mData.Length; i++)
        {
            if (largestSliceCount < slice[i])
            {
                idxOfLargestSlice = i;
                largestSliceCount = slice[i];
            }
        }

        // Check validity for pie chart
        if (countedSlices == 0)
        {
            print("PieChart: Slices == 0");
            yield return null;
        }

        // Adjust largest dataset to get proper slice
        slice[idxOfLargestSlice] += mSlices - countedSlices;

        // Check validity for pie chart data
        if (slice[idxOfLargestSlice] <= 0)
        {
            print("PieChart: Largest pie <= 0");
            yield return null;
        }

        // Init vertices and triangles arrays
        mVertices = new Vector3[mSlices * 3];
        WRVertices = new Vector3[mSlices * 3];
        FinalVerticesArray = new Vector3[mSlices * 6];

        mNormals = new Vector3[mSlices * 6];
        WRNormals = new Vector3[mSlices * 3];

        mUvs = new Vector2[mSlices * 6];

        mTriangles = new int[mSlices * 6];
        //WRTriangles = new int[mSlices * 3];
        //FinalTriangles = new int[mSlices * 3];

        //gameObject.AddComponent("MeshFilter");
        //gameObject.AddComponent("MeshRenderer");

        //Mesh mesh = ((MeshFilter)GetComponent("MeshFilter")).mesh;
        //mesh.Clear();

        //mesh.name = "Pie Chart Mesh";

        // Roration offset (to get star point to "12 o'clock")
        float rotOffset = mRotationAngle / 360f * 2f * Mathf.PI;

        // Calc the points in circle
        float angle;
        float[] x = new float[mSlices];
        float[] y = new float[mSlices];

        for (int i = 0; i < mSlices; i++)
        {
            angle = i * 2f * Mathf.PI / mSlices;
            x[i] = (Mathf.Cos(angle + rotOffset) * mRadius);
            y[i] = (Mathf.Sin(angle + rotOffset) * mRadius);
        }
        // Generate mesh with slices (vertices and triangles)
        for (int i = 0; i < mSlices; i++)
        {
            mVertices[i * 3 + 0] = new Vector3(0f, 0f, 0f);
            mVertices[i * 3 + 1] = new Vector3(x[i], y[i], 0f);
            // This will ensure that last vertex = first vertex..
            mVertices[i * 3 + 2] = new Vector3(x[(i + 1) % mSlices], y[(i + 1) % mSlices], 0);

            WRVertices[i * 3 + 0] = new Vector3(0f, 0f, 0.033f);
            WRVertices[i * 3 + 1] = new Vector3(x[i], y[i], 0.033f);
            // This will ensure that last vertex = first vertex..
            WRVertices[i * 3 + 2] = new Vector3(x[(i + 1) % mSlices], y[(i + 1) % mSlices], 0.033f);

            mUvs[i * 3 + 0] = new Vector2(0f, 0f);
            mUvs[i * 3 + 1] = new Vector2(x[i], y[i]);
            // This will ensure that last uv = first uv..
            mUvs[i * 3 + 2] = new Vector2(x[(i + 1) % mSlices], y[(i + 1) % mSlices]);
        }

        for (int nextTris = 0; nextTris < mTriangles.Length; nextTris++)
        {
            mTriangles[nextTris] = nextTris;
        }
        for (int i = 0; i < mNormals.Length; i++)
        {
            mNormals[i] = Vector3.forward;
        }
        for (int i = 0; i < mNormals.Length / 2; i++)
        {
            WRNormals[i] = mNormal;
        }

        int k = 0, l = 0;
        for (k = 0, l = 0; k < mVertices.Length; k++)
        {
            FinalVerticesArray[l++] = mVertices[k];
        }
        for (k = 0; k < WRVertices.Length; k++)
        {
            FinalVerticesArray[l++] = WRVertices[k];
        }
        //GameObject trmpParent = Instantiate(new GameObject());
        //for (int i = 0; i < FinalVerticesArray.Length; i++)
        //{
        //    GameObject go = Instantiate(OtherTempObject, FinalVerticesArray[i], Quaternion.identity);
        //    go.transform.parent = trmpParent.transform;
        //    go.name = i.ToString();
        //}

        // Assign verts, norms, uvs and tris to mesh and calc normals
        //mesh.vertices = FinalVerticesArray;
        //mesh.normals = mNormals;
        //mesh.uv = mUvs;
        //mesh.triangles = triangles;

        //mesh.subMeshCount = mData.Length + 2;

        Mesh secondMesh = new Mesh();
        secondMesh.vertices = WRVertices;
        secondMesh.normals = WRNormals;
        //secondMesh.uv = mUvs;

        int addingelement = 1;
        int addingelementSide = 1;

        int[][] subTris = new int[mData.Length][];
        int[][] subTris3D = new int[mData.Length][];

        countedSlices = 0;

        // Set sub meshes
        for (int i = 0; i < mData.Length; i++)
        {
            // Every triangle has three veritces..
            subTris[i] = new int[slice[i] * 3];
            subTris3D[i] = new int[slice[i] * 3];
            int[] subTrisLeft = new int[slice[i] * 3];
            int[] subTrisRight = new int[slice[i] * 3];
            int[] sidetrinaglesA = new int[slice[i] * 3];
            int[] sidetrinaglesB = new int[slice[i] * 3];
            int sliceint = slice[i];
            for (int j = 0; j < sliceint; j++)
            {
                subTris[i][j * 3 + 0] = mTriangles[countedSlices * 3 + 0];
                subTris[i][j * 3 + 1] = mTriangles[countedSlices * 3 + 1];
                subTris[i][j * 3 + 2] = mTriangles[countedSlices * 3 + 2];

                subTris3D[i][j * 3 + 0] = mTriangles[countedSlices * 3 + 300];
                subTris3D[i][j * 3 + 1] = mTriangles[countedSlices * 3 + 301];
                subTris3D[i][j * 3 + 2] = mTriangles[countedSlices * 3 + 302];

                if ((addingelement + 303) <= 600)
                {
                    subTrisLeft[j * 3 + 0] = addingelement + 3;
                    subTrisLeft[j * 3 + 1] = addingelement;
                    subTrisLeft[j * 3 + 2] = (addingelement + 3) + 300;


                    subTrisRight[j * 3 + 0] = addingelement + 300;
                    subTrisRight[j * 3 + 1] = addingelement;
                    subTrisRight[j * 3 + 2] = (addingelement + 303);
                }
                sidetrinaglesA[j * 3 + 0] = subTris[i][j * 3 + 0];
                sidetrinaglesA[j * 3 + 1] = subTris3D[i][j * 3 + 0];
                sidetrinaglesA[j * 3 + 2] = subTris[i][j * 3 + 2];

                sidetrinaglesB[j * 3 + 0] = subTris3D[i][j * 3 + 0];
                sidetrinaglesB[j * 3 + 1] = subTris3D[i][j * 3 + 2];
                sidetrinaglesB[j * 3 + 2] = subTris[i][j * 3 + 2];


                addingelement += 3;
                //if (j % 5 == 0)
                //    yield return new WaitForSeconds(delay);
                countedSlices++;
                //mesh.SetTriangles(subTris[i], i);
            }

            int[] sidetrinaglesFinal = new int[12];

            sidetrinaglesFinal[0] = sidetrinaglesA[0];
            sidetrinaglesFinal[1] = sidetrinaglesA[1];
            sidetrinaglesFinal[2] = sidetrinaglesA[2];
            sidetrinaglesFinal[3] = sidetrinaglesA[sidetrinaglesA.Length - 3];
            sidetrinaglesFinal[4] = sidetrinaglesA[sidetrinaglesA.Length - 2];
            sidetrinaglesFinal[5] = sidetrinaglesA[sidetrinaglesA.Length - 1];
            sidetrinaglesFinal[6] = sidetrinaglesB[0];
            sidetrinaglesFinal[7] = sidetrinaglesB[1];
            sidetrinaglesFinal[8] = sidetrinaglesB[2];
            sidetrinaglesFinal[9] = sidetrinaglesB[sidetrinaglesA.Length - 3];
            sidetrinaglesFinal[10] = sidetrinaglesB[sidetrinaglesA.Length - 2];
            sidetrinaglesFinal[11] = sidetrinaglesB[sidetrinaglesA.Length - 1];




            var Final = subTris[i].Concat(subTris3D[i]).Concat(subTrisLeft).Concat(sidetrinaglesFinal).Concat(subTrisRight);

            CreateObjectAndSetMesh(FinalVerticesArray, Final.ToArray(), "Parts" + i);
        }

    }
    void CreateObjectAndSetMesh(Vector3[] vertices, int[] triangles, string objectname)
    {
        GameObject gameObj = Instantiate(TempObject);
        Mesh m = gameObj.GetComponent<MeshFilter>().mesh;
        gameObj.name = objectname;
        m.vertices = vertices;
        m.triangles = triangles;
        m.Optimize();

        Material mainMaterial = new Material(Materials[0])
        {
            color = new Color32((byte)Random.Range(0, 256), (byte)Random.Range(0, 256), (byte)Random.Range(0, 256), (byte)255)
        };

        gameObj.GetComponent<MeshRenderer>().material = mainMaterial;
    }
    public float[] Data { get { return mData; } set { mData = value; } }

    public int Slices { get { return mSlices; } set { mSlices = value; } }

    public float RotationAngle { get { return mRotationAngle; } set { mRotationAngle = value; } }

    public float Radius { get { return mRadius; } set { mRadius = value; } }

    public Material[] Materials { get { return mMaterials; } set { mMaterials = value; } }

}

how can i give them an outline?

are there any way to give them an outline?