using UnityEngine; using UnityEditor; using System.Collections.Generic; using System; public class BillboardGenerator : EditorWindow { private string targetTag = "BillboardTarget"; private string savePath = "Assets/{0}/BillboardTextures/"; private string sceneName = "MinimalistBedroom"; private int textureSize = 512; private string combainedPath; [MenuItem("Tools/Billboard Generator")] static void Init() { BillboardGenerator window = (BillboardGenerator)EditorWindow.GetWindow(typeof(BillboardGenerator)); window.Show(); } void OnGUI() { GUILayout.Label("Billboard Generator Settings", EditorStyles.boldLabel); targetTag = EditorGUILayout.TextField("Target Tag", targetTag); textureSize = EditorGUILayout.IntField("Texture Size", textureSize); sceneName = EditorGUILayout.TextField("Scene Name", sceneName); savePath = EditorGUILayout.TextField("Save Path", savePath); combainedPath = string.Format(savePath, sceneName); if (GUILayout.Button("Generate Billboards")) { GenerateBillboards(); } } void GenerateBillboards() { // 确保保存路径存在 if (!System.IO.Directory.Exists(combainedPath)) { System.IO.Directory.CreateDirectory(combainedPath); } // 在方法开始时添加 tag SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]); SerializedProperty tagsProp = tagManager.FindProperty("tags"); // 检查 tag 是否已存在 bool found = false; for (int i = 0; i < tagsProp.arraySize; i++) { SerializedProperty t = tagsProp.GetArrayElementAtIndex(i); if (t.stringValue.Equals("Billboard")) { found = true; break; } } // 如果 tag 不存在,添加它 if (!found) { tagsProp.InsertArrayElementAtIndex(tagsProp.arraySize); SerializedProperty newTag = tagsProp.GetArrayElementAtIndex(tagsProp.arraySize - 1); newTag.stringValue = "Billboard"; tagManager.ApplyModifiedProperties(); } // 获取所有标记的物体 GameObject[] taggedObjects = GameObject.FindGameObjectsWithTag(targetTag); foreach (GameObject obj in taggedObjects) { // 创建临时相机 GameObject tempCameraObj = new GameObject("TempCamera"); Camera tempCamera = tempCameraObj.AddComponent(); tempCamera.clearFlags = CameraClearFlags.SolidColor; tempCamera.backgroundColor = new Color(0, 0, 0, 0); // 透明背景 // 获取目标对象及其子对象的所有Renderer组件 Renderer[] renderers = obj.GetComponentsInChildren(); if (renderers.Length == 0) { Debug.LogWarning($"No Renderer found in {obj.name} or its children."); continue; // 跳过此对象,或者根据需要处理 } // 初始化包围盒为第一个Renderer的bounds Bounds bounds = renderers[0].bounds; // 合并所有Renderer的bounds for (int i = 1; i < renderers.Length; i++) { bounds.Encapsulate(renderers[i].bounds); } // 设置相机位置和朝向 float distance = bounds.extents.magnitude * 2.0f; // 根据需要调整倍率 Vector3 direction = obj.transform.forward; // 模型的正面方向 Vector3 cameraPosition = bounds.center - direction * distance; tempCamera.transform.position = cameraPosition; tempCamera.transform.LookAt(bounds.center); // 设置相机视野 tempCamera.orthographic = true; tempCamera.orthographicSize = bounds.extents.magnitude; // 创建RenderTexture,支持Alpha通道 RenderTexture rt = new RenderTexture(textureSize, textureSize, 24, RenderTextureFormat.ARGB32); rt.antiAliasing = 1; // 禁用MSAA tempCamera.targetTexture = rt; // 渲染到纹理 tempCamera.Render(); // 创建Texture2D并读取RenderTexture RenderTexture.active = rt; Texture2D texture = new Texture2D(textureSize, textureSize, TextureFormat.RGBA32, false); texture.ReadPixels(new Rect(0, 0, textureSize, textureSize), 0, 0); texture.Apply(); RenderTexture.active = null; // 重置 // 保存贴图 string texturePath = combainedPath + obj.name + "_billboard.png"; byte[] bytes = texture.EncodeToPNG(); System.IO.File.WriteAllBytes(texturePath, bytes); AssetDatabase.ImportAsset(texturePath); // 确保资源已导入 // 设置贴图的导入设置,支持透明度 TextureImporter importer = AssetImporter.GetAtPath(texturePath) as TextureImporter; if (importer != null) { importer.textureType = TextureImporterType.Default; importer.alphaIsTransparency = true; importer.mipmapEnabled = false; importer.SaveAndReimport(); } // 创建材质 string materialPath = combainedPath + obj.name + "_billboard.mat"; Material billboardMaterial = new Material(Shader.Find("Unlit/Transparent")); AssetDatabase.CreateAsset(billboardMaterial, materialPath); billboardMaterial.mainTexture = AssetDatabase.LoadAssetAtPath(texturePath); // 创建Billboard预制体 GameObject billboard = GameObject.CreatePrimitive(PrimitiveType.Quad); billboard.name = obj.name + "_Billboard"; billboard.transform.position = obj.transform.position; billboard.transform.localRotation = Quaternion.identity; // 重置旋转 billboard.transform.localScale = obj.transform.localScale; billboard.GetComponent().material = billboardMaterial; // 添加Billboard脚本 billboard.AddComponent(); // 创建预制体 string prefabPath = combainedPath + obj.name + "_billboard.prefab"; PrefabUtility.SaveAsPrefabAsset(billboard, prefabPath); // 清理临时对象 DestroyImmediate(tempCameraObj); DestroyImmediate(billboard); // 在场景中隐藏原物体 obj.SetActive(false); // 加载并实例化Billboard预制体到场景中 GameObject billboardPrefab = AssetDatabase.LoadAssetAtPath(prefabPath); if (billboardPrefab != null) { GameObject billboardInstance = PrefabUtility.InstantiatePrefab(billboardPrefab) as GameObject; billboardInstance.transform.position = obj.transform.position; billboardInstance.transform.localRotation = Quaternion.identity; // 重置旋转 billboardInstance.transform.localScale = obj.transform.localScale; // 将billboard放在原物体的父级下 billboardInstance.transform.SetParent(obj.transform.parent); billboardInstance.tag = "Billboard"; } else { Debug.LogError($"Failed to load billboard prefab at path: {prefabPath}"); } } Debug.Log("Billboard generation completed!"); } } // Billboard行为脚本 public class BillboardBehavior : MonoBehaviour { private Camera mainCamera; void Start() { mainCamera = Camera.main; } void Update() { transform.LookAt(mainCamera.transform.position); transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y, 0); } }