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:
But now I want to create pie charts and I quite don’t know where to start from. Any ideas? Thanks in advance!
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:
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 thanks!
The docs, to start with…
–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/
@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!
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?