Hi guys,
I have a problem which drives me crazy about material swapping.
[CONTEXT]
This happens on iOS. Not saying it doesn’t happen on other platforms, just that this is the one I’m testing with.
Imagine a chess game; the board is composed of multiple tiles, the user being able to touch them in order to select them. Depending on which tile is selected and what the user is allowed to do with it (attack, move, etc.) the tiles change their material in order to give the user a visual feedback.
[PROBLEM]
I’m running my game on an iPad, using Xcode’s Instruments to follow the memory consumption. I noticed that every time I touch a tile and thus change its material some new memory is allocated. I don’t get why, because all I do is
if (.....)
renderer.material = attackMat;
else if (...)
renderer.material = validMoveMat;
where attackMat and validMoveMat are some standard Unity Material already assigned at scene start.
The 2nd point is that this memory allocation occurs only the 1st time I touch each tile: if I touch tile A1 some memory is allocated and its material is changed; if I touch it again (whenever, not necessarily justa after the first touch) its material will be changed again, but no more memory allocation (even if the new material is not one of the 2 already used).
I ran some extensive tests and am pretty sure now that the memory is allocated for that purpose, I just don’t understand why and how to avoid it.
My guess is that the engine “batches” your similar textured objects in the beginning… and sees that
“Oh you have black and white tiles… let me create 2 memory slots for you”…
And when you introduce the new material… it will go
“Oh so you actually needed one more… let me give you a total of 3 slots to play with”
And thus from then on… you’ll be running on 3… and it wont reduce back down even when you revert back to the original material…
(Metaphorically speaking)
Right, just read the docs on Renderer.material and don’t skip the “important note”.
Whenever you read or write to renderer.material, Unity will duplicate the material so this renderer has it’s own instance. Once a renderer has a unique instance it will keep it.
This will actually break any batching since each tile you click will now have a unique material. What you actually want to do is this:
if (...)
renderer.sharedMaterial = attackMat;
else if (...)
renderer.sharedMaterial = validMoveMat;
“sharedMaterial” will return a reference to the shared Material instance. When you set the sharedMaterial Unity will save the reference and will not duplicate the material.
Keep in mind that since the different objects share the same material, any changes on the material itself will affect all objects that use this material.
Note by change on the material i mean things like: