- Home /
Mobile efficiency: this.renderer.material.color vs vertex shaders for changing object color.
I currently have 3 different types of enemies spawning into a scene.
Each of these three enemy types are spawned based off of a prefab; enemy1, enemy2, enemy3.
Each enemy takes damage individually although there may be 5 of enemy 1, 5 of enemy2, and 5 of enemy3 in the scene at any given time (often even more than 5 of each).
At the moment when the enemies take damage I have the individual enemy change colors (using: this.renderer.material.color) from green to yellow to red as they get closer to death (these colors were just a proof of concept for myself).
I am concerned about overall performance, as this game is being designed for a mobile platform, and was recently told that somehow changing individual colors of my spawned enemies would cut down the graphical performance of the game and that I should use vertex shaders in place of "this.renderer.material.color."
I do not understand why the performance would be better (and specifically for a mobile platform) if I made the replacement. Can someone attempt to explain this or point me in the right direction?
Thank you.
Answer by Peter G · Oct 23, 2011 at 08:24 PM
My guess is that whoever told you this was referring to the instancing of materials. When you change the property of a material, Unity makes a new instance of a material to accommodate the change. This way only the individual object changes. But the biggest drawback is that the object won't be batched anymore, and that adds draw calls which is a problem on mobile platforms.
Vertex colors could be what this person meant by vertex shaders. You can use a vertex colored shader, then change the vertex colors of the mesh. You need this line in your fixed-function shader:
ColorMaterial AmbientAndDiffuse
If you don't understand shaders, then there are several built-in vertex colored shaders that will do what you want.
Then you need to get the mesh data of your model and change its vertex colors.
var mesh : Mesh = GetComponent.<MeshFilter>().mesh;
var colors = mesh.colors;
for (var i = 0; i < colors.Length ; i++){
colors[i] = Color.Red;
//just for example.
}
mesh.colors = colors;
I haven't done side by side testing so I can't say which way is faster. If you don't have that many characters, it really doesn't make much difference. If you have lots of objects then the second choice might be faster.
Sounds reasonable.
As far as implementation, I tried the following:
function DrawHealth()
{ var mesh : $$anonymous$$esh = GetComponent.<$$anonymous$$eshFilter>().mesh;
var colors = mesh.colors;
if (health <=90)
{
colors = Color.Green;
}
if (health <=60)
{
colors = Color.Yellow;
}
if (health <=30)
{
colors = Color.Red;
}
if (health <=10)
{
colors = Color.Black;
}
mesh.colors = colors;
}
I get no errors, but as my units' health decreases I do not get color change for some reason. I am using the $$anonymous$$obile / Vertex Colored shader that came with Unity.
Ok, here's the issue. mesh.colors is an array of Colors so you need to assign it an array of values.
if (health <= 90) {
for (var color in Colors) {
color = Color.Green;
}
}
You really should use a for(;;) loop doing this especially on mobile platforms because its more efficient with arrays, but the foreach loop gets the idea across better.
Interesting, when it calls the line
"color = Color.Green;"
I get the following error:
NullReferenceException: Object reference not set to an instance of an object EnemyExistence.DrawHealth () (at Assets/TowerDefense Assets/Scripts/EnemyExistence.js:52) EnemyExistence.OnCollisionEnter (UnityEngine.Collision hit) (at Assets/TowerDefense Assets/Scripts/EnemyExistence.js:30)
I have not really trouble shooted (shot? ha) this yet, but I do like to post a notification comment as soon as you can so you know I am working on it. Will work on this tonight/tomorrow (more tomorrow) and reply back.
Thank you for the tips so far.
O$$anonymous$$, I took the information you gave me, looked at the Unity Script Reference for $$anonymous$$esh.colors.
I replaced all of the above code with :
var mesh : $$anonymous$$esh = GetComponent($$anonymous$$eshFilter).mesh;
var vertices : Vector3[] = mesh.vertices;
var colors : Color[] = new Color[vertices.Length];
for (var i = 0; i < vertices.Length;i++)
colors[i] = Color.Lerp(Color.red, Color.green, vertices[i].y);
mesh.colors = colors;
The result is that when I call this set of code my objects change to a red/green color (the bottom of them appears to be red, the top of them appears to be green) - which is progress - but I do not quite understand what is going on.
What would I need to change in the above snippet of code to get the color of the entire object to change based on a variable I have labeled as health? (I want the objects to get more and more red with the more damage they take, with maximum damage being "red" - just before they 'die.')
UPDATE *
I changed my code to this:
var mesh : $$anonymous$$esh = GetComponent($$anonymous$$eshFilter).mesh;
var vertices : Vector3[] = mesh.vertices;
var colors : Color[] = new Color[vertices.Length];
for (var i = 0; i < vertices.Length;i++)
/*colors[i] = Color.Lerp(Color.red, Color.green, Time.time);*/
//while (health <= 100)
colors[i] = Color.Lerp(Color.black, Color.blue, Time.time);
/*while (health <= 80)
colors[i] = Color.Lerp(Color.blue, Color.green, Time.time);
while (health <= 60)
colors[i] = Color.Lerp(Color.green, Color.yellow, Time.time);
while (health <= 40)
colors[i] = Color.Lerp(Color.yellow, Color.orange, Time.time);
while (health <= 20)
colors[i] = Color.Lerp(Color.orange, Color.red, Time.time);*/
mesh.colors = colors;
--But I have all my while statements commented out as it causes Unity to lock up. I want to follow roughly this logic, how would I implement this correctly?
(I know I cannot use the while statements like I am above (infinite loops much?), but I want to follow this 'kind' of logic, should I go back to using if statements?)
Sorry to take so long to answer, I haven't been around UA that frequently lately.
You are trying to smooth out the color it looks like. The cleanest way would be a spline interpolation, but that's somewhat complicated to implement so you can do it with a few if
statements and some while()
blocks. You will probably need some yield
statements. Unity uses yield differently than the C# keyword so check the reference on how to effectively use them.
Your answer
Follow this Question
Related Questions
Is this a good practice for mobile devices? 2 Answers
Vertical mobile input 0 Answers
Mobile 2.5D MMO Game Development Advice 0 Answers
Problem with GUI on different resolutions 1 Answer
Shader optim ? 1 Answer