整理后处理脚本

main
zhangaotian 2025-04-25 15:07:22 +08:00
parent dd1e696010
commit 4ede122621
11 changed files with 1336 additions and 1161 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
/// <summary>
/// 地面处理工具类
/// </summary>
public static class GroundUtils
{
/// <summary>
/// 修改地面贴图及材质的路径 用于打包
/// </summary>
public static void ChangeMapTextureAndMatPath(GameObject ground, string targetDir, string prefix,
List<string> groundRootNames, Transform rootTransform, Transform unpackedRoot)
{
if (ground == null)
{
Debug.LogError("找不到地面对象,无法修改地面贴图和材质路径");
return;
}
// 获取地面的渲染器
var renderer = ground.GetComponent<MeshRenderer>();
if (renderer == null || renderer.sharedMaterial == null)
{
Debug.LogError("地面对象缺少渲染器或材质");
return;
}
// 确保目标目录存在
EnsureDirectoryExists(targetDir);
// 处理材质和贴图
Dictionary<Material, Material> matMapping = new Dictionary<Material, Material>();
Dictionary<Texture, Texture> texMapping = new Dictionary<Texture, Texture>();
// 处理主地面材质
MaterialUtils.ProcessMaterialAndTexture(renderer.sharedMaterial, targetDir, prefix, matMapping, texMapping);
// 处理groundRoot下的所有材质
foreach (var rootName in groundRootNames)
{
var rootObj = SceneUtils.FindChildRecursive(rootTransform, rootName);
if (rootObj != null)
{
var renderers = rootObj.GetComponentsInChildren<Renderer>(true);
foreach (var r in renderers)
{
foreach (var mat in r.sharedMaterials)
{
if (mat != null)
{
MaterialUtils.ProcessMaterialAndTexture(mat, targetDir, prefix, matMapping, texMapping);
}
}
}
}
}
// 更新场景中地面对象的材质引用
MaterialUtils.UpdateGroundMaterialReferences(ground, matMapping);
// 检查其他地面节点
foreach (var rootName in groundRootNames)
{
var rootObj = SceneUtils.FindChildRecursive(rootTransform, rootName);
if (rootObj != null)
{
var renderers = rootObj.GetComponentsInChildren<Renderer>(true);
foreach (var r in renderers)
{
MaterialUtils.UpdateRendererMaterialReferences(r, matMapping);
}
}
}
// 解除预制体对原始贴图的引用
if (unpackedRoot != null)
{
// 如果Art节点是从预制体解包的确保彻底断开所有引用
MaterialUtils.EnsureNoOriginalReferences(unpackedRoot.gameObject);
}
// 在整个场景中查找任何可能仍然引用原始材质或贴图的渲染器
MaterialUtils.FindAndReplaceAllMaterialReferences(matMapping);
// 脏标记整个场景,确保所有改动都被保存
EditorSceneManager.MarkSceneDirty(ground.scene);
// 保存所有更改
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("地面贴图和材质路径修改完成");
}
/// <summary>
/// 确保目录存在
/// </summary>
private static void EnsureDirectoryExists(string dirPath)
{
if (!AssetDatabase.IsValidFolder(dirPath))
{
string parentDir = Path.GetDirectoryName(dirPath);
if (!AssetDatabase.IsValidFolder(parentDir))
{
AssetDatabase.CreateFolder(Path.GetDirectoryName(parentDir), Path.GetFileName(parentDir));
}
AssetDatabase.CreateFolder(parentDir, Path.GetFileName(dirPath));
AssetDatabase.Refresh();
}
}
/// <summary>
/// 切割地面贴图
/// </summary>
public static void SplitGroundTexture(GameObject ground, string groundSplitDir, string prefix, int splitWidth, int splitHeight)
{
if (ground == null)
return;
var renderer = ground.GetComponent<MeshRenderer>();
if (renderer == null)
return;
var mat = renderer.sharedMaterial;
if (mat == null)
return;
var texture = mat.mainTexture as Texture2D;
if (texture == null)
return;
var groundParent = Object.Instantiate(ground, ground.transform.parent, true);
groundParent.name = "Ground";
// 计算原始地面的大小
var originalScale = ground.transform.localScale;
var originalWidth = originalScale.x;
var originalHeight = originalScale.y;
var originPos = -new Vector3(originalWidth / 2f, originalHeight / 2f, 0);
groundParent.transform.localScale = Vector3.one;
// 确保目录存在
EnsureDirectoryExists(groundSplitDir);
var textures = SplitTextureUtil.SplitTexture(texture, splitWidth, splitHeight, groundSplitDir, prefix);
// 计算每个分割块的大小(基于原始地面大小)
var rowCount = textures.GetLength(0);
var columnCount = textures.GetLength(1);
var blockWidth = originalWidth / columnCount;
var blockHeight = originalHeight / rowCount;
for (int row = 0; row < rowCount; row++)
{
for (int column = 0; column < columnCount; column++)
{
var splitTexture = textures[row, column];
if (splitTexture == null)
{
Debug.LogError($"Failed to load split texture at row {row}, column {column}");
continue;
}
var splitGround = Object.Instantiate(ground, groundParent.transform, true);
Object.DestroyImmediate(splitGround.GetComponent<Collider>());
var splitName = $"{prefix}Ground_{row}_{column}";
splitGround.name = splitName;
// 设置固定的quad大小不随贴图尺寸变化
splitGround.transform.localScale = new Vector3(blockWidth, blockHeight, 1);
// 创建并保存材质
var splitMat = Object.Instantiate(mat);
splitMat.mainTexture = splitTexture;
var matPath = $"{groundSplitDir}/{splitName}.mat";
AssetDatabase.CreateAsset(splitMat, matPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// 重新加载保存后的材质
var savedMat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
if (savedMat == null)
{
Debug.LogError($"Failed to load saved material at {matPath}");
continue;
}
var meshRenderer = splitGround.GetComponent<MeshRenderer>();
if (meshRenderer != null)
{
meshRenderer.sharedMaterial = savedMat;
meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
}
// 计算位置使用固定的block大小
splitGround.transform.localPosition =
originPos + new Vector3(column * blockWidth + blockWidth / 2f, row * blockHeight + blockHeight / 2f,
0);
}
}
Object.DestroyImmediate(ground);
Object.DestroyImmediate(groundParent.GetComponent<Renderer>());
Object.DestroyImmediate(groundParent.GetComponent<Collider>());
var boxCollider = groundParent.gameObject.AddComponent<BoxCollider>();
boxCollider.size = new Vector3(200, 200, 1);
boxCollider.center = new Vector3(0, 0, 0.5f);
Object.DestroyImmediate(groundParent.GetComponent<MeshFilter>());
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 065916795ee704f07b75513739284fc4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,250 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEngine.Rendering;
/// <summary>
/// 灯光烘焙工具类
/// </summary>
public static class LightmapUtils
{
/// <summary>
/// 处理参与烘焙的节点
/// </summary>
public static void PreProcessBake(List<string> rootNames, Transform rootTransform, bool castShadow, bool needReplaceShader,
List<Renderer> allBakeRenderers, List<Renderer> needReplaceRenderers)
{
foreach (var rootName in rootNames)
{
var rootObj = SceneUtils.FindChildRecursive(rootTransform, rootName);
if (rootObj != null)
{
SceneUtils.SetStaticFlagsRecursive(rootObj.gameObject, StaticEditorFlags.ContributeGI);
var renderers = rootObj.GetComponentsInChildren<Renderer>(true);
if (renderers != null && renderers.Length > 0)
{
Debug.Log($"从{rootName}收集到 {renderers.Length} 个渲染器用于烘焙");
foreach (var renderer in renderers)
{
Debug.Log($"处理从{rootName}收集到物体{renderer.gameObject.name}的渲染器");
if (renderer.receiveShadows)
{
renderer.shadowCastingMode = castShadow ? ShadowCastingMode.On : ShadowCastingMode.Off;
allBakeRenderers.Add(renderer);
if (needReplaceShader)
needReplaceRenderers.Add(renderer);
}
}
}
else
{
Debug.Log($"从{rootName}收集不到渲染器");
}
}
}
}
/// <summary>
/// 准备灯光:将实时灯光改为烘焙灯光
/// </summary>
public static void PrepareLight(Transform root, List<Light> realTimeLights, List<Light> bakedLights)
{
var lights = root.GetComponentsInChildren<Light>();
foreach (var light in lights)
{
light.transform.position = new Vector3(0, 0, 0);
// 复制灯光对象
var bakeLightObj = Object.Instantiate(light.gameObject, light.transform.parent);
var bakeLight = bakeLightObj.GetComponent<Light>();
// 设置为烘焙模式
bakeLightObj.name = light.name + "_Bake";
bakeLight.lightmapBakeType = LightmapBakeType.Baked;
// 取消实时灯光的static
GameObjectUtility.SetStaticEditorFlags(light.gameObject, 0);
// 设置烘焙灯光的static
GameObjectUtility.SetStaticEditorFlags(bakeLightObj, StaticEditorFlags.ContributeGI);
// 添加到列表
bakedLights.Add(bakeLight);
realTimeLights.Add(light);
}
// 关闭实时灯光
foreach (var realTimeLight in realTimeLights)
{
if (realTimeLight != null)
realTimeLight.gameObject.SetActive(false);
}
// 开启烘焙灯光
foreach (var bakeLight in bakedLights)
{
if (bakeLight != null)
bakeLight.gameObject.SetActive(true);
}
}
/// <summary>
/// 恢复实时灯光
/// </summary>
public static void RevertLight(List<Light> realTimeLights, List<Light> bakedLights)
{
// 开启实时灯光
foreach (var realTimeLight in realTimeLights)
{
if (realTimeLight != null)
realTimeLight.gameObject.SetActive(true);
}
Debug.Log($"销毁{bakedLights.Count}个烘焙灯光");
// 销毁烘焙灯光
foreach (var bakeLight in bakedLights)
{
if (bakeLight != null)
Object.DestroyImmediate(bakeLight.gameObject);
}
}
/// <summary>
/// 执行烘焙
/// </summary>
public static void Bake(Scene activeScene)
{
// 设置烘焙参数并开始烘焙
var lightingSettings = new LightingSettings();
Debug.Log($"烘焙时 场景是{activeScene.name}");
if (Lightmapping.TryGetLightingSettings(out var existingSettings))
{
lightingSettings = existingSettings;
}
else
{
lightingSettings.name = $"{activeScene.name}_LightingSettings";
AssetDatabase.CreateAsset(lightingSettings, $"Assets/{lightingSettings.name}.asset");
Lightmapping.lightingSettings = lightingSettings;
}
// 设置烘焙参数
lightingSettings.mixedBakeMode = MixedLightingMode.Subtractive;
lightingSettings.lightmapper = LightingSettings.Lightmapper.ProgressiveGPU;
lightingSettings.directSampleCount = 1;
lightingSettings.indirectSampleCount = 1;
lightingSettings.environmentSampleCount = 1;
lightingSettings.lightProbeSampleCountMultiplier = 8;
lightingSettings.maxBounces = 1;
lightingSettings.filteringMode = LightingSettings.FilterMode.Auto;
lightingSettings.lightmapResolution = 40;
lightingSettings.lightmapPadding = 2;
lightingSettings.lightmapMaxSize = 1024;
lightingSettings.lightmapCompression = LightmapCompression.HighQuality;
lightingSettings.indirectScale = 0;
Lightmapping.lightingSettings = lightingSettings;
// 强制场景保存以确保设置生效
EditorSceneManager.MarkSceneDirty(activeScene);
EditorSceneManager.SaveOpenScenes();
Debug.Log("开始烘焙灯光...");
Lightmapping.Bake();
Debug.Log("灯光烘焙完成");
}
/// <summary>
/// 移动光照烘焙相关文件到指定目录
/// </summary>
public static void CopyLightmapFiles(Scene scene, string lightmapDirPath)
{
string sceneName = scene.name;
Debug.Log("开始移动光照烘焙文件...");
try
{
// 确保目标目录存在
string lightmapDirAssetPath = SceneUtils.GetAssetPath(lightmapDirPath);
if (!AssetDatabase.IsValidFolder(lightmapDirAssetPath))
{
// 创建父目录结构
string[] folders = lightmapDirAssetPath.Split('/');
string currentPath = folders[0]; // Assets
for (int i = 1; i < folders.Length; i++)
{
string folderName = folders[i];
string parentPath = currentPath;
currentPath = $"{currentPath}/{folderName}";
if (!AssetDatabase.IsValidFolder(currentPath))
{
AssetDatabase.CreateFolder(parentPath, folderName);
}
}
AssetDatabase.Refresh();
}
// 1. 移动LightingSettings.asset文件
string lightingSettingsPath = $"Assets/{sceneName}_LightingSettings.asset";
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(lightingSettingsPath) != null)
{
string destPath = $"{lightmapDirAssetPath}/{sceneName}_LightingSettings.asset";
// 如果目标已存在,先删除
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(destPath) != null)
{
AssetDatabase.DeleteAsset(destPath);
}
AssetDatabase.MoveAsset(lightingSettingsPath, destPath);
Debug.Log($"已移动: {lightingSettingsPath} -> {destPath}");
}
// 2. 移动整个光照目录
string lightmapDirName = sceneName;
string lightmapSourceDir = $"{Path.GetDirectoryName(scene.path)}/{lightmapDirName}";
string lightmapSourceDirAssetPath = SceneUtils.GetAssetPath(lightmapSourceDir);
if (AssetDatabase.IsValidFolder(lightmapSourceDirAssetPath))
{
string destLightmapDir = $"{lightmapDirAssetPath}/{lightmapDirName}";
// 如果目标已存在,先删除
if (AssetDatabase.IsValidFolder(destLightmapDir))
{
AssetDatabase.DeleteAsset(destLightmapDir);
AssetDatabase.Refresh();
}
// 直接移动整个目录
string result = AssetDatabase.MoveAsset(lightmapSourceDirAssetPath, destLightmapDir);
if (string.IsNullOrEmpty(result))
{
Debug.Log($"已成功移动光照数据目录: {lightmapSourceDirAssetPath} -> {destLightmapDir}");
}
else
{
Debug.LogError($"移动光照数据目录失败: {result}");
}
}
else
{
Debug.LogWarning($"找不到光照数据目录: {lightmapSourceDirAssetPath}");
}
}
catch (System.Exception e)
{
Debug.LogError($"移动光照文件时出错: {e.Message}");
}
AssetDatabase.Refresh();
Debug.Log("光照烘焙文件移动完成");
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 82ef00a42a40b4cffacc73f85be50b34
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,506 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
/// <summary>
/// 材质工具类,处理材质和贴图的复制、替换等操作
/// </summary>
public static class MaterialUtils
{
/// <summary>
/// 处理单个材质及其关联的贴图
/// </summary>
public static void ProcessMaterialAndTexture(Material sourceMat, string targetDir, string prefix,
Dictionary<Material, Material> matMapping,
Dictionary<Texture, Texture> texMapping)
{
if (sourceMat == null || matMapping.ContainsKey(sourceMat))
return;
// 收集原始材质中的所有贴图引用
Dictionary<string, Texture> textureProperties = new Dictionary<string, Texture>();
Dictionary<string, Vector2> textureScales = new Dictionary<string, Vector2>();
Dictionary<string, Vector2> textureOffsets = new Dictionary<string, Vector2>();
// 常见贴图属性名称
string[] texturePropertyNames = new string[]
{
"_MainTex", "_BaseMap", "_AlbedoMap", "_DiffuseMap",
"_BumpMap", "_NormalMap", "_EmissionMap", "_MetallicGlossMap",
"_OcclusionMap", "_DetailAlbedoMap", "_DetailNormalMap", "_SpecGlossMap",
"_ParallaxMap", "_DetailMask", "_MaskMap", "_SmoothnessMap"
};
// 收集原始材质中的所有贴图
foreach (var propName in texturePropertyNames)
{
if (sourceMat.HasProperty(propName))
{
Texture tex = sourceMat.GetTexture(propName);
if (tex != null)
{
textureProperties[propName] = tex;
textureScales[propName] = sourceMat.GetTextureScale(propName);
textureOffsets[propName] = sourceMat.GetTextureOffset(propName);
}
}
}
// 如果没有找到任何贴图属性至少确保我们查看_MainTex
if (textureProperties.Count == 0 && sourceMat.mainTexture != null)
{
textureProperties["_MainTex"] = sourceMat.mainTexture;
textureScales["_MainTex"] = sourceMat.mainTextureScale;
textureOffsets["_MainTex"] = sourceMat.mainTextureOffset;
}
// 为每个唯一贴图创建副本
Dictionary<Texture, Texture> localTexMapping = new Dictionary<Texture, Texture>();
foreach (var texEntry in textureProperties)
{
Texture originalTexture = texEntry.Value;
if (originalTexture == null) continue;
// 检查这个贴图是否已经有映射
if (texMapping.ContainsKey(originalTexture))
{
localTexMapping[originalTexture] = texMapping[originalTexture];
continue;
}
// 否则创建新的贴图副本
string originalTexPath = AssetDatabase.GetAssetPath(originalTexture);
if (string.IsNullOrEmpty(originalTexPath)) continue; // 跳过内置贴图
string texName = $"{prefix}{Path.GetFileNameWithoutExtension(originalTexPath)}";
string texExtension = Path.GetExtension(originalTexPath);
string newTexPath = $"{targetDir}/{texName}{texExtension}";
// 避免重复创建相同的贴图
if (!AssetDatabase.LoadAssetAtPath<Texture>(newTexPath))
{
AssetDatabase.CopyAsset(originalTexPath, newTexPath);
Debug.Log($"已复制贴图: {originalTexPath} -> {newTexPath}");
}
Texture copiedTexture = AssetDatabase.LoadAssetAtPath<Texture>(newTexPath);
if (copiedTexture != null)
{
localTexMapping[originalTexture] = copiedTexture;
texMapping[originalTexture] = copiedTexture;
}
}
// 准备材质路径
string matName = $"{prefix}{sourceMat.name}";
string newMatPath = $"{targetDir}/{matName}.mat";
// 重要: 无论如何都创建新材质,而不是复制,以确保所有引用都被正确更新
Material copiedMat = new Material(sourceMat.shader);
// 设置所有非贴图属性
CopyAllMaterialProperties(sourceMat, copiedMat);
// 删除可能存在的旧材质
if (AssetDatabase.LoadAssetAtPath<Material>(newMatPath) != null)
{
AssetDatabase.DeleteAsset(newMatPath);
}
// 创建新材质资产
AssetDatabase.CreateAsset(copiedMat, newMatPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// 重新加载刚保存的材质,确保获取正确的引用
copiedMat = AssetDatabase.LoadAssetAtPath<Material>(newMatPath);
if (copiedMat == null)
{
Debug.LogError($"无法加载新创建的材质: {newMatPath}");
return;
}
// 更新所有贴图引用 - 必须在创建资产后进行,确保引用正确保存
bool anyTexChanged = false;
foreach (var texProp in textureProperties)
{
string propName = texProp.Key;
Texture originalTexture = texProp.Value;
if (originalTexture != null && localTexMapping.ContainsKey(originalTexture) &&
copiedMat.HasProperty(propName))
{
Texture newTexture = localTexMapping[originalTexture];
// 特殊处理_MainTex确保它被正确更新
if (propName == "_MainTex")
{
copiedMat.mainTexture = newTexture;
Debug.Log($"显式设置 {copiedMat.name} 的 mainTexture");
}
copiedMat.SetTexture(propName, newTexture);
// 设置缩放和偏移
if (textureScales.ContainsKey(propName) && textureOffsets.ContainsKey(propName))
{
copiedMat.SetTextureScale(propName, textureScales[propName]);
copiedMat.SetTextureOffset(propName, textureOffsets[propName]);
}
Debug.Log($"已更新材质 {copiedMat.name} 的贴图属性 {propName}");
anyTexChanged = true;
}
}
// 如果有任何贴图被更改,确保再次保存材质
if (anyTexChanged)
{
EditorUtility.SetDirty(copiedMat);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// 再次检查是否所有贴图属性都已正确设置
bool allTexturePropsSet = true;
foreach (var texProp in textureProperties)
{
if (localTexMapping.ContainsKey(texProp.Value) &&
copiedMat.HasProperty(texProp.Key))
{
Texture expectedTexture = localTexMapping[texProp.Value];
Texture actualTexture = copiedMat.GetTexture(texProp.Key);
if (actualTexture != expectedTexture)
{
allTexturePropsSet = false;
Debug.LogError($"材质 {copiedMat.name} 的贴图属性 {texProp.Key} 未能正确设置");
}
}
}
if (allTexturePropsSet)
{
Debug.Log($"材质 {copiedMat.name} 的所有贴图属性已正确设置");
}
else
{
// 强制通过序列化方式修复引用
FixMaterialTextureReferences(copiedMat, localTexMapping);
}
}
// 添加到映射中
matMapping[sourceMat] = copiedMat;
Debug.Log($"已处理材质: {sourceMat.name} -> {newMatPath}");
}
/// <summary>
/// 使用序列化方式强制修复材质中的贴图引用
/// </summary>
public static void FixMaterialTextureReferences(Material material, Dictionary<Texture, Texture> texMapping)
{
string assetPath = AssetDatabase.GetAssetPath(material);
if (string.IsNullOrEmpty(assetPath)) return;
try
{
// 读取材质文件
string materialText = File.ReadAllText(assetPath);
bool changed = false;
// 对于每一个贴图映射查找并替换GUID
foreach (var mapping in texMapping)
{
Texture originalTexture = mapping.Key;
Texture newTexture = mapping.Value;
string originalGuid = string.Empty;
string newGuid = string.Empty;
// 获取原始贴图和新贴图的GUID
string originalPath = AssetDatabase.GetAssetPath(originalTexture);
string newPath = AssetDatabase.GetAssetPath(newTexture);
if (!string.IsNullOrEmpty(originalPath))
{
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(originalTexture, out originalGuid, out long _);
}
if (!string.IsNullOrEmpty(newPath))
{
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newTexture, out newGuid, out long _);
}
// 如果找到了GUID进行替换
if (!string.IsNullOrEmpty(originalGuid) && !string.IsNullOrEmpty(newGuid))
{
Debug.Log($"在材质 {material.name} 中替换GUID: {originalGuid} -> {newGuid}");
string pattern = $"guid: {originalGuid}";
string replacement = $"guid: {newGuid}";
if (materialText.Contains(pattern))
{
materialText = materialText.Replace(pattern, replacement);
changed = true;
}
}
}
// 如果有修改,写回文件
if (changed)
{
Debug.Log($"使用文本编辑方式修复材质 {material.name} 的贴图引用");
File.WriteAllText(assetPath, materialText);
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
}
}
catch (System.Exception ex)
{
Debug.LogError($"修复材质贴图引用时出错: {ex.Message}");
}
}
/// <summary>
/// 复制材质的所有属性
/// </summary>
public static void CopyAllMaterialProperties(Material source, Material destination)
{
// 复制浮点数、颜色和矢量属性
Shader shader = source.shader;
int propertyCount = ShaderUtil.GetPropertyCount(shader);
for (int i = 0; i < propertyCount; i++)
{
string propertyName = ShaderUtil.GetPropertyName(shader, i);
ShaderUtil.ShaderPropertyType propertyType = ShaderUtil.GetPropertyType(shader, i);
switch (propertyType)
{
case ShaderUtil.ShaderPropertyType.Color:
destination.SetColor(propertyName, source.GetColor(propertyName));
break;
case ShaderUtil.ShaderPropertyType.Vector:
destination.SetVector(propertyName, source.GetVector(propertyName));
break;
case ShaderUtil.ShaderPropertyType.Float:
case ShaderUtil.ShaderPropertyType.Range:
destination.SetFloat(propertyName, source.GetFloat(propertyName));
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
// 贴图属性在外部处理
break;
}
}
// 复制关键字
foreach (string keyword in source.shaderKeywords)
{
destination.EnableKeyword(keyword);
}
// 复制渲染队列
destination.renderQueue = source.renderQueue;
}
/// <summary>
/// 更新地面对象的材质引用
/// </summary>
public static void UpdateGroundMaterialReferences(GameObject ground, Dictionary<Material, Material> matMapping)
{
Renderer renderer = ground.GetComponent<Renderer>();
if (renderer != null)
{
UpdateRendererMaterialReferences(renderer, matMapping);
}
}
/// <summary>
/// 更新渲染器的材质引用
/// </summary>
public static void UpdateRendererMaterialReferences(Renderer renderer, Dictionary<Material, Material> matMapping)
{
Material[] sharedMaterials = renderer.sharedMaterials;
bool changed = false;
for (int i = 0; i < sharedMaterials.Length; i++)
{
Material originalMat = sharedMaterials[i];
if (originalMat != null && matMapping.ContainsKey(originalMat))
{
sharedMaterials[i] = matMapping[originalMat];
changed = true;
}
}
if (changed)
{
renderer.sharedMaterials = sharedMaterials;
EditorUtility.SetDirty(renderer);
Debug.Log($"已更新 {renderer.gameObject.name} 的材质引用");
}
}
/// <summary>
/// 确保没有原始引用残留
/// </summary>
public static void EnsureNoOriginalReferences(GameObject obj)
{
// 递归处理所有子对象
foreach (Transform child in obj.transform)
{
EnsureNoOriginalReferences(child.gameObject);
}
// 设置对象为脏确保Unity序列化变更
EditorUtility.SetDirty(obj);
// 如果是预制体实例,解包它
if (PrefabUtility.IsPartOfPrefabInstance(obj))
{
PrefabUtility.UnpackPrefabInstance(obj, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
}
}
/// <summary>
/// 在整个场景中查找并替换所有材质引用
/// </summary>
public static void FindAndReplaceAllMaterialReferences(Dictionary<Material, Material> matMapping)
{
// 获取场景中的所有渲染器
Renderer[] allRenderers = Object.FindObjectsOfType<Renderer>();
foreach (var renderer in allRenderers)
{
UpdateRendererMaterialReferences(renderer, matMapping);
}
}
/// <summary>
/// 替换材质的Shader为光照贴图支持的Shader
/// </summary>
public static void ReplaceShaderWithLightmapShader(List<Renderer> renderers)
{
// 追踪已处理的材质
var processedMaterials = new HashSet<Material>(); // 避免处理重复材质
// 尝试查找光照贴图Shader
var lightmapShader = Shader.Find("NLD_URP/NLD_Scene_Lightmap");
if (lightmapShader == null)
{
Debug.LogError("找不到NLD_Scene_Lightmap着色器请确保它存在于项目中");
return;
}
Debug.Log($"开始处理 {renderers.Count} 个渲染器用于Shader替换");
foreach (var renderer in renderers)
{
if (renderer == null)
continue;
// 获取渲染器的所有材质
Material[] sharedMaterials = renderer.sharedMaterials;
if (sharedMaterials == null || sharedMaterials.Length == 0)
continue;
for (int i = 0; i < sharedMaterials.Length; i++)
{
Material material = sharedMaterials[i];
if (material == null)
continue;
// 如果已经处理过这个材质,跳过
if (processedMaterials.Contains(material))
continue;
processedMaterials.Add(material);
if (material.shader != lightmapShader)
{
// 记录原始shader名称仅用于日志
string originalShaderName = material.shader != null ? material.shader.name : "未知";
// 保存原始主贴图及其Scale/Offset
Texture mainTexture = null;
Vector2 texScale = Vector2.one;
Vector2 texOffset = Vector2.zero;
Color baseColor = Color.white;
// 检查和保存原始贴图信息
if (material.HasProperty("_MainTex"))
{
mainTexture = material.GetTexture("_MainTex");
texScale = material.GetTextureScale("_MainTex");
texOffset = material.GetTextureOffset("_MainTex");
}
else if (material.HasProperty("_BaseMap"))
{
mainTexture = material.GetTexture("_BaseMap");
texScale = material.GetTextureScale("_BaseMap");
texOffset = material.GetTextureOffset("_BaseMap");
}
else if (material.HasProperty("_AlbedoMap"))
{
mainTexture = material.GetTexture("_AlbedoMap");
texScale = material.GetTextureScale("_AlbedoMap");
texOffset = material.GetTextureOffset("_AlbedoMap");
}
else if (material.HasProperty("_DiffuseMap"))
{
mainTexture = material.GetTexture("_DiffuseMap");
texScale = material.GetTextureScale("_DiffuseMap");
texOffset = material.GetTextureOffset("_DiffuseMap");
}
// 保存原始颜色
if (material.HasProperty("_Color"))
{
baseColor = material.GetColor("_Color");
}
else if (material.HasProperty("_BaseColor"))
{
baseColor = material.GetColor("_BaseColor");
}
// 修改shader
material.shader = lightmapShader;
// 设置贴图
if (mainTexture != null)
{
if (material.HasProperty("_BaseMap"))
{
material.SetTexture("_BaseMap", mainTexture);
material.SetTextureScale("_BaseMap", texScale);
material.SetTextureOffset("_BaseMap", texOffset);
}
if (material.HasProperty("_MainTex"))
{
material.SetTexture("_MainTex", mainTexture);
material.SetTextureScale("_MainTex", texScale);
material.SetTextureOffset("_MainTex", texOffset);
}
}
// 设置颜色
if (material.HasProperty("_BaseColor"))
{
material.SetColor("_BaseColor", baseColor);
}
if (material.HasProperty("_Color"))
{
material.SetColor("_Color", baseColor);
}
material.SetFloat("_Outline", 0.001f); //
Debug.Log($"已替换材质 {material.name} 的Shader: {originalShaderName} -> NLD_Scene_Lightmap");
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c4dd253ebf784d1e9cfa4d3d68d1181
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,114 @@
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 网格合并工具类
/// </summary>
public static class MeshCombineUtils
{
/// <summary>
/// 收集渲染器
/// </summary>
public static List<Renderer> CollectRenderers(List<Transform> roots)
{
List<Renderer> allRenderers = new();
foreach (var root in roots)
{
if (!root)
{
continue;
}
var render = root.GetComponentsInChildren<Renderer>();
if (render is not { Length: > 0 })
{
continue;
}
allRenderers.AddRange(render);
}
return allRenderers;
}
/// <summary>
/// 根据距离对渲染器进行分组
/// </summary>
public static Dictionary<string, List<Renderer>> GroupRenderersByCircularDistance(List<Renderer> renderers,
float maxDistance)
{
var groups = new Dictionary<string, List<Renderer>>();
var groupIndex = 0;
// 已分组的渲染器
var processedRenderers = new HashSet<Renderer>();
// 对所有渲染器进行处理
while (processedRenderers.Count < renderers.Count)
{
// 找到第一个未处理的渲染器作为新组的起点
Renderer startRenderer = null;
foreach (var renderer in renderers)
{
if (!processedRenderers.Contains(renderer))
{
startRenderer = renderer;
break;
}
}
if (startRenderer == null)
break;
// 创建新组
var currentGroup = new List<Renderer>();
currentGroup.Add(startRenderer);
processedRenderers.Add(startRenderer);
bool addedNew = true;
while (addedNew)
{
addedNew = false;
// 计算当前组的中心点
var groupCenter = CalculateGroupCenter(currentGroup);
// 查找距离中心点在阈值内的未处理渲染器
foreach (var renderer in renderers)
{
if (processedRenderers.Contains(renderer))
continue;
var distance = Vector3.Distance(groupCenter, renderer.transform.position);
if (!(distance <= maxDistance)) continue;
currentGroup.Add(renderer);
processedRenderers.Add(renderer);
addedNew = true;
}
}
groups.Add($"Group_{groupIndex}", currentGroup);
groupIndex++;
}
Debug.Log($"基于圆形区域分组:将{renderers.Count}个渲染器分成{groups.Count}个组,半径为{maxDistance}");
return groups;
}
/// <summary>
/// 计算渲染组的中心点
/// </summary>
private static Vector3 CalculateGroupCenter(List<Renderer> group)
{
if (group.Count == 0)
return Vector3.zero;
var sum = Vector3.zero;
foreach (var renderer in group)
{
sum += renderer.transform.position;
}
return sum / group.Count;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3be33ff74463a4d3c8296b795a31f868
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,171 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using UnityEngine.Rendering;
/// <summary>
/// 场景操作工具类
/// </summary>
public static class SceneUtils
{
/// <summary>
/// 递归查找子节点
/// </summary>
public static Transform FindChildRecursive(Transform parent, string childName)
{
// 首先检查直接子节点
Transform child = parent.Find(childName);
if (child != null)
return child;
// 如果直接子节点中没找到,递归查找每个子节点
for (int i = 0; i < parent.childCount; i++)
{
child = FindChildRecursive(parent.GetChild(i), childName);
if (child != null)
return child;
}
return null;
}
/// <summary>
/// 递归设置GameObject的静态标志
/// </summary>
public static void SetStaticFlagsRecursive(GameObject obj, StaticEditorFlags flags)
{
// 设置当前物体的静态标志
GameObjectUtility.SetStaticEditorFlags(obj, flags);
// 递归设置所有子物体
for (int i = 0; i < obj.transform.childCount; i++)
{
Transform child = obj.transform.GetChild(i);
SetStaticFlagsRecursive(child.gameObject, flags);
}
}
/// <summary>
/// 给地面添加BoxCollider
/// </summary>
public static void AddBoxColliderToGround(GameObject ground)
{
if (ground != null)
{
Object.DestroyImmediate(ground.GetComponent<Collider>());
var boxCollider = ground.gameObject.AddComponent<BoxCollider>();
boxCollider.size = new Vector3(1, 1, 1);
boxCollider.center = new Vector3(0, 0, 0.5f);
EditorSceneManager.MarkSceneDirty(ground.gameObject.scene);
EditorSceneManager.SaveOpenScenes();
AssetDatabase.Refresh();
}
else
{
Debug.LogError("地面节点不存在无法添加BoxCollider");
}
}
/// <summary>
/// 设置地面渲染队列
/// </summary>
public static void SetGroundRenderQueue(GameObject ground, int renderQueue)
{
if (ground != null)
{
GameObjectUtility.SetStaticEditorFlags(ground, StaticEditorFlags.ContributeGI);
var renderer = ground.GetComponent<Renderer>();
if (renderer != null)
{
renderer.shadowCastingMode = ShadowCastingMode.Off;
var materials = renderer.sharedMaterials;
foreach (var material in materials)
{
if (material != null)
{
renderer.shadowCastingMode = ShadowCastingMode.Off;
material.renderQueue = renderQueue;
Debug.Log($"将地面的材质 {material.name} 的RenderQueue改为{renderQueue}");
}
}
}
// 刷新资源以确保RenderQueue修改已保存
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
/// <summary>
/// 解包预制体
/// </summary>
public static void UnpackPrefab(GameObject obj)
{
if (obj != null && PrefabUtility.IsPartOfPrefabInstance(obj))
{
Debug.Log($"解包场景{obj.scene.name} 的预制体: {obj.name}");
PrefabUtility.UnpackPrefabInstance(obj,
PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
EditorSceneManager.MarkSceneDirty(obj.scene);
EditorSceneManager.SaveOpenScenes();
// 强制刷新
AssetDatabase.Refresh();
}
}
/// <summary>
/// 移除Scene中的重复EventSystem
/// </summary>
public static void RemoveRedundantEventSystems(Scene scene)
{
foreach (var rootObj in scene.GetRootGameObjects())
{
UnityEngine.EventSystems.EventSystem[] eventSystems = rootObj.GetComponentsInChildren<UnityEngine.EventSystems.EventSystem>();
if (null == eventSystems)
continue;
for (var i = 0; i < eventSystems.Length; ++i)
{
Object.DestroyImmediate(eventSystems[i].gameObject);
}
}
}
/// <summary>
/// 设置雾的位置
/// </summary>
public static void SetFogPosition(GameObject fog)
{
if (fog == null)
return;
var transform = fog.transform;
var position = transform.position;
transform.position = new Vector3(position.x, position.y + 0.01f, position.z);
}
/// <summary>
/// 获取标准化的资源路径
/// </summary>
public static string GetAssetPath(string path)
{
// 已经是Assets路径格式
if (path.StartsWith("Assets"))
return path;
// 绝对路径转相对Assets路径
string fullPath = Path.GetFullPath(path);
string assetsFullPath = Path.GetFullPath(Application.dataPath);
if (fullPath.StartsWith(assetsFullPath))
return "Assets" + fullPath.Substring(assetsFullPath.Length).Replace('\\', '/');
return path;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf8205ca3560b49cea7493bb6d55f615
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: