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;
}
}
}