868 lines
31 KiB
C#
868 lines
31 KiB
C#
// 引入必要的命名空间
|
||
|
||
using System;
|
||
using System.Threading.Tasks;
|
||
using Cysharp.Threading.Tasks;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.UI;
|
||
using DG.Tweening;
|
||
|
||
/// <summary>
|
||
/// 引导遮罩类,用于创建和管理引导过程中的遮罩效果
|
||
/// </summary>
|
||
public class GuideMask : MonoBehaviour
|
||
{
|
||
// 遮罩的 RectTransform 组件,可在 Inspector 面板中赋值
|
||
public RectTransform rectTransform;
|
||
public GameObject CanvasParent;
|
||
|
||
public RectTransform buttonPrefab;
|
||
|
||
// 遮罩的 RawImage 组件,用于显示遮罩图片
|
||
private RawImage _rawImage;
|
||
|
||
// 遮罩的 RectTransform 组件
|
||
private RectTransform _rectTrans;
|
||
|
||
// 遮罩使用的材质
|
||
private Material _materia;
|
||
|
||
// 当前遮罩的原点位置,初始化为一个很大的值
|
||
private Vector4 _currentOrigin = new Vector4(-9999, -9999, 9999, 9999);
|
||
|
||
// 当前正在进行的 DOTween 动画
|
||
private Tween currentTween;
|
||
|
||
// 遮罩所在的 Canvas 组件
|
||
private Canvas _canvas;
|
||
|
||
// 事件穿透组件
|
||
private EventPenetrate ev;
|
||
|
||
// 引导遮罩的游戏对象
|
||
public GameObject guide;
|
||
|
||
// 是否正在显示动画
|
||
private bool ShowTween = false;
|
||
|
||
// 当前遮罩的半径数值
|
||
private float CurRadNum;
|
||
|
||
// 动画完成后的回调函数
|
||
private Action Tweencallback;
|
||
|
||
// 回调函数是否被触发的标志
|
||
private bool callback = false;
|
||
|
||
// 单例模式,确保全局只有一个 GuideMask 实例
|
||
public static GuideMask Instance { private set; get; }
|
||
|
||
// 遮罩的偏移量,用于调整遮罩的大小
|
||
private float offset = 3;
|
||
|
||
// 目标位置跟踪相关字段
|
||
private GameObject _trackingTarget; // 当前跟踪的目标对象
|
||
private Vector3 _lastTargetPosition; // 上次记录的目标位置
|
||
private Vector3 _lastTargetScale; // 上次记录的目标缩放
|
||
private bool _isTrackingTarget = false; // 是否正在跟踪目标
|
||
|
||
/// <summary>
|
||
/// 脚本实例被创建时调用,进行初始化操作
|
||
/// </summary>
|
||
void Awake()
|
||
{
|
||
// 将当前实例赋值给单例
|
||
Instance = this;
|
||
// // 获取渲染器的材质
|
||
// _materia = GetComponent<MeshRenderer>().material;
|
||
// // 设置材质的 _Origin 属性为当前原点位置
|
||
// _materia.SetVector("_Origin", _currentOrigin);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建一个带有点击事件的遮罩
|
||
/// </summary>
|
||
/// <param name="Clickcallback">点击遮罩时执行的回调函数</param>
|
||
/// <param name="callback">遮罩显示完成后执行的回调函数</param>
|
||
/// <param name="black">是否使用黑色遮罩,默认为 false</param>
|
||
public void CreateMaskClick(UnityAction Clickcallback, Action callback, bool black = false)
|
||
{
|
||
// 显示引导遮罩,并在显示完成后执行回调
|
||
ShowGuideMask(() =>
|
||
{
|
||
// 如果引导遮罩对象存在
|
||
if (guide != null)
|
||
{
|
||
// 如果需要黑色遮罩
|
||
if (black)
|
||
{
|
||
// 设置遮罩图片的颜色为黑色且不透明
|
||
_rawImage.color = new Color(0, 0, 0, 1);
|
||
}
|
||
else
|
||
{
|
||
// 设置遮罩图片的颜色为白色且透明
|
||
_rawImage.color = new Color(1, 1, 1, 0);
|
||
}
|
||
|
||
// 移除按钮上的所有点击事件监听器
|
||
guide.GetComponent<Button>().onClick.RemoveAllListeners();
|
||
// 添加新的点击事件监听器
|
||
guide.GetComponent<Button>().onClick.AddListener(Clickcallback);
|
||
// 执行回调函数
|
||
callback();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 创建一个矩形遮罩区域
|
||
/// </summary>
|
||
/// <param name="target">目标对象,遮罩将围绕该对象创建</param>
|
||
/// <param name="tipePlane">提示面板对象,可选参数</param>
|
||
public void CreateRectangleMask(GameObject target, GameObject tipePlane = null)
|
||
{
|
||
if (!rectTransform.gameObject.activeInHierarchy)
|
||
{
|
||
rectTransform.gameObject.SetActive(true);
|
||
}
|
||
|
||
|
||
// 如果目标对象存在
|
||
if (target != null)
|
||
{
|
||
try
|
||
{
|
||
// 获取目标对象的 RectTransform 组件
|
||
RectTransform rec = target.GetComponent<RectTransform>();
|
||
// 用于存储目标对象的四个角的世界坐标
|
||
Vector3[] _corners = new Vector3[4];
|
||
// 获取目标对象的四个角的世界坐标
|
||
rec.GetWorldCorners(_corners);
|
||
// 将目标对象左下角的世界坐标转换为画布坐标
|
||
Vector2 pos1 = WorldToCanvasPos(_corners[0]);
|
||
// 将目标对象右上角的世界坐标转换为画布坐标
|
||
Vector2 pos2 = WorldToCanvasPos(_corners[2]);
|
||
|
||
// 获取目标对象的全局缩放比例
|
||
Vector3 targetScale = target.transform.lossyScale;
|
||
|
||
// 计算目标对象的实际大小,考虑缩放因素
|
||
Vector2 actualSize = new Vector2(
|
||
rec.sizeDelta.x * targetScale.x,
|
||
rec.sizeDelta.y * targetScale.y
|
||
);
|
||
|
||
|
||
// 检查目标对象是否在ScrollRect中
|
||
ScrollRect scrollRect = target.GetComponentInParent<ScrollRect>();
|
||
|
||
// // 打印左下角的画布坐标
|
||
// Debug.Log(pos1);
|
||
// // 打印右上角的画布坐标
|
||
// Debug.Log(pos2);
|
||
|
||
// 获取目标对象的四个角的世界坐标
|
||
Vector3[] corners = new Vector3[4];
|
||
rec.GetWorldCorners(corners);
|
||
// 将目标对象左下角的世界坐标转换为画布坐标
|
||
Vector2 worldPos = WorldToCanvasPos(corners[0]);
|
||
|
||
|
||
// 如果遮罩的 RectTransform 组件存在
|
||
if (rectTransform != null)
|
||
{
|
||
if (scrollRect != null)
|
||
{
|
||
// 获取UI对象的RectTransform
|
||
RectTransform targetRectTrans = target.GetComponent<RectTransform>();
|
||
|
||
// 打印目标对象的基本信息
|
||
// Debug.Log($"目标对象: {target.name}, 位置: {target.transform.position}, localPosition: {target.transform.localPosition}");
|
||
// Debug.Log($"目标对象: pivot: {targetRectTrans.pivot}, anchor: {targetRectTrans.anchorMin}/{targetRectTrans.anchorMax}");
|
||
|
||
// 获取UI对象在世界空间中的四个角坐标
|
||
Vector3[] corners1 = new Vector3[4];
|
||
targetRectTrans.GetWorldCorners(corners1);
|
||
|
||
// 打印四个角的坐标
|
||
// Debug.Log($"目标四角: 左下:{corners1[0]}, 左上:{corners1[1]}, 右上:{corners1[2]}, 右下:{corners1[3]}");
|
||
|
||
// 计算世界空间中的中心点
|
||
Vector3 centerWorld = (corners1[0] + corners1[2]) / 2;
|
||
|
||
// 计算UI对象的尺寸
|
||
float width = Vector3.Distance(corners1[0], corners1[3]);
|
||
float height = Vector3.Distance(corners1[0], corners1[1]);
|
||
|
||
// 打印遮罩对象信息
|
||
//Debug.Log($"遮罩对象: pivot: {rectTransform.pivot}, anchor: {rectTransform.anchorMin}/{rectTransform.anchorMax}");
|
||
// Debug.Log($"遮罩位置前: position: {rectTransform.position}, anchoredPosition: {rectTransform.anchoredPosition}");
|
||
|
||
// 设置锚点和轴心点都在中心
|
||
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||
|
||
// 设置遮罩的世界位置和大小
|
||
rectTransform.transform.localScale = Vector3.one;
|
||
//rectTransform.position = centerWorld; // 直接设置世界坐标位置
|
||
rectTransform.sizeDelta = new Vector2(width + 10, height + 10);
|
||
|
||
// 打印设置后的信息
|
||
// Debug.Log($"遮罩位置后: position: {rectTransform.position}, anchoredPosition: {rectTransform.anchoredPosition}");
|
||
// Debug.Log($"ScrollRect中的UI对象 - 中心点:{centerWorld}, 宽度:{width}, 高度:{height}");
|
||
|
||
|
||
rectTransform.SetParent(target.transform);
|
||
rectTransform.anchoredPosition = Vector2.zero;
|
||
// rectTransform.parent=null;
|
||
rectTransform.SetParent(CanvasParent.transform);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 创建一个默认的矩形遮罩区域
|
||
// CreateRectangleMaskRect(new Vector2(0, 0), new Vector2(0, 0), target);
|
||
// 清空动画完成后的回调函数
|
||
Tweencallback = null;
|
||
}
|
||
|
||
|
||
if (scrollRect != null)
|
||
{
|
||
// 如果在ScrollRect中,调整位置计算
|
||
//Debug.Log($"目标对象在ScrollRect中,原始位置: pos1={pos1}, pos2={pos2}");
|
||
|
||
// 调整位置,考虑UI对象的中心点
|
||
Vector2 adjustedPos1 = pos1;
|
||
Vector2 adjustedPos2 = pos2;
|
||
buttonPrefab.anchoredPosition = new Vector2(0, rectTransform.sizeDelta.y / 2);
|
||
//Debug.Log($"调整后位置: pos1={adjustedPos1}, pos2={adjustedPos2}");
|
||
|
||
// 创建矩形遮罩区域
|
||
//CreateRectangleMaskRect(adjustedPos1, adjustedPos2, target, tipePlane);
|
||
}
|
||
else
|
||
{
|
||
rectTransform.SetParent(target.transform);
|
||
rectTransform.anchoredPosition = Vector2.zero;
|
||
rectTransform.sizeDelta = new Vector2(actualSize.x + 10, actualSize.y + 10);
|
||
// rectTransform.parent=null;
|
||
rectTransform.SetParent(CanvasParent.transform);
|
||
|
||
|
||
// Debug.Log(rectTransform.sizeDelta.y/2);
|
||
buttonPrefab.anchoredPosition = new Vector2(0, rectTransform.sizeDelta.y / 2);
|
||
|
||
|
||
// Debug.Log(pos1);
|
||
// Debug.Log(pos2);
|
||
// 如果不在ScrollRect中,使用常规方法
|
||
// CreateRectangleMaskRect(pos1, pos2, target, tipePlane);
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.Log(e.Message);
|
||
}
|
||
|
||
// 清空动画完成后的回调函数
|
||
Tweencallback = null;
|
||
|
||
// 自动开始跟踪目标对象位置变化
|
||
StartTrackingTarget(target);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据给定的坐标创建矩形遮罩区域
|
||
/// </summary>
|
||
/// <param name="pos1">矩形的左下角坐标</param>
|
||
/// <param name="pos2">矩形的右上角坐标</param>
|
||
/// <param name="target">目标对象</param>
|
||
/// <param name="tipePlane">提示面板对象,可选参数</param>
|
||
public void CreateRectangleMaskRect(Vector3 pos1, Vector3 pos2, GameObject target, GameObject tipePlane = null)
|
||
{
|
||
// 显示引导遮罩,并在显示完成后执行回调
|
||
ShowGuideMask(() =>
|
||
{
|
||
if (!_rectTrans.gameObject.activeInHierarchy)
|
||
{
|
||
_rectTrans.gameObject.SetActive(true);
|
||
}
|
||
|
||
// 标记回调函数需要被触发
|
||
callback = true;
|
||
// 设置事件穿透组件的目标对象
|
||
ev.SetTargetImage(target);
|
||
// 设置遮罩的大小为零
|
||
_rectTrans.sizeDelta = Vector2.zero;
|
||
// 设置材质的 _MaskType 属性为 1,表示矩形遮罩
|
||
_materia.SetFloat("_MaskType", 1.0f);
|
||
// 设置允许点击穿透属性为1.0,表示开启点击穿透
|
||
_materia.SetFloat("_AllowClickThrough", 1.0f);
|
||
|
||
// 提示面板的宽度
|
||
float tipeWidth = 0;
|
||
// 提示面板的高度
|
||
float tipeHeight = 0;
|
||
// 如果提示面板对象存在
|
||
if (tipePlane != null)
|
||
{
|
||
// 获取提示面板的 RectTransform 组件
|
||
RectTransform tipeRect = tipePlane.GetComponent<RectTransform>();
|
||
// 用于存储提示面板的四个角的世界坐标
|
||
Vector3[] tipeCorners = new Vector3[4];
|
||
// 获取提示面板的四个角的世界坐标
|
||
tipeRect.GetWorldCorners(tipeCorners);
|
||
// 将提示面板左下角的世界坐标转换为画布坐标
|
||
Vector2 tipePos1 = WorldToCanvasPos(tipeCorners[0]);
|
||
|
||
// 打印提示面板左下角的画布坐标
|
||
Debug.Log(tipePos1.y);
|
||
// 打印目标对象左下角的画布坐标
|
||
Debug.Log(pos1.y);
|
||
|
||
// 获取提示面板的大小
|
||
Vector2 ve = tipeRect.sizeDelta;
|
||
// 设置提示面板的宽度
|
||
tipeWidth = ve.x;
|
||
// 如果提示面板的左下角比目标对象的左下角低
|
||
if (tipePos1.y < pos1.y)
|
||
{
|
||
// 设置提示面板的高度
|
||
tipeHeight = ve.y;
|
||
}
|
||
}
|
||
|
||
// 设置遮罩的位置
|
||
SetMaskPosition(new Vector2(pos1.x, pos1.y), new Vector2(pos2.x, pos2.y), tipeWidth, tipeHeight);
|
||
});
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取目标对象的半径
|
||
/// </summary>
|
||
/// <param name="rect">目标对象的 RectTransform 组件</param>
|
||
/// <returns>目标对象的半径</returns>
|
||
public float GetTargetRad(RectTransform rect)
|
||
{
|
||
// 用于存储目标对象的四个角的世界坐标
|
||
Vector3[] _corners = new Vector3[4];
|
||
// 获取目标对象的四个角的世界坐标
|
||
rect.GetWorldCorners(_corners);
|
||
// 计算目标对象的半径,通过计算左下角和右上角的距离并除以 2
|
||
float _radius = Vector2.Distance(WorldToCanvasPos(_corners[0]),
|
||
WorldToCanvasPos(_corners[2])) / 2f;
|
||
// 返回计算得到的半径
|
||
return _radius;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取目标对象的中心点坐标
|
||
/// </summary>
|
||
/// <param name="rect">目标对象的 RectTransform 组件</param>
|
||
/// <returns>目标对象的中心点坐标</returns>
|
||
public Vector2 GetTargetCenter(RectTransform rect)
|
||
{
|
||
// 用于存储目标对象的四个角的世界坐标
|
||
Vector3[] _corners = new Vector3[4];
|
||
// 获取目标对象的四个角的世界坐标
|
||
rect.GetWorldCorners(_corners);
|
||
|
||
// 计算目标对象中心点的 x 坐标
|
||
float x = _corners[0].x + ((_corners[3].x - _corners[0].x) / 2f);
|
||
// 计算目标对象中心点的 y 坐标
|
||
float y = _corners[0].y + ((_corners[1].y - _corners[0].y) / 2f);
|
||
// 创建目标对象中心点的世界坐标
|
||
Vector3 centerWorld = new Vector3(x, y, 0);
|
||
// 将目标对象中心点的世界坐标转换为画布坐标
|
||
Vector2 center = WorldToCanvasPos(centerWorld);
|
||
// 返回目标对象中心点的画布坐标
|
||
return center;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 初始化材质,设置_Origin属性
|
||
/// </summary>
|
||
public void InitializeMaterial()
|
||
{
|
||
if (_materia != null)
|
||
{
|
||
_materia.SetVector("_Origin", new Vector4(-9999, -9999, 9999, 9999));
|
||
_currentOrigin = new Vector4(-9999, -9999, 9999, 9999);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取目标对象的中心点坐标,并进行偏移
|
||
/// </summary>
|
||
/// <param name="rect">目标对象的 RectTransform 组件</param>
|
||
/// <param name="_x">x 轴的偏移量</param>
|
||
/// <param name="_y">y 轴的偏移量</param>
|
||
/// <returns>目标对象偏移后的中心点坐标</returns>
|
||
public Vector2 GetTargetCenter(RectTransform rect, float _x, float _y)
|
||
{
|
||
// 用于存储目标对象的四个角的世界坐标
|
||
Vector3[] _corners = new Vector3[4];
|
||
// 获取目标对象的四个角的世界坐标
|
||
rect.GetWorldCorners(_corners);
|
||
|
||
// 计算目标对象中心点的 x 坐标
|
||
float x = _corners[0].x + ((_corners[3].x - _corners[0].x) / 2f);
|
||
// 计算目标对象中心点的 y 坐标
|
||
float y = _corners[0].y + ((_corners[1].y - _corners[0].y) / 2f);
|
||
// 创建目标对象偏移后的中心点的世界坐标
|
||
Vector3 centerWorld = new Vector3(x + _x, y + _y, 0);
|
||
// 将目标对象偏移后的中心点的世界坐标转换为画布坐标
|
||
Vector2 center = WorldToCanvasPos(centerWorld);
|
||
// 返回目标对象偏移后的中心点的画布坐标
|
||
return center;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将世界坐标转换为画布坐标
|
||
/// </summary>
|
||
/// <param name="world">世界坐标</param>
|
||
/// <returns>画布坐标</returns>
|
||
private Vector2 WorldToCanvasPos(Vector3 world)
|
||
{
|
||
// 如果画布组件为空
|
||
if (null == _canvas)
|
||
// 获取当前游戏对象的画布组件
|
||
_canvas = gameObject.GetComponent<Canvas>();
|
||
// 用于存储转换后的画布坐标
|
||
Vector2 position;
|
||
// 将世界坐标转换为画布坐标
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(_canvas.transform as RectTransform,
|
||
world, _canvas.GetComponent<Camera>(), out position);
|
||
// 返回转换后的画布坐标
|
||
return position;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示引导遮罩
|
||
/// </summary>
|
||
/// <param name="callback">遮罩显示完成后执行的回调函数</param>
|
||
public void ShowGuideMask(Action callback)
|
||
{
|
||
// 标记不显示动画
|
||
ShowTween = false;
|
||
// 如果遮罩的 RectTransform 组件为空
|
||
if (_rectTrans == null)
|
||
{
|
||
// 从资源中加载引导遮罩的预制体
|
||
GameObject obj = Resources.Load<GameObject>("GuideMode/GuideSystem");
|
||
// 实例化引导遮罩预制体
|
||
guide = Instantiate((GameObject)obj, transform);
|
||
|
||
guide.transform.SetSiblingIndex(0);
|
||
|
||
// 获取引导遮罩的 RectTransform 组件
|
||
_rectTrans = guide.GetComponent<RectTransform>();
|
||
// 获取引导遮罩的 RawImage 组件
|
||
_rawImage = guide.GetComponent<RawImage>();
|
||
// 设置引导遮罩图片的颜色为白色且不透明
|
||
_rawImage.color = new Color(1, 1, 1, 1);
|
||
// 获取引导遮罩的材质
|
||
_materia = _rawImage.material;
|
||
// 获取事件穿透组件
|
||
ev = guide.GetComponent<EventPenetrate>();
|
||
|
||
// 执行回调函数
|
||
callback();
|
||
}
|
||
else
|
||
{
|
||
// 执行回调函数
|
||
callback();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将可点击区域设置为空
|
||
/// </summary>
|
||
public void SetTargetNil()
|
||
{
|
||
// 如果事件穿透组件存在
|
||
if (ev != null)
|
||
{
|
||
// 设置事件穿透组件的目标对象为空
|
||
ev.SetTargetImage(null);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 隐藏引导遮罩(不销毁,只是隐藏)
|
||
/// </summary>
|
||
public void HideGuideMask()
|
||
{
|
||
// 只停止跟踪状态,但保留目标对象引用
|
||
_isTrackingTarget = false;
|
||
|
||
// 如果引导遮罩对象存在
|
||
if (guide != null)
|
||
{
|
||
// 隐藏引导遮罩对象
|
||
guide.SetActive(false);
|
||
Debug.Log("引导遮罩已隐藏");
|
||
}
|
||
|
||
// 如果遮罩的 RectTransform 组件存在
|
||
if (rectTransform != null)
|
||
{
|
||
// 隐藏遮罩
|
||
rectTransform.gameObject.SetActive(false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示引导遮罩(重新显示之前隐藏的遮罩)
|
||
/// </summary>
|
||
public void ShowGuideMask()
|
||
{
|
||
// 如果引导遮罩对象存在
|
||
if (guide != null)
|
||
{
|
||
// 显示引导遮罩对象
|
||
guide.SetActive(true);
|
||
Debug.Log("引导遮罩已显示");
|
||
}
|
||
|
||
// 如果遮罩的 RectTransform 组件存在
|
||
if (rectTransform != null)
|
||
{
|
||
// 显示遮罩
|
||
rectTransform.gameObject.SetActive(true);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭引导遮罩
|
||
/// </summary>
|
||
public void CloseGuideMask()
|
||
{
|
||
// 停止跟踪目标对象
|
||
StopTrackingTarget();
|
||
|
||
// 如果引导遮罩对象存在
|
||
if (guide != null)
|
||
{
|
||
// 清空 RawImage 组件引用
|
||
_rawImage = null;
|
||
// 清空 RectTransform 组件引用
|
||
_rectTrans = null;
|
||
// 如果材质存在
|
||
if (_materia != null)
|
||
{
|
||
// 设置材质的 _MaskType 属性为 1
|
||
_materia.SetFloat("_MaskType", 1.0f);
|
||
// 设置材质的 _Origin 属性为零向量
|
||
_materia.SetVector("_Origin", new Vector4(0, 0, 0, 0));
|
||
}
|
||
|
||
// 如果遮罩的 RectTransform 组件存在
|
||
if (rectTransform != null)
|
||
// 设置遮罩的缩放比例为零
|
||
rectTransform.transform.localScale = Vector3.zero;
|
||
// 销毁引导遮罩对象
|
||
Destroy(guide);
|
||
}
|
||
}
|
||
|
||
// 动画时间
|
||
float TweenTime = 0;
|
||
|
||
/// <summary>
|
||
/// 每帧更新时调用
|
||
/// </summary>
|
||
public void Update()
|
||
{
|
||
// 如果正在显示动画
|
||
if (ShowTween)
|
||
{
|
||
// 如果材质存在
|
||
if (_materia != null)
|
||
{
|
||
// 获取材质的 _Origin 属性
|
||
Vector4 mateV4 = _materia.GetVector("_Origin");
|
||
// 如果当前半径小于等于目标半径加 25
|
||
if (mateV4.z <= CurRadNum + 25)
|
||
{
|
||
// 如果动画完成回调函数存在
|
||
if (Tweencallback != null)
|
||
{
|
||
// 执行动画完成回调函数
|
||
Tweencallback();
|
||
}
|
||
|
||
// 标记不显示动画
|
||
ShowTween = false;
|
||
// 退出方法
|
||
return;
|
||
}
|
||
|
||
// 线性插值更新当前半径
|
||
mateV4.z = Mathf.Lerp(mateV4.z, CurRadNum, 15f * Time.deltaTime);
|
||
// 设置材质的 _Origin 属性
|
||
_materia.SetVector("_Origin", mateV4);
|
||
}
|
||
}
|
||
|
||
// 如果回调函数需要被触发
|
||
if (callback)
|
||
{
|
||
// 如果动画完成回调函数存在
|
||
if (Tweencallback != null)
|
||
{
|
||
// 执行动画完成回调函数
|
||
Tweencallback();
|
||
}
|
||
|
||
// 标记回调函数已触发
|
||
callback = false;
|
||
}
|
||
|
||
// 目标位置跟踪更新
|
||
if (_trackingTarget != null)
|
||
{
|
||
// 检查目标UI对象是否激活
|
||
if (!_trackingTarget.activeInHierarchy)
|
||
{
|
||
// 如果目标UI对象未激活,隐藏引导遮罩
|
||
if (_isTrackingTarget)
|
||
{
|
||
if (_trackingTarget.name != "电子表格页面")
|
||
HideGuideMask();
|
||
}
|
||
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// 如果目标UI对象激活但当前没有跟踪,重新开始跟踪
|
||
if (!_isTrackingTarget)
|
||
{
|
||
ShowGuideMask();
|
||
StartTrackingTarget(_trackingTarget);
|
||
}
|
||
|
||
// 如果正在跟踪且位置发生变化,更新位置
|
||
if (_isTrackingTarget && HasTargetPositionChanged())
|
||
{
|
||
UpdateTargetPosition();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 脚本实例被销毁时调用
|
||
/// </summary>
|
||
public void OnDestroy()
|
||
{
|
||
// 关闭引导遮罩
|
||
CloseGuideMask();
|
||
// 如果当前有动画正在播放
|
||
if (currentTween != null)
|
||
{
|
||
// 杀死当前动画
|
||
currentTween.Kill();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置遮罩的位置
|
||
/// </summary>
|
||
/// <param name="pos1">矩形的左下角坐标</param>
|
||
/// <param name="pos2">矩形的右上角坐标</param>
|
||
/// <param name="tipeWidth">提示面板的宽度,默认为 0</param>
|
||
/// <param name="tipeHeight">提示面板的高度,默认为 0</param>
|
||
/// <param name="duration">动画持续时间,默认为 0.3 秒</param>
|
||
/// <param name="onComplete">动画完成后的回调函数,可选参数</param>
|
||
public void SetMaskPosition(Vector2 pos1, Vector2 pos2, float tipeWidth = 0, float tipeHeight = 0, float duration = 0.3f, Action onComplete = null)
|
||
{
|
||
// 如果当前有动画正在播放
|
||
if (currentTween != null && currentTween.IsPlaying())
|
||
{
|
||
// 杀死当前动画
|
||
currentTween.Kill();
|
||
}
|
||
|
||
|
||
// 创建目标原点位置
|
||
Vector4 targetOrigin = new Vector4(pos1.x, pos1.y, pos2.x, pos2.y);
|
||
|
||
// 使用 DOTween 创建动画
|
||
currentTween = DOTween.To(
|
||
() => _currentOrigin,
|
||
(value) =>
|
||
{
|
||
// 更新当前原点位置
|
||
_currentOrigin = value;
|
||
// 设置材质的 _Origin 属性
|
||
_materia.SetVector("_Origin", value);
|
||
},
|
||
targetOrigin,
|
||
duration
|
||
).SetEase(Ease.OutQuad);
|
||
|
||
// 如果动画完成回调函数存在
|
||
if (onComplete != null)
|
||
{
|
||
// 添加动画完成回调函数
|
||
currentTween.OnComplete(() => onComplete());
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 专门处理ScrollRect中UI元素的遮罩定位
|
||
/// </summary>
|
||
/// <param name="target">目标UI对象</param>
|
||
private void HandleScrollRectMask(GameObject target)
|
||
{
|
||
if (target == null || rectTransform == null) return;
|
||
|
||
RectTransform targetRectTrans = target.GetComponent<RectTransform>();
|
||
if (targetRectTrans == null) return;
|
||
|
||
// 获取目标在世界空间的位置和大小
|
||
Vector3[] corners = new Vector3[4];
|
||
targetRectTrans.GetWorldCorners(corners);
|
||
|
||
// 计算中心点和大小
|
||
Vector3 centerWorld = (corners[0] + corners[2]) / 2;
|
||
float width = Vector3.Distance(corners[0], corners[3]);
|
||
float height = Vector3.Distance(corners[0], corners[1]);
|
||
|
||
//Debug.Log($"ScrollRect UI对象: 中心点世界坐标={centerWorld}, 宽度={width}, 高度={height}");
|
||
|
||
// 设置遮罩属性
|
||
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||
|
||
// 直接设置世界位置和大小
|
||
rectTransform.position = centerWorld;
|
||
rectTransform.sizeDelta = new Vector2(width + 10, height + 10);
|
||
|
||
//Debug.Log($"遮罩设置后: 位置={rectTransform.position}, 大小={rectTransform.sizeDelta}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始跟踪目标对象的位置变化
|
||
/// </summary>
|
||
/// <param name="target">要跟踪的目标对象</param>
|
||
public void StartTrackingTarget(GameObject target)
|
||
{
|
||
if (target == null) return;
|
||
|
||
_trackingTarget = target;
|
||
_lastTargetPosition = target.transform.position;
|
||
_lastTargetScale = target.transform.lossyScale;
|
||
_isTrackingTarget = true;
|
||
|
||
Debug.Log($"开始跟踪目标对象: {target.name}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止跟踪目标对象
|
||
/// </summary>
|
||
public void StopTrackingTarget()
|
||
{
|
||
_isTrackingTarget = false;
|
||
_trackingTarget = null;
|
||
Debug.Log("停止跟踪目标对象");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查目标对象是否重新激活,如果是则重新显示引导
|
||
/// </summary>
|
||
public void CheckTargetActiveStatus()
|
||
{
|
||
if (_trackingTarget != null && _trackingTarget.activeInHierarchy)
|
||
{
|
||
// 如果目标对象重新激活,显示引导遮罩
|
||
ShowGuideMask();
|
||
// 重新开始跟踪
|
||
StartTrackingTarget(_trackingTarget);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查目标对象位置是否发生变化
|
||
/// </summary>
|
||
/// <returns>如果位置发生变化返回true,否则返回false</returns>
|
||
private bool HasTargetPositionChanged()
|
||
{
|
||
if (_trackingTarget == null) return false;
|
||
|
||
Vector3 currentPosition = _trackingTarget.transform.position;
|
||
Vector3 currentScale = _trackingTarget.transform.lossyScale;
|
||
|
||
// 检查位置或缩放是否发生变化(使用小的阈值避免浮点精度问题)
|
||
bool positionChanged = Vector3.Distance(currentPosition, _lastTargetPosition) > 0.01f;
|
||
bool scaleChanged = Vector3.Distance(currentScale, _lastTargetScale) > 0.01f;
|
||
|
||
return positionChanged || scaleChanged;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新目标位置并重新计算遮罩位置
|
||
/// </summary>
|
||
private void UpdateTargetPosition()
|
||
{
|
||
if (_trackingTarget == null || rectTransform == null) return;
|
||
|
||
try
|
||
{
|
||
// 更新记录的位置和缩放
|
||
_lastTargetPosition = _trackingTarget.transform.position;
|
||
_lastTargetScale = _trackingTarget.transform.lossyScale;
|
||
|
||
// 获取目标对象的RectTransform组件
|
||
RectTransform targetRect = _trackingTarget.GetComponent<RectTransform>();
|
||
if (targetRect == null) return;
|
||
|
||
// 检查目标对象是否在ScrollRect中
|
||
ScrollRect scrollRect = _trackingTarget.GetComponentInParent<ScrollRect>();
|
||
|
||
if (scrollRect != null)
|
||
{
|
||
// 处理ScrollRect中的UI对象
|
||
HandleScrollRectMask(_trackingTarget);
|
||
}
|
||
else
|
||
{
|
||
// 处理普通UI对象
|
||
Vector3[] corners = new Vector3[4];
|
||
targetRect.GetWorldCorners(corners);
|
||
|
||
// 计算目标对象的实际大小,考虑缩放因素
|
||
Vector2 actualSize = new Vector2(
|
||
targetRect.sizeDelta.x * _lastTargetScale.x,
|
||
targetRect.sizeDelta.y * _lastTargetScale.y
|
||
);
|
||
|
||
// 设置遮罩位置和大小
|
||
rectTransform.SetParent(_trackingTarget.transform);
|
||
rectTransform.anchoredPosition = Vector2.zero;
|
||
rectTransform.sizeDelta = new Vector2(actualSize.x + 10, actualSize.y + 10);
|
||
rectTransform.SetParent(CanvasParent.transform);
|
||
|
||
// 更新按钮位置
|
||
buttonPrefab.anchoredPosition = new Vector2(0, rectTransform.sizeDelta.y / 2);
|
||
}
|
||
|
||
Debug.Log($"目标位置已更新: {_trackingTarget.name}");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"更新目标位置时发生错误: {e.Message}");
|
||
}
|
||
}
|
||
} |