- Home /
Replacing materials in sharedMaterials in an editor script.
I have a problem similar to http://answers.unity3d.com/questions/36659/setting-sharedmaterial-only-setting-for-one-object but the proposed solution does not satisfy my needs. I have a lot of prefabs imported into my project with multiple materials per mesh.
When I import an FBX into my project, UNity creates a set of materials for it. The thing is, there are a lot of materials on different objects that should be the same. The good example is glass material on buildings: I have a lot of different buildings with different wallsand roof materials, etc. but I want them all to use the same glass material, so I could set it up once, and every time I change it, all the windows would change.
Thing is there are tons of cross used materials (around 100) like that and a lot of prefabs (like 200 with 5-10 materials per prefab), so setting materials list manually would be a huge task.
I wrought an editor script to search for materials with certain names in objects and replace them with materials form a preset. It's working perfectly with an object, but never changes a prefab. I cannot use CopyPropertiesFromMaterial because it won't make different objects use the same material, just copy settings from my preset materials to another. Here's the code I use:
Material[] tempList = recipient.renderer.sharedMaterials; int changesDone = 0; for(int i = 0; i < tempList.Length; i++) { string replacingName = ParseMaterialName(tempList[i].name); if(replacingName == "") { ..... continue; }
if(nativeMaterials.ContainsKey(replacingName))
{
tempList[i] = nativeMaterials[replacingName];
changesDone++;
......
continue;
}
if(SharedMaterials.ContainsKey(replacingName))
{
tempList[i] = SharedMaterials[replacingName];
changesDone++;
.....
continue;
}
.....
}
.....
if(changesDone > 0)
{
recipient.renderer.sharedMaterials = tempList;
return true;
}
......
EditorUtility.SetDirty(recipient);
AssetDatabase.SaveAssets();
What am I doing wrong? Is it possible to somehow change the whole materials list of a prefab and save the changes to asset database?
Answer by Shiolle · May 05, 2011 at 01:01 PM
I found a solution. Instead of
EditorUtility.SetDirty(recipient);
AssetDatabase.SaveAssets();
I should have used
target.name = obj.name;
UnityEngine.Object newPref = EditorUtility.CreateEmptyPrefab("Assets" + path + obj.name + ".prefab");
EditorUtility.ReplacePrefab(target, newPref);
AssetDatabase.Refresh();
The answer was as usual in docs all along: http://unity3d.com/support/documentation/ScriptReference/EditorUtility.ReplacePrefab.html
I have to note two peculiarities though:
- I have to refresh database after every object. Placing AssetDatabase.Refresh(); after all the objects were modified somehow failed to update prefabs for unknown reason.
- target.name = obj.name; is there because target (the object instantiated from initial prefab and being modified) has a "(clone)" part attached to it's name by default.
Answer by Bunny83 · May 05, 2011 at 12:11 PM
The problem here is that model-prefabs can't be saved because they are generated with the ModelImporter. There are different types of prefabs in Unity. Take a look at PrefabType.
Furthermore Unity is just doing what you told it ;). The modelimporter have a setting which materials it should create.
If you have many models that are updated very often you should create an AssetPostProcessor. Take a look at .OnPreprocessModel, .OnPostprocessModel and .OnAssignMaterialModel.
edit
To modify an existing prefab i always go this way:
- Get a prefab reference with prefab = AssetDatabase.LoadAssetAtPath.
- Instantiate the prefab using obj = EditorUtility.InstantiatePrefab.
- Apply your changes to the instantiated object.
- Save the changes to the prefab with EditorUtility.ReplacePrefab(obj,prefab).
- Use either AssetDatabase.ImportAsset or AssetDatabase.Refresh to update the asset.
I've discovered when you use CreateEmptyPrefab
you might loose the references to this prefab because it's a new one. Just use the original prefab for ReplacePrefab it exchange the "content" and keep the prefab itself.
I wasn't talking about model-prefab, but regular prefabs (GetPrefabType returns Prefab). I was only talking about modifying prefabs made upon those models. Thanks for hinting about post processing models though.
Well, you said "when you import a fbx model..." so i thought you talk about a $$anonymous$$odelPrefab
;). I've added some info on changing prefabs.
Thanks, figured out that part already (see my answer). Two things I noticed. LoadAssetAtPath as well as Load$$anonymous$$ainAssetAtPath always return 0 assets. 1. LoadAllAssetsAtPath works fine, although I need to filter through them in order to find the type I need (GameObject). 2. ReplacePrefab somehow does not work with existing prefab. I had to create empty prefab at the same location as the old one and place ReplacePrefab onto new one.
That's strange. It works fine for me. Are you sure you used EditorUtility.InstantiatePrefab to instantiate the object? I can remember that if the prefab has changed you have to "refresh" your prefab reference. I use a script with the exact order i've posted above and everything works.
Your answer
Follow this Question
Related Questions
Connecting oracle with unity tutorial, Steps or a way? 1 Answer
How to save changes of fbx subassets ? 0 Answers
CreateAssetMenu below builtin/default create asset menu items 1 Answer
How do I make copies of my Font with an editor script? 2 Answers
Adding an element to List a .asset file of ScriptableObject 0 Answers