- Home /
Check if Material of a gameObject == a material variable (instance)?
I want to have a function basically like so...
public Material m;
private matEnum getWhichMat(GameObject GO)
{
if(GO.material is the same as m)
return matEnum.firstMat;
else if... (check all mats. I have 4 or 5 total)
}
...however I can't figure out how :/ Right now I'm comparing the COLORS of both materials, but I want to be able to compare the materials themselves and the function work. I've tried both == and .equals with various things like MeshRenderer, material, etc
EDIT: Tons of extra details:
I have 4 materials in a folder (which happen to be the same except for different colors, but the whole point of fixing this is so I can use different materials). My script has 4 public Material variables, which I then dragged the materials in my project view to the spots on the inspector.
Then in the script, I have this function to check and see if the gameobject sent in as a parameter is using one of those 4 materials, and if so, which one (just a long if-else chain)
I have tried 1) go.renderer.material == m, 2) go.renderer.material.Equals(m), 3) go.renderer.sharedMarerial == m, and 4) go.renderer.sharedMaterial.Equals(m), and none of them worked
I also added debug.log lines to make sure that nothing is counted as null, and all 5 are showing up as values (not null). However the game object's material says the name of the material it's using followed by "(Instance)" (so "marker_hex_green (Instance)")
EDIT 2:
Okay here is my full work as I have it:
First, context. My game is a tile-based game where units move onto different nodes. Nodes have 4 colors: white (default, should never be shown), green (only used when buying a new unit), blue (used to indicate movement), and orange (used to indicate attacking).
In my script, I have these variables, and the script modeled after your most recent post/edit:
public enum hexMats { defaultt = 0, landing = 1, move = 2, attack = 3 };
public Material defaultHexMat; //dragged the white material to this in the inspector
public Material landingHexMat; //dragged the green material to this in the inspector
public Material moveHexMat; //dragged the blue material to this in the inspector
public Material attackHexMat; //dragged the orange material to this in the inspector
/// <summary> Returns the (scriptName).hexMats enum that the node's material currently is. </summary>
public hexMats getNodeMatEnum(TileNode n)
{
if (n.gameObject.GetComponent<MeshRenderer>())
{
if (n.gameObject.renderer.sharedMaterial == defaultHexMat)
return hexMats.defaultt;
else if (n.gameObject.renderer.sharedMaterial == landingHexMat)
return hexMats.landing;
else if (n.gameObject.renderer.sharedMaterial == moveHexMat)
return hexMats.move;
else if (n.gameObject.renderer.sharedMaterial == attackHexMat)
return hexMats.attack;
else
{
Debug.Log("Found the mesh component, but did not compare to any of the 4 mats. Returning default");
Debug.Log("your material is " + n.gameObject.renderer.sharedMaterial);
return hexMats.defaultt;
}
}
else
{
Debug.Log("No Mesh Component Found! Returning default");
return hexMats.defaultt;
}
}
RESULTS: I tried this on a green hex, and my console showed that it DID fine the mesh component, but not any of the 4 mats. It showed "your material is marker-hex-green (Instance) (UnityEngine.Material)". Note that marker-hex-green is the material i dragged to landingHexMat in the inspector. (it's actually two underscores, not hyphens, but the forums have weird formating for underscores..)
Answer by Eric5h5 · Sep 07, 2014 at 01:05 AM
Use if (go.renderer.sharedMaterial == m)
. Accessing renderer.material creates an instance so it's not useful for this.
Answer by hangemhigh · Sep 07, 2014 at 01:03 AM
You should explain more of what you are trying to do. For me, it seems like you are trying to check if GO object has the same material with M material? To compare a material to a game object, you must use gameobjectName.renderer.material. Code is below.
public Material m;
bool hasThesameMaterial(GameObject GO){
if(GO.renderer.material==m){ //checks if they are the same material
return true; //returns true if the same material.
}else{
return false; //returns false if different material
}
}
To call this function, you do
GameObject myobject; //the object you want to check if the material is the same with 'm'
if(hasThesameMaterial(myobject)){
//it has the same material
}else{
//it DOESNT have the same material
}
EDIT 1:
Then in the script, I have this function to check and see if the gameobject sent in as a parameter is using one of those 4 materials, and if so, which one (just a long if-else chain)
public Material m1;
public Material m2;
public Material m3;
public Material m4;
string hasAnyOfTheMaterials(GameObject GO){
if(GO.GetComponent<MeshRenderer>()){ //Get Mesh Component
if(GO.renderer.sharedMaterial==m1){ //checks if it has material 1 material
return "m1";
}
else if(GO.renderer.sharedMaterial==m2){ //checks if it has material 2 material
return "m2";
}
else if(GO.renderer.sharedMaterial==m3){ //checks if it has material 3 material
return "m3";
}
else if(GO.renderer.sharedMaterial==m4){ //checks if it has material 4 material
return "m4";
}
else{
return "none"; //it has none of the materials
}
}
}else{
Debug.Log("No Mesh Component Found");
}
To call the function
GameObject myobject; //the object you want to check if it has the same material.
if(hasAnyOfTheMaterials(myobject)=="none"){
Debug.Log("it has none of these materials");
}
You can also do this.
string currentMaterial = hasAnyOfTheMaterials(myobject);
Debug.Log("Game object's material is "+currentMaterial);
What if you want to know the ACTUAL name of the material. The name of given to the material?
Replace
return "m1";, return "m2";, return "m3";, and return "m4";
with
return GO.renderer.sharedMaterial.name;
It will return the exact name the material is using. Leave "none" alone so that you can use it to determine which if the material it is using is not one of those 4 materials. If you want it to return the name of the material when it is none of those 4 materials, then replace
return "none";
with
return GO.renderer.sharedMaterial.name;
You can also replace
return "none";
with
return null;
so now you can do
if(!hasAnyOfTheMaterials(myobject)){
//It doesn't have any of the materials
}else if(hasAnyOfTheMaterials(myobject)=="m1"){
//It has Material 1
}.................... so on
NOTE: When you use renderer.material, it creates a new instance. With renderer.sharedMaterial, it accesses the main material. Even when you read a material with renderer.material, it creates a new instance. When you modify a material's color, texture or other of its properties with renderer.material, it creates a new instance. So use renderer.sharedMaterial when you want to change a material, read or compare material value. I forgot to say that in the beginning.
And the most important change I made here is get the MeshRenderer Component "GO.GetComponent()". That might help. If not, post your code, post what you have now and I will replicate the problem on my side.
EDIT 2:
It showed "your material is marker-hex-green (Instance) (UnityEngine.Material)". Note that marker-hex-green is the material i dragged to landingHexMat in the inspector. (it's actually two underscores, not hyphens, but the forums have weird formatting for underscores..)
You are correct. Unity doesn't like the underscores and changes it to hyphens. It is not suppose to have 'Instance' in the name. Like I said before, Instance means that that you used something like
n.gameObject.renderer.Material....
instead of
n.gameObject.renderer.sharedMaterial....
in your code. Check carefully in all your C# code files.
UnityEngine.Material will normally be there if you use gameObject.renderer.sharedMaterial.
To remove (UnityEngine.Material) from the name in Debug.Log(), use
Debug.Log("your material is " + n.gameObject.renderer.sharedMaterial.**name**);
instead of
Debug.Log("your material is " + n.gameObject.renderer.sharedMaterial);
The variable "name" will actually return the name of the object's material.
You need to change your naming Convention
From 50 Tips For Working with unity Use Pascal case, like this: ComplicatedVerySpecificObject. Do not use spaces, underscores, or hyphens
I think that this applies to File Naming too as Unity replaces underscores with hyphens.
So your "marker-hex-green" variable should be named something like markerHexGreen or markerHexGreenMat. Do the same for other Materials. The Mat reminds you that this is a Material.
After doing these above, run it and see if Instance is still showing. If yes, then you must chose one of the following temporary ways to fix it for now and look for a better way after.
Method 1: Create a public GameObject then pass the GameObject to the function. Remove
gameObject
from
n.gameObject.renderer.sharedMaterial == attackHexMat
Below is what you are suppose to have. Make sure to assign an object to the GameObject through the Editor.
public GameObject myColorObject;
public hexMats getNodeMatEnum(GameObject n)
{
if (n.GetComponent<MeshRenderer>())
{
if (n.renderer.sharedMaterial == defaultHexMat)
return hexMats.defaultt;
else if (n.renderer.sharedMaterial == landingHexMat)
return hexMats.landing;
else if (n.renderer.sharedMaterial == moveHexMat)
return hexMats.move;
else if (n.renderer.sharedMaterial == attackHexMat)
return hexMats.attack;
else
{
Debug.Log("Found the mesh component, but did not compare to any of the 4 mats. Returning default");
Debug.Log("your material is " + n.gameObject.renderer.sharedMaterial);
return hexMats.defaultt;
}
}
else
{
Debug.Log("No Mesh Component Found! Returning default");
return hexMats.defaultt;
}
}
The "Instance" word should now disappear. If true, then the program should work. If not, then use Method 2.
Method 2: Revert back to the code you had before you performed Method 1. The "Instance" in the material name is probably the problem. Since the word "(Instance)" appears next to the material name but not before it. You can use string manipulating to counter it. Below is your code.
string whatToUse = "";
public enum hexMats { defaultt = 0, landing = 1, move = 2, attack = 3 };
public Material defaultHexMat; //dragged the white material to this in the inspector
public Material landingHexMat; //dragged the green material to this in the inspector
public Material moveHexMat; //dragged the blue material to this in the inspector
public Material attackHexMat; //dragged the orange material to this in the inspector
public hexMats getNodeMatEnum(TileNode n)
{
if (n.gameObject.GetComponent<MeshRenderer>())
{
//Call Function and See if it contains the word '(Instance)'
if(containsInstance(n.gameObject.renderer.sharedMaterial.ToString())){
//It has '(Instance in the name)'
//Let's modify Our name
whatToUse = " (Instance) (UnityEngine.Material)";
}else{
whatToUse = " (UnityEngine.Material)";
}
if (n.gameObject.renderer.sharedMaterial.ToString() == defaultHexMat.name+whatToUse)
return hexMats.defaultt;
else if (n.gameObject.renderer.sharedMaterial.ToString() == landingHexMat.name+whatToUse)
return hexMats.landing;
else if (n.gameObject.renderer.sharedMaterial.ToString() == moveHexMat.name+whatToUse)
return hexMats.move;
else if (n.gameObject.renderer.sharedMaterial.ToString() == attackHexMat.name+whatToUse)
return hexMats.attack;
else
{
Debug.Log("Found the mesh component, but did not compare to any of the 4 mats. Returning default");
Debug.Log("your material is " + n.gameObject.renderer.sharedMaterial.name);
return hexMats.defaultt;
}
}
else
{
Debug.Log("No Mesh Component Found! Returning default");
return hexMats.defaultt;
}
}
//Important Function
//Checks if the word contains an instance or not
bool containsInstance(string materialName){
if(materialName.Contains(" (Instance) ")){
return true;
}else{
return false;
}
}
That's it. It should work weather there is an Instance or Not.
Logic Behind Method 2:
Two possible names returned by Unity during run time
marker-hex-blue (UnityEngine.Material) = GO.renderer.sharedMaterial
marker-hex-blue (Instance) (UnityEngine.Material) = GO.renderer.sharedMaterial
We decide which one of these below to use for comparing depending on the value in the code above.
marker-hex-blue (UnityEngine.Material) = m1
marker-hex-blue = m1.name
if go.renderer.sharedMaterial contains Instance
add space at the end then add '(Instance)' at the end of each all material variable name followed by space then '(UnityEngine.Material)'
else
only add space followed by '(UnityEngine.Material)' at the end of all material variable name.
//Remember we are not really modifying the name, we are concatenating it temporary and then comparing it.
To really modify the name of the variable, we would do m1.name = "modified";
But we never should do that and that's not what we are doing. It should now work.
NOTE: I added a Zipped File I used for debugging and the fixing the '(Instance)' problem. You can download to see a working example.
Phew. That took long.
And for this to work, you must have material assigned to $$anonymous$$aterial m from the editor or through code so that it wont compare null to GO.renderer.material....
I have 4 materials in a folder (which happen to be the same except for different colors, but the whole point of fixing this is so I can use different materials). $$anonymous$$y script has 4 public $$anonymous$$aterial variables, which I then dragged the materials in my project view to the spots on the inspector.
Then in the script, I have this function to check and see if the gameobject sent in as a parameter is using one of those 4 materials, and if so, which one (just a long if-else chain)
I have tried 1) go.renderer.material == m, 2) go.renderer.material.Equals(m), 3) go.renderer.shared$$anonymous$$arerial == m, and 4) go.renderer.shared$$anonymous$$aterial.Equals(m), and none of them worked
I also added debug.log lines to make sure that nothing is counted as null, and all 5 are showing up as values (not null). However the game object's material says the name of the material it's using followed by "(Instance)" (so "marker_hex_green (Instance)")
Thank you very very much for your help. I'm still having the issue, so I updated my post with all my work. Start from Edit 2