Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/UI/Button3D.cs

142 lines
5.5 KiB
C#
Raw Normal View History

2025-08-27 20:44:29 +08:00
using System.Linq; // 用于可能的LINQ操作但在此版本中可能不直接使用
using TMPro; // 用于 TextMeshPro 组件
using UnityEngine;
using UnityEngine.Events; // 用于 UnityEvent
namespace UI
{
public class Button3D : MonoBehaviour
{
[Tooltip("当按钮被点击时触发的UnityEvent。可以在Inspector中配置.")]
public UnityEvent OnClick;
public MeshRenderer renderer; // 请确保在Inspector中拖拽赋值
public Material outlineMaterial; // 请确保在Inspector中拖拽赋值
public TMP_Text text; // 文本组件
private Material[] _originalMaterials; // 用于存储按钮的原始材质,以便正确添加和移除描边
private void Awake()
{
if (renderer == null)
{
Debug.LogError("Button3D: MeshRenderer is not assigned. Please assign it in the Inspector.", this);
return;
}
if (outlineMaterial == null)
{
Debug.LogWarning(
"Button3D: Outline Material is not assigned. Outline effect will not work. Please assign it in the Inspector.",
this);
}
if (text == null)
{
Debug.LogWarning("Button3D: TMP_Text is not assigned. Text features will not work. Please assign it in the Inspector.", this);
// 这里不return允许按钮无文本运行
}
// 存储原始材质数组,以便在添加和移除描边时正确操作
// .ToArray() 是为了确保我们拿到的是拷贝,而不是引用,防止外部意外修改
_originalMaterials = renderer.materials.ToArray();
// 确保初始化时没有边框材质
// Important: 调用RemoveOutlineMaterialInternal()可能会清除_originalMaterials所以要确保先保存
RemoveOutlineMaterialInternal(); // 这一步会根据_originalMaterials重新设置renderer.materials
// 初始时隐藏文本
if (text != null)
{
text.gameObject.SetActive(false);
}
}
private void Update()
{
// 文本始终朝向摄像机
if (text != null && text.IsActive() && Camera.main != null)
{
text.transform.LookAt(text.transform.position + Camera.main.transform.rotation * Vector3.forward,
Camera.main.transform.rotation * Vector3.up);
}
}
private void OnMouseEnter()
{
if (renderer == null) return;
// 鼠标进入时显示文本
if (text != null)
{
text.gameObject.SetActive(true);
}
// 如果没有描边材质,或者渲染器已经有描边材质,就没必要再添加了
// 检查当前材质数组长度是否已经大于等于_originalMaterials的长度+1
// 这样可以避免重复添加
if (outlineMaterial == null || renderer.materials.Length > _originalMaterials.Length)
{
return;
}
// 如果当前材质数组和_originalMaterials不一致说明可能被其他逻辑修改了
// 稳妥起见最好先恢复到_originalMaterials
if (!renderer.materials.SequenceEqual(_originalMaterials))
{
//Debug.LogWarning("Button3D: Renderer materials were unexpectedly modified before OnMouseEnter. Restoring original materials.");
// 通常不应该发生,除非有其他脚本在修改
renderer.materials = _originalMaterials;
}
// 创建一个新数组,包含原始材质和描边材质
Material[] newMaterials = new Material[_originalMaterials.Length + 1];
System.Array.Copy(_originalMaterials, newMaterials, _originalMaterials.Length);
newMaterials[_originalMaterials.Length] = outlineMaterial;
renderer.materials = newMaterials;
}
private void OnMouseExit()
{
if (renderer == null) return;
// 鼠标离开时隐藏文本
if (text != null)
{
text.gameObject.SetActive(false);
}
// 调用内部方法移除边框材质
RemoveOutlineMaterialInternal();
}
private void OnMouseDown()
{
// 触发UnityEvent
OnClick.Invoke();
}
/// <summary>
/// 移除渲染器上的边框材质(如果存在),恢复到按钮的原始材质。
/// </summary>
private void RemoveOutlineMaterialInternal()
{
if (renderer == null) return;
// 即使_originalMaterials == null因为Awake中的LogError也可以安全赋值空数组
// 但如果_originalMaterials确实是null说明Awake有问题后续操作也可能继续出错
if (_originalMaterials == null)
{
Debug.LogError("Button3D: _originalMaterials is null. Cannot remove outline. Check Awake method.", this);
return;
}
// 直接将渲染器材质恢复为_originalMaterials的副本
renderer.materials = _originalMaterials.ToArray(); // 使用ToArray()确保赋值一个副本避免_originalMaterials被意外修改
}
}
}