142 lines
5.5 KiB
C#
142 lines
5.5 KiB
C#
|
||
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被意外修改
|
||
}
|
||
}
|
||
}
|
||
|