RenderTexture通俗解釋
RenderTexture就像是Unity中的"虛擬相機膠片",它可以:
- 捕獲3D內容:將3D場景或對象"拍照"記錄下來
- 實時更新:不是靜態圖片,而是動態視頻,角色可以動起來
- 用作紋理:可以顯示在UI界面上
在角色預覽系統中的作用
從你的截圖和代碼中可以看出:
- 分離渲染:CharacterPreviewRT(512×512分辨率)專門用來渲染角色模型
- 橋梁作用:連接了3D世界和2D界面
- 3D世界:CharacterPreviewCamera(相機)拍攝放在CharacterPosition(位置)的角色模型
- 2D界面:通過RawImage顯示這個"拍攝"結果
- 性能優化:
- 只渲染需要的角色,不渲染整個場景
- 可以控制渲染質量(如你設置的2×抗鋸齒)
- 交互實現:
- 你的代碼中的HandleCharacterRotation()方法讓用戶可以旋轉預覽角色
實現思路(我的界面結構參考)
?
創建一個顯示的面板(CharacterInfoPanel),并且添加組件(重要)
界面參考布局
相機配置注意配置Culling Mask為對應的層級
?
代碼參考:
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using Game.Managers;namespace Game.UI
{/// <summary>/// 角色選擇面板,處理角色瀏覽、選擇和預覽功能/// </summary>public class CharacterSelectPanel : BasePanel{[Header("角色列表")][SerializeField] private Transform characterListContainer; // 角色列表容器[SerializeField] private GameObject characterItemPrefab; // 角色選項預制體[SerializeField] private ScrollRect characterScrollRect; // 角色列表滾動視圖[Header("角色預覽")][SerializeField] private RawImage characterPreviewImage; // 角色預覽圖像[SerializeField] private Transform characterPosition; // 角色位置[Header("按鈕")][SerializeField] private Button confirmButton; // 確認按鈕[Header("旋轉設置")][SerializeField] private float rotationSpeed = 30f; // 旋轉速度// 角色數據private List<CharacterData> characterDataList = new List<CharacterData>();// 預覽相關private GameObject currentPreviewCharacter;private bool isDragging = false;private float lastMouseX;private int selectedCharacterIndex = -1;private List<CharacterItemUI> characterItemUIs = new List<CharacterItemUI>();protected override void Initialize(){base.Initialize();Debug.Log("[CharacterSelectPanel] 初始化面板");// 驗證組件引用if(characterPosition == null)Debug.LogError("[CharacterSelectPanel] characterPosition未設置,請將預覽位置拖拽到Inspector面板");if(characterPreviewImage == null)Debug.LogError("[CharacterSelectPanel] characterPreviewImage未設置,請將RawImage拖拽到Inspector面板");if(characterItemPrefab == null)Debug.LogError("[CharacterSelectPanel] characterItemPrefab未設置,請將角色項預制體拖拽到Inspector面板");// 檢查角色項預制體是否包含必要組件CharacterItemUI testItemUI = characterItemPrefab.GetComponent<CharacterItemUI>();if(testItemUI == null)Debug.LogError("[CharacterSelectPanel] 角色項預制體必須包含CharacterItemUI組件!請在預制體上添加此組件");// 初始化組件引用if (confirmButton != null){confirmButton.onClick.AddListener(OnConfirmButtonClicked);confirmButton.interactable = false; // 初始狀態下禁用確認按鈕}// 加載角色數據LoadCharacterData();// 初始化角色列表PopulateCharacterList();}protected override void SetupButtons(){base.SetupButtons();// 可以在這里添加其他按鈕的監聽器}void Update(){// 處理角色旋轉HandleCharacterRotation();}/// <summary>/// 處理角色旋轉/// </summary>private void HandleCharacterRotation(){if (characterPreviewImage == null || currentPreviewCharacter == null) return;// 檢查預覽圖片上的鼠標事件if (RectTransformUtility.RectangleContainsScreenPoint(characterPreviewImage.rectTransform, Input.mousePosition)){// 鼠標按下開始拖動if (Input.GetMouseButtonDown(0)){isDragging = true;lastMouseX = Input.mousePosition.x;}}// 松開鼠標停止拖動if (Input.GetMouseButtonUp(0)){isDragging = false;}// 拖動時旋轉角色if (isDragging && currentPreviewCharacter != null){float deltaX = Input.mousePosition.x - lastMouseX;currentPreviewCharacter.transform.Rotate(0, -deltaX * rotationSpeed * Time.deltaTime, 0);lastMouseX = Input.mousePosition.x;}}/// <summary>/// 加載角色數據/// </summary>private void LoadCharacterData(){Debug.Log("[CharacterSelectPanel] 加載角色數據");// 理想情況下,這些數據應該從DataManager或資源文件加載// 這里為演示創建一些測試數據characterDataList.Clear();// 簡化測試數據,只包含必要信息characterDataList.Add(new CharacterData(0, "戰士", "", "Characters/Warrior"));characterDataList.Add(new CharacterData(1, "忍者", "", "Characters/Ninja"));characterDataList.Add(new CharacterData(2, "魔法師", "", "Characters/Mage"));characterDataList.Add(new CharacterData(3, "武僧", "", "Characters/Monk"));Debug.Log($"[CharacterSelectPanel] 已加載{characterDataList.Count}個角色數據");}/// <summary>/// 填充角色列表/// </summary>private void PopulateCharacterList(){if (characterListContainer == null){Debug.LogError("[CharacterSelectPanel] characterListContainer未設置,無法填充角色列表");return;}if (characterItemPrefab == null){Debug.LogError("[CharacterSelectPanel] characterItemPrefab未設置,無法填充角色列表");return;}Debug.Log("[CharacterSelectPanel] 填充角色列表");// 清空現有列表和記錄foreach (Transform child in characterListContainer){Destroy(child.gameObject);}characterItemUIs.Clear();// 為每個角色創建列表項for (int i = 0; i < characterDataList.Count; i++){CharacterData data = characterDataList[i];GameObject item = Instantiate(characterItemPrefab, characterListContainer);item.name = $"CharacterItem_{data.name}";// 配置角色項CharacterItemUI itemUI = item.GetComponent<CharacterItemUI>();if (itemUI != null){int index = i; // 創建局部變量以便在閉包中使用itemUI.Setup(data);itemUI.OnItemSelected += () => OnCharacterItemSelected(index);characterItemUIs.Add(itemUI);Debug.Log($"[CharacterSelectPanel] 創建角色列表項: {data.name}");}else{Debug.LogError($"[CharacterSelectPanel] 角色列表項預制體上沒有CharacterItemUI組件!");}}}/// <summary>/// 角色項被選中/// </summary>private void OnCharacterItemSelected(int index){if (index < 0 || index >= characterDataList.Count) return;Debug.Log($"[CharacterSelectPanel] 選擇角色: {characterDataList[index].name}");// 更新選中的角色索引selectedCharacterIndex = index;CharacterData data = characterDataList[index];// 更新角色項UI狀態for (int i = 0; i < characterItemUIs.Count; i++){if (characterItemUIs[i] != null){characterItemUIs[i].SetSelected(i == index);}}// 加載角色預覽LoadCharacterPreview(data.prefabPath);// 啟用確認按鈕if (confirmButton != null)confirmButton.interactable = true;}/// <summary>/// 加載角色預覽/// </summary>private void LoadCharacterPreview(string prefabPath){Debug.Log($"[CharacterSelectPanel] 開始加載角色預覽: {prefabPath}");if (characterPosition == null){Debug.LogError("[CharacterSelectPanel] characterPosition未設置,無法加載角色預覽");return;}// 清除當前預覽角色if (currentPreviewCharacter != null){Destroy(currentPreviewCharacter);currentPreviewCharacter = null;}// 加載角色預制體GameObject characterPrefab = Resources.Load<GameObject>(prefabPath);if (characterPrefab == null){Debug.LogError($"[CharacterSelectPanel] 無法加載角色預制體: {prefabPath},請檢查路徑是否正確并且確保預制體存在于Resources目錄");return;}Debug.Log($"[CharacterSelectPanel] 成功加載角色預制體: {prefabPath}");// 實例化新角色currentPreviewCharacter = Instantiate(characterPrefab, characterPosition);// 設置層和位置int previewLayer = LayerMask.NameToLayer("CharacterPreview");if (previewLayer < 0){Debug.LogError("[CharacterSelectPanel] 未找到'CharacterPreview'層,請在項目設置中添加此層");previewLayer = 0; // 使用默認層}currentPreviewCharacter.layer = previewLayer;SetLayerRecursively(currentPreviewCharacter, previewLayer);// 重置位置和旋轉currentPreviewCharacter.transform.localPosition = Vector3.zero;currentPreviewCharacter.transform.localRotation = Quaternion.identity;// 播放待機動畫(如果有)Animator animator = currentPreviewCharacter.GetComponent<Animator>();if (animator != null){animator.Play("Idle");Debug.Log("[CharacterSelectPanel] 已播放Idle動畫");}else{Debug.Log("[CharacterSelectPanel] 角色沒有Animator組件,跳過動畫播放");}Debug.Log($"[CharacterSelectPanel] 角色預覽加載完成: {prefabPath}");}/// <summary>/// 遞歸設置物體及其子物體的層/// </summary>private void SetLayerRecursively(GameObject obj, int layer){obj.layer = layer;foreach (Transform child in obj.transform){SetLayerRecursively(child.gameObject, layer);}}/// <summary>/// 確認按鈕點擊事件/// </summary>private void OnConfirmButtonClicked(){if (selectedCharacterIndex < 0){Debug.LogWarning("請先選擇一個角色");return;}// 保存選擇的角色if (DataManager.Instance != null){DataManager.Instance.SetSelectedCharacter(selectedCharacterIndex);}Debug.Log($"確認選擇角色: {characterDataList[selectedCharacterIndex].name}");// 隱藏角色選擇面板Hide();// 進入戰斗場景if (GameManager.Instance != null){GameManager.Instance.ChangeState(GameState.Battle);}}protected override void OnPanelShow(){base.OnPanelShow();Debug.Log("[CharacterSelectPanel] 顯示面板");// 重置選擇狀態selectedCharacterIndex = -1;if (confirmButton != null)confirmButton.interactable = false;// 清除當前預覽if (currentPreviewCharacter != null){Destroy(currentPreviewCharacter);currentPreviewCharacter = null;}}protected override void OnBackButtonClicked(){Debug.Log("[CharacterSelectPanel] 返回按鈕點擊");Hide();// 返回難度選擇界面UIManager.Instance.ShowDifficultySelectPanel();}}/// <summary>/// 角色數據類/// </summary>[System.Serializable]public class CharacterData{public int id;public string name;public string description;public string prefabPath;public CharacterData(int id, string name, string description, string prefabPath){this.id = id;this.name = name;this.description = description;this.prefabPath = prefabPath;}}
}