Quick C# Array Initialization Question (Mesh.Colors)

The following code snippets are executed in the Start function for a component and attempts to modify a Mesh’s per-vertex color array. Assume the Mesh’s material and shader are properly set.

Allocating the colors array for a mesh in the following manner does not work:

        // Does not work
         GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
         Mesh cubeMesh = cube.GetComponent<MeshFilter>().mesh;

         cubeMesh.colors = new UnityEngine.Color[cubeMesh.vertexCount];
         for (int i = 0; i < cubeMesh.vertexCount; i++)
         {
             cubeMesh.colors[i] = Color.blue;
         }

Allocating in the following manner works perfectly:

        // Works (cube colors change)
         GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
         Mesh cubeMesh = cube.GetComponent<MeshFilter>().mesh;

         Color []cubeColors = new UnityEngine.Color[cubeMesh.vertexCount];
         for (int i = 0; i < cubeMesh.vertexCount; i++)
         {
             cubeColors[i] = Color.blue;
         }
         cubeMesh.colors = cubeColors;

Why doesn’t the first method work?

The mesh.colors array is a copy. When you assign an array to the mesh, it’s uploaded to the graphics card. It would probably be more clear if they had used functions instead of properties (i.e. cubeMesh.GetColors() and SetColors()), since the way it is makes it look like you’re working with a reference when you’re not.

–Eric

I’m going to guess the mutator for Mesh.colors also sends a signal that the colours have changed and the mesh should be redrawn. Setting the array directly should change the underlying values but may not trigger a refresh. What happens if you do the following?

GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
Mesh cubeMesh = cube.GetComponent<MeshFilter>().mesh;

cubeMesh.colors = new UnityEngine.Color[cubeMesh.vertexCount];
for (int i = 0; i < cubeMesh.vertexCount; i++)
{
  cubeMesh.colors[i] = Color.blue;
}
cubeMesh.colors = (Color[])cubeMesh.colors.Clone();

This is a very good example of when arrays should be treated as immutable objects. I think Mesh.colors should return a IEnumerable rather than a Color[ ].

That makes a lot of sense! Do you have any general C# tip for identifying when your working with a reference or a copy?

This doesn’t seem to work. Which makes sense and proves the above answer by Eric5h5 is spot on since each time “cubeMesh.colors” is called (in the loop), it returns a copy of the array.

Here is all the complete code for future reference. To test just create a new scene add the “SetVertexColorTest” component to any game object, create a new shader, paste in the shader code and hit play.

using UnityEngine;
using System.Collections;

public class SetVertexColorTest : MonoBehaviour
{
    // Use this for initialization
    void Start ()
    {
        GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        Mesh cubeMesh = cube.GetComponent<MeshFilter>().mesh;
        cube.GetComponent<MeshRenderer>().material = new Material(Shader.Find("VertexColour"));

        cubeMesh.colors = new UnityEngine.Color[cubeMesh.vertexCount];
        for (int i = 0; i < cubeMesh.vertexCount; i++)
        {
            cubeMesh.colors[i] = Color.blue;
        }
// Doesn't work
        cubeMesh.colors = (Color[])cubeMesh.colors.Clone();
    }
}

And the shader:

Shader "VertexColour"
{
SubShader {
    Pass {
        Fog { Mode Off }
        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        // vertex input: position, color
        struct appdata {
            float4 vertex : POSITION;
            fixed4 color : COLOR;
        };

        struct v2f {
            float4 pos : SV_POSITION;
            fixed4 color : COLOR;
        };
  
        v2f vert (appdata v) {
            v2f o;
            o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
            o.color = v.color;
            return o;
        }
  
        fixed4 frag (v2f i) : SV_Target { return i.color; }
        ENDCG
    }
}
}

I suppose one way is to do the test you just discovered… Change it and then check if the change sticks.

Generally however, I would call this a code smell. The framework is treating this property as a value type so it should have been immutable.