下面是一个专门针对单个预制体的材质批量修改工具。这个脚本允许您选择一个预制体,然后批量替换其中所有渲染器组件使用的材质。
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
public class PrefabMaterialReplacer : EditorWindow
{
private GameObject targetPrefab;
private Material oldMaterial;
private Material newMaterial;
private Vector2 scrollPosition;
private List<Renderer> renderersInPrefab = new List<Renderer>();
private bool showPreview = true;
[MenuItem("Tools/预制体材质替换工具")]
public static void ShowWindow()
{
GetWindow<PrefabMaterialReplacer>("预制体材质替换工具");
}
private void OnGUI()
{
GUILayout.Label("预制体材质批量替换工具", EditorStyles.boldLabel);
EditorGUILayout.Space();
// 选择预制体
EditorGUI.BeginChangeCheck();
targetPrefab = (GameObject)EditorGUILayout.ObjectField("目标预制体", targetPrefab, typeof(GameObject), false);
if (EditorGUI.EndChangeCheck() && targetPrefab != null)
{
AnalyzePrefab();
}
// 选择材质
oldMaterial = (Material)EditorGUILayout.ObjectField("原材质", oldMaterial, typeof(Material), false);
newMaterial = (Material)EditorGUILayout.ObjectField("新材质", newMaterial, typeof(Material), false);
EditorGUILayout.Space();
// 显示预制体中的渲染器信息
if (renderersInPrefab.Count > 0)
{
showPreview = EditorGUILayout.Foldout(showPreview, "预制体渲染器预览");
if (showPreview)
{
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(150));
foreach (var renderer in renderersInPrefab)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.ObjectField(renderer, typeof(Renderer), true);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.LabelField($"找到 {renderersInPrefab.Count} 个渲染器组件", EditorStyles.miniLabel);
}
EditorGUILayout.Space();
// 替换按钮
if (GUILayout.Button("替换材质", GUILayout.Height(30)))
{
if (targetPrefab != null && oldMaterial != null && newMaterial != null)
{
ReplaceMaterials();
}
else
{
EditorUtility.DisplayDialog("错误", "请确保选择了预制体、原材质和新材质", "确定");
}
}
// 快速操作按钮
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("选择预制体中的所有材质"))
{
SelectAllMaterialsInPrefab();
}
if (GUILayout.Button("预览替换效果"))
{
PreviewMaterialReplacement();
}
EditorGUILayout.EndHorizontal();
}
// 分析预制体中的渲染器组件
private void AnalyzePrefab()
{
renderersInPrefab.Clear();
// 检查是否是预制体
if (PrefabUtility.GetPrefabAssetType(targetPrefab) == PrefabAssetType.NotAPrefab)
{
EditorUtility.DisplayDialog("错误", "选择的对象不是预制体", "确定");
targetPrefab = null;
return;
}
// 获取预制体中的所有渲染器
Renderer[] allRenderers = targetPrefab.GetComponentsInChildren<Renderer>(true);
renderersInPrefab.AddRange(allRenderers);
Debug.Log($"在预制体 '{targetPrefab.name}' 中找到 {renderersInPrefab.Count} 个渲染器组件");
}
// 替换材质
private void ReplaceMaterials()
{
if (targetPrefab == null) return;
// 获取预制体路径
string prefabPath = AssetDatabase.GetAssetPath(targetPrefab);
// 加载预制体内容
GameObject prefabInstance = PrefabUtility.LoadPrefabContents(prefabPath);
int replacementCount = 0;
// 查找所有渲染器组件
Renderer[] allRenderers = prefabInstance.GetComponentsInChildren<Renderer>(true);
foreach (Renderer renderer in allRenderers)
{
// 检查共享材质
Material[] sharedMaterials = renderer.sharedMaterials;
bool materialsChanged = false;
for (int i = 0; i < sharedMaterials.Length; i++)
{
if (sharedMaterials[i] == oldMaterial)
{
sharedMaterials[i] = newMaterial;
materialsChanged = true;
replacementCount++;
}
}
// 如果材质有变化,应用更改
if (materialsChanged)
{
renderer.sharedMaterials = sharedMaterials;
}
// 检查材质属性(针对某些特殊渲染器)
Material materialProperty = renderer.sharedMaterial;
if (materialProperty == oldMaterial)
{
renderer.sharedMaterial = newMaterial;
replacementCount++;
}
}
// 保存预制体
PrefabUtility.SaveAsPrefabAsset(prefabInstance, prefabPath);
PrefabUtility.UnloadPrefabContents(prefabInstance);
// 刷新资源数据库
AssetDatabase.Refresh();
Debug.Log($"在预制体 '{targetPrefab.name}' 中成功替换了 {replacementCount} 个材质引用");
EditorUtility.DisplayDialog("完成", $"成功替换了 {replacementCount} 个材质引用", "确定");
}
// 选择预制体中的所有材质
private void SelectAllMaterialsInPrefab()
{
if (targetPrefab == null) return;
List<Material> materials = new List<Material>();
// 获取预制体路径
string prefabPath = AssetDatabase.GetAssetPath(targetPrefab);
// 加载预制体内容
GameObject prefabInstance = PrefabUtility.LoadPrefabContents(prefabPath);
// 查找所有渲染器组件
Renderer[] allRenderers = prefabInstance.GetComponentsInChildren<Renderer>(true);
foreach (Renderer renderer in allRenderers)
{
foreach (Material material in renderer.sharedMaterials)
{
if (material != null && !materials.Contains(material))
{
materials.Add(material);
}
}
}
// 卸载预制体
PrefabUtility.UnloadPrefabContents(prefabInstance);
// 选择所有材质
Selection.objects = materials.ToArray();
Debug.Log($"在预制体 '{targetPrefab.name}' 中找到 {materials.Count} 个独特材质");
}
// 预览材质替换效果
private void PreviewMaterialReplacement()
{
if (targetPrefab == null || newMaterial == null) return;
// 在场景中创建预览实例
GameObject previewInstance = (GameObject)PrefabUtility.InstantiatePrefab(targetPrefab);
previewInstance.name = $"{targetPrefab.name}_Preview";
// 查找所有渲染器组件并替换材质
Renderer[] allRenderers = previewInstance.GetComponentsInChildren<Renderer>(true);
foreach (Renderer renderer in allRenderers)
{
// 检查共享材质
Material[] sharedMaterials = renderer.sharedMaterials;
for (int i = 0; i < sharedMaterials.Length; i++)
{
if (oldMaterial == null || sharedMaterials[i] == oldMaterial)
{
sharedMaterials[i] = newMaterial;
}
}
// 应用更改
renderer.sharedMaterials = sharedMaterials;
}
// 聚焦到预览对象
Selection.activeGameObject = previewInstance;
SceneView.lastActiveSceneView.FrameSelected();
Debug.Log("已创建材质替换预览,这不是永久修改,只是场景中的临时实例");
}
}
使用说明
打开工具窗口:
通过菜单栏 "Tools/预制体材质替换工具" 打开窗口
选择目标预制体:
将Project窗口中的预制体拖拽到"目标预制体"字段
选择材质:
在"原材质"字段选择要替换的材质
在"新材质"字段选择替换后的新材质
预览和分析:
工具会自动分析预制体中的渲染器组件
点击"预览替换效果"可以在场景中创建临时预览
点击"选择预制体中的所有材质"可以查看预制体使用的所有材质
执行替换:
点击"替换材质"按钮执行批量替换操作
系统会显示替换了多少个材质引用
功能特点
精确替换:只替换指定的材质,不影响其他材质
预览功能:可以在不修改原预制体的情况下预览替换效果
材质分析:可以查看预制体中使用的所有材质
安全操作:使用PrefabUtility确保操作的安全性
详细日志:操作完成后会显示详细的日志信息
注意事项
备份预制体:在进行批量修改前,建议备份您的预制体
版本控制:如果使用版本控制,确保在执行操作前提交更改
材质引用:替换材质后,所有使用该预制体的场景中的实例也会自动更新
着色器兼容性:确保新材质使用与旧材质兼容的着色器,否则可能需要调整材质属性
这个工具应该能帮助您快速批量修改单个预制体中的材质,而无需手动遍历每个子对象。