Reputation: 547
I have a space shooter where players can select a color for their ships, and this applies to the bullets the ships shoot as well. The ships use the same bullet prefab, so I would like to change the color of the bullet once it's created in the game. I originally used the following code to change the color.
Material bulletMat = this.GetComponent<MeshRenderer>().sharedMaterial;
if (bulletMat != null)
{
bulletMat.SetColor("_TintColor", color);
}
However, I found out that this will change the color of the material and the bullets would switch in between colors with each bullet created. After doing some more research, I came accross the MaterialPropertyBlock variable and the following tutorial http://thomasmountainborn.com/2016/05/25/materialpropertyblocks/. So I followed this tutorial and setup the following code.
public Renderer renderer;
public MaterialPropertyBlock matBlock;
public Color color;
public bool colorSet = false;
void Awake()
{
renderer = this.GetComponent<Renderer>();
matBlock = new MaterialPropertyBlock();
}
public void ColorSet(Color color)
{
this.color = color;
this.colorSet = true;
}
void Update()
{
if (this.colorSet)
{
renderer.GetPropertyBlock(matBlock);
matBlock.SetColor("_Color", this.color);
renderer.SetPropertyBlock(matBlock);
}
}
However, the same issue as the previous solution happened....
Can someone help me figure out how to set the color for each individual bullet?
Upvotes: 1
Views: 3335
Reputation: 90872
You use sharedMaterial
only wherever you really want to change the material for every Object using this material. That's exactly what you don't want to do. Instead you ahve to use material
so you only change a "clone" instance of the material.
Unity actually automatically instantiates a local copy of the current material only for that object if you do:
public class ColorChanger : MonoBehavior
{
private MeshRenderer meshRenderer;
private void OnEnable()
{
meshRenderer = GetComponent<MeshRenderer>();
}
public void SetColor(Color newColor)
{
renderer.material.color = newColor;
}
}
or using SetColor
as you had it should do the same I guess
public void SetColor(Color newColor)
{
renderer.material.SetColor("_TintColor", newColor);
}
I personally find the before metnioned one easier. But the second one gives you more control since you can e.g. also change the emitColor which is not possible on another way.
Now when you instantiate your bullets make sure you first instantiate the prefab and than change the color using the component above:
public GameObject bulletPrefab;
public Color myColor;
public GameObject Hand;
private void Shoot()
{
// There are various ways for Instantiate e.g. providing start position and rotation or also the parent object
// Since it shall be fired from the player I'll assume here there is a "Hand" object from which you want to fire
// and that you want to add the bullet directly to the hierarchy without a parrent
GameObject newBullet = Instantiate(bulletPrefab, Hand.position, Hand.rotation);
// Now we get the color change component
ColorChanger colorChanger = newBullet.GetComponent<ColorChanger>();
// And set the color
colorChanger.SetColor(myColor);
}
Note:
In case this is a multiplayer game the bullets should be Instantiated only on the Server so you'ld have to first let the server know which color the bullet shall be and than also provide this information back to all players after you Network.Spawn
them.
Upvotes: 2
Reputation: 4059
You don't need to keep updating the property block. Once is enough. You've also got to make sure that the material allows instancing, and that the shader supports instancing of "_Color
". On the standard and sprite materials, you'll find that at the bottom of the inspector window for the material.
So, you can remove the test in Update
, and modify the ColorSet
method. In fact, on a Bullet type object, I'd be inclined to make it as lean as possible (ideally move all functionality to a central bullet manager, but that's off topic).
public void ColorSet(Color color)
{
renderer.GetPropertyBlock(matBlock);
matBlock.SetColor("_Color", color);
renderer.SetPropertyBlock(matBlock);
}
If this isn't helping, are you grabbing a bullet from a pool, which already has colours set on the objects? Is it simply a case that you're not setting the correct colour on already instantiated objects?
I'm also questioning why renderer.material
didn't work (or if you ever tried it?). But, it's my understanding/guess that setting a new material actually breaks instancing. So you might actually get better performance from setting the property block anyway.
Upvotes: 1