前言
在用Unity遊戲開發的時候,可能有時候我們需要修改模型的縮放比例(Scale Factor)或者從FBX導出材質球,或者需要修改Shader 等等操作時,如果模型少,我們可以一個一個的修改,但是如果大量的模型都需要修改的話,那我們一個一個的單獨去修改就非常的耗時耗力。
是以,為了偷懶,我就去簡單學習了下Unity Editor的編寫。
然後就簡單的編寫了一個Unity 編輯器擴充,支援一鍵導出材質、設定模型縮放比例、修改Shader等功能。
功能展示
該擴充大概長這個樣子,界面比較簡單:

點選 設定模型縮放比例 、導出材質球、設定Shader 分别代表修改以下紅色的地方:
具體操作如下:
代碼實作:
該腳本需要放在Editor檔案夾下。
// Create By 長生但酒狂
// Create Time 2020.4.24
using System.IO;
using UnityEngine;
using UnityEditor;
// ---------------------------【一鍵從FBX導出材質和設定shader】---------------------------
public class ExtractMaterials : EditorWindow
{
// 目前選擇路徑
public string modelPath = "Assets";
public Rect modelRect;
// shader
int selectShaderIndex = 0;
string[] shaderNameLists;
// Scale Factor
int scaleFactor = 1;
[MenuItem("長生但酒狂的插件/導出材質和修改Shader")]
public static void showWindow()
{
EditorWindow.CreateInstance<ExtractMaterials>().Show();
}
private void Awake()
{
// 擷取所有Shader
ShaderInfo[] shaderLists = ShaderUtil.GetAllShaderInfo();
shaderNameLists = new string[shaderLists.Length];
for (int i = 0; i < shaderLists.Length; i++)
{
shaderNameLists[i] = shaderLists[i].name;
if (shaderNameLists[i] == "LayaAir3D/Mesh/PBR(Standard)")
{
selectShaderIndex = i;
}
}
}
public void OnGUI()
{
// 模型路徑
EditorGUILayout.Space();
EditorGUILayout.LabelField("模型路徑 (滑鼠拖拽檔案夾到這裡)");
EditorGUILayout.Space();
GUI.SetNextControlName("input1");//設定下一個控件的名字
modelRect = EditorGUILayout.GetControlRect();
modelPath = EditorGUI.TextField(modelRect, modelPath);
EditorGUILayout.Space();
EditorGUILayout.Space();
// Shader下拉清單
EditorGUILayout.LabelField("目前選中Shader:");
EditorGUILayout.Space();
selectShaderIndex = EditorGUILayout.Popup(selectShaderIndex, shaderNameLists);
EditorGUILayout.Space();
EditorGUILayout.Space();
// scale Factor
EditorGUILayout.LabelField("模型縮放設定");
scaleFactor = EditorGUILayout.IntField("Scale Factor:", scaleFactor);
EditorGUILayout.Space();
EditorGUILayout.Space();
// 檔案拖拽
DragFolder();
// 設定模型縮放比例
if (GUILayout.Button("設定模型縮放比例"))
{
ForEachModels(true);
}
EditorGUILayout.Space();
// 導出材質
if (GUILayout.Button("導出材質球"))
{
ForEachModels(false);
}
EditorGUILayout.Space();
// 設定Shader
if (GUILayout.Button("設定Shader"))
{
ForEachMaterials();
}
}
// 滑鼠拖拽檔案
void DragFolder()
{
//滑鼠位于目前視窗
if (mouseOverWindow == this)
{
//拖入視窗未松開滑鼠
if (Event.current.type == EventType.DragUpdated)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;//改變滑鼠外觀
// 判斷區域
if (modelRect.Contains(Event.current.mousePosition))
GUI.FocusControl("input1");
}
//拖入視窗并松開滑鼠
else if (Event.current.type == EventType.DragExited)
{
string dragPath = string.Join("", DragAndDrop.paths);
// 判斷區域
if (modelRect.Contains(Event.current.mousePosition))
this.modelPath = dragPath;
// 取消焦點(不然GUI不會重新整理)
GUI.FocusControl(null);
}
}
}
// 周遊模型
void ForEachModels(bool isSetModelSetup)
{
string[] allPath = AssetDatabase.FindAssets("t:GameObject", new string[] { modelPath });
// Debug.Log("-- allPath: " + allPath.Length);
for (int i = 0, len = allPath.Length; i < len; i++)
{
string filePath = AssetDatabase.GUIDToAssetPath(allPath[i]);
// 設定模型
if (isSetModelSetup)
setModelSetup(filePath);
else
ExtractMaterialsFromFBX(filePath);
}
// 如果選取的是FBX模型檔案
if (allPath.Length == 0)
{
if (Path.GetExtension(modelPath) == ".fbx")
{
// 設定模型
if (isSetModelSetup)
setModelSetup(modelPath);
else
ExtractMaterialsFromFBX(modelPath);
}
else
{
Debug.LogError("目前選擇目錄未找到FBX檔案: " + this.modelPath);
}
}
}
// 從fbx模型導出材質球
public void ExtractMaterialsFromFBX(string assetPath)
{
// 材質目錄
string materialFolder = Path.GetDirectoryName(assetPath) + "/Material";
// 如果不存在該檔案夾則建立一個新的
if (!AssetDatabase.IsValidFolder(materialFolder))
AssetDatabase.CreateFolder(Path.GetDirectoryName(assetPath), "Material");
// 擷取 assetPath 下所有資源。
Object[] assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
foreach (Object item in assets)
{
if (item.GetType() == typeof(Material))
{
string path = System.IO.Path.Combine(materialFolder, item.name) + ".mat";
// 為資源建立一個新的唯一路徑。
path = AssetDatabase.GenerateUniqueAssetPath(path);
// 通過在導入資源(例如,FBX 檔案)中提取外部資源,在對象(例如,材質)中建立此資源。
string value = AssetDatabase.ExtractAsset(item, path);
// 成功提取( 如果 Unity 已成功提取資源,則傳回一個空字元串)
if (string.IsNullOrEmpty(value))
{
AssetDatabase.WriteImportSettingsIfDirty(assetPath);
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
}
}
Debug.Log(Path.GetFileName(assetPath) + " 的 Material 導出成功!!");
}
// 修改模型設定
void setModelSetup(string path)
{
ModelImporter importer = (ModelImporter)ModelImporter.GetAtPath(path);
if (importer)
{
importer.globalScale = scaleFactor;
importer.SaveAndReimport();
Debug.Log("Scale Factor 設定成功: " + Path.GetFileName(path));
}
}
// 周遊材質
void ForEachMaterials()
{
string[] allPath = AssetDatabase.FindAssets("t:Material", new string[] { modelPath });
if (allPath.Length == 0)
{
if (System.IO.Path.GetExtension(modelPath) == ".mat")
setMaterialShader(modelPath);
else
Debug.LogError("目前目錄未找到Material: " + modelPath);
return;
}
for (int i = 0, len = allPath.Length; i < len; i++)
{
string filePath = AssetDatabase.GUIDToAssetPath(allPath[i]);
setMaterialShader(filePath);
}
Debug.Log("Material Shader 設定完成, 一共: " + allPath.Length + "個");
}
void setMaterialShader(string path)
{
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
Shader mShader = Shader.Find(shaderNameLists[selectShaderIndex]);
if (mShader == null)
{
Debug.LogError("設定shader失敗, 未找到該shader: " + shaderNameLists[selectShaderIndex]);
return;
}
mat.shader = mShader;
// AssetDatabase.Refresh();
}
}
最後
有興趣的小夥伴可以來我GitHub逛逛,裡面記錄了一些我在學習Unity過程中的一些總結和Demo!喜歡的話可以給個Star,謝謝!