Files
5.24BuYu/Assets/MyGame/aa/UI/DefaultPanel/GuaGuaLe/GuaGuaLeGPU.cs
T
2026-10-08 04:09:31 +08:00

349 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Assets.MyGame.Scripts.Net;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// GPU 刮刮乐(三层架构版)
///
/// 层级结构:
/// RawImage (prizeImage) ← 最下层:奖品图
/// Text (tmp_AwardName) ← 中间层:奖品文字
/// RawImage (scratchImage) ← 最上层:涂层遮罩(挂本脚本)
/// </summary>
public class GuaGuaLeGPU : SubUIBase, IPointerDownHandler, IDragHandler
{
[Header("UI 引用(必填)")]
public RawImage scratchImage; // 最上层:涂层遮罩(挂此脚本)
public GuaGuaLeItem giftPrefab; // 奖品预制体(用于显示奖品图片)
public Transform giftPrefabParent;
public Sprite sprite_Gold;
public Sprite sprite_Diamond;
public Button btn_Get;
[Header("材质(模板)")]
public Material maskMat; // 使用 ScratchMask.shader(涂层遮罩)
public Material eraseMat; // 使用 ScratchErase.shader(擦除)
[Header("涂层颜色")]
public Color coatColor = new Color(0.7f, 0.7f, 0.7f, 1f);
[Header("笔刷")]
[Range(0.01f, 0.3f)] public float brushSize = 0.06f;
[Header("自动揭示")]
[Range(0f, 1f)] public float revealThreshold = 0.6f;
public bool autoReveal = true;
[Header("事件")]
public UnityEngine.Events.UnityEvent onRevealed;
// ---- 运行时 ----
private RenderTexture maskRT;
private Material instanceMaskMat; // 涂层遮罩材质实例
private Material instanceEraseMat; // 擦除材质实例
private int rtWidth = 512;
private int rtHeight = 512;
private string currentAward;
private bool isRevealed;
private bool isScratching;
private Vector2 lastUV;
private bool hasLastUV;
private Texture2D progressReadTex;
private int dragCount;
/// <summary>
/// 刮刮卡面板打开期间,挂起 WebSocket 推送(AssetUpdated/BackpackUpdated)触发的用户数据刷新,
/// 统一在点击"领取"按钮时才刷新用户数据(加金币/钻石/道具)。
/// </summary>
public static bool SuspendWsRefresh;
public override void Awake()
{
base.Awake();
// 领取按钮:刮完后最后一步点击领取时,才刷新用户数据(金币/钻石/道具/刮刮卡数量)
if (btn_Get != null)
btn_Get.onClick.AddListener(OnGetClick);
}
private void OnGetClick()
{
SuspendWsRefresh = false;
string userId = GameManager.Instance?.user?.id;
if (!string.IsNullOrEmpty(userId))
APIRequestList.GetUser(userId, a => GameManager.Instance.UpdataUserData(a));
}
public override void Hide()
{
SuspendWsRefresh = false;
base.Hide();
}
protected override void OnShow(object args = null)
{
base.OnShow(args);
// 打开面板即挂起 WebSocket 推送刷新,数据统一在点击"领取"时更新
SuspendWsRefresh = true;
ResetCard();
}
private void Start()
{
if (scratchImage == null) { Debug.LogError("[GPU] scratchImage 为空!"); return; }
if (maskMat == null) { Debug.LogError("[GPU] maskMat 为空!"); return; }
if (eraseMat == null) { Debug.LogError("[GPU] eraseMat 为空!"); return; }
InitCard();
}
private void InitCard()
{
maskRT = new RenderTexture(rtWidth, rtHeight, 0, RenderTextureFormat.ARGB32)
{
filterMode = FilterMode.Bilinear
};
maskRT.Create();
ClearRT(maskRT, Color.white);
instanceMaskMat = new Material(maskMat);
instanceMaskMat.SetTexture("_MaskTex", maskRT);
instanceMaskMat.SetColor("_CoatColor", coatColor);
scratchImage.material = instanceMaskMat;
scratchImage.texture = null;
instanceEraseMat = new Material(eraseMat);
instanceEraseMat.SetFloat("_BrushSize", brushSize);
progressReadTex = new Texture2D(rtWidth, rtHeight, TextureFormat.ARGB32, false);
ResetState();
// 打开时即抽奖出结果:提前创建奖品显示(涂层下面),加数据统一在最后点击"领取"按钮时进行
FetchAward();
}
private void ResetState()
{
isRevealed = false;
isScratching = false;
hasLastUV = false;
dragCount = 0;
}
// ================================================================
// API 抽奖
// ================================================================
private void FetchAward()
{
APIRequestList.ScratchCardDraw(result =>
{
if (result == null || giftPrefab == null) return;
currentAward = result.prizeName ?? "谢谢参与";
// 清除旧的奖励物品
for (int i = giftPrefabParent.childCount - 1; i >= 0; i--)
Destroy(giftPrefabParent.GetChild(i).gameObject);
var item = Instantiate(giftPrefab, giftPrefabParent);
switch (result.prizeType)
{
case 1: // 金币 → 本地 sprite
item.Init(sprite_Gold, result.count);
break;
case 2: // 钻石 → 本地 sprite
item.Init(sprite_Diamond, result.count);
break;
default: // 道具 → 网络下载图片
string imgUrl =APIManager.baseURL+ result.baseProduct?.imageUrl;
if (!string.IsNullOrEmpty(imgUrl))
item.Init(imgUrl, result.count);
break;
}
// 不在此处刷新用户数据:加金币/钻石/道具统一在最后点击"领取"按钮时进行
});
}
// ================================================================
// 公开方法
// ================================================================
public void ResetCard()
{
btn_Get.gameObject.SetActive(false);
if (maskRT == null) return;
ClearRT(maskRT, Color.white);
ResetState();
if (instanceMaskMat != null)
instanceMaskMat.SetColor("_CoatColor", coatColor);
// 重置后重新抽奖:提前创建新的奖品显示
FetchAward();
}
public void RevealAll()
{
btn_Get.gameObject.SetActive(true);
if (maskRT == null || isRevealed) return;
isRevealed = true;
ClearRT(maskRT, Color.black);
// 刮开揭示时不刷新用户数据:加金币/钻石/道具统一在最后点击"领取"按钮时进行
onRevealed?.Invoke();
}
// ================================================================
// 输入处理
// ================================================================
public void OnPointerDown(PointerEventData e)
{
if (isRevealed) return;
isScratching = true;
if (UVFromPointer(e, out var uv))
{
ScratchAt(uv);
lastUV = uv;
hasLastUV = true;
}
TryAutoReveal();
}
public void OnDrag(PointerEventData e)
{
if (isRevealed || !isScratching) return;
if (!UVFromPointer(e, out var uv)) return;
if (hasLastUV)
{
float dist = Vector2.Distance(uv, lastUV);
int steps = Mathf.Max(1, Mathf.CeilToInt(dist / (brushSize * 0.4f)));
for (int i = 1; i <= steps; i++)
{
float t = (float)i / steps;
var p = Vector2.Lerp(lastUV, uv, t);
ScratchAt(p);
}
}
else
{
ScratchAt(uv);
}
lastUV = uv;
hasLastUV = true;
if (++dragCount % 10 == 0)
TryAutoReveal();
}
private void OnDisable()
{
isScratching = false;
hasLastUV = false;
}
// ================================================================
// 核心擦除(GPU)
// ================================================================
private void ScratchAt(Vector2 uv)
{
instanceEraseMat.SetVector("_BrushPos", new Vector4(uv.x, uv.y, 0, 0));
var temp = RenderTexture.GetTemporary(rtWidth, rtHeight, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(maskRT, temp, instanceEraseMat);
Graphics.Blit(temp, maskRT);
RenderTexture.ReleaseTemporary(temp);
}
// ================================================================
// 进度检测
// ================================================================
private void TryAutoReveal()
{
if (isRevealed || !autoReveal || revealThreshold <= 0f) return;
if (CalcRevealProgress() >= revealThreshold)
RevealAll();
}
public float CalcRevealProgress()
{
if (maskRT == null || progressReadTex == null) return 0f;
RenderTexture prev = RenderTexture.active;
RenderTexture.active = maskRT;
progressReadTex.ReadPixels(new Rect(0, 0, rtWidth, rtHeight), 0, 0, false);
progressReadTex.Apply();
RenderTexture.active = prev;
Color32[] pixels = progressReadTex.GetPixels32();
int total = 0, cleared = 0;
int step = Mathf.Max(1, pixels.Length / 2048);
for (int i = 0; i < pixels.Length; i += step)
{
total++;
if (pixels[i].r < 50) cleared++;
}
return total == 0 ? 0f : (float)cleared / total;
}
// ================================================================
// 工具
// ================================================================
private static void ClearRT(RenderTexture rt, Color color)
{
RenderTexture prev = RenderTexture.active;
RenderTexture.active = rt;
GL.Clear(false, true, color);
RenderTexture.active = prev;
}
private bool UVFromPointer(PointerEventData e, out Vector2 uv)
{
uv = Vector2.zero;
var rt = scratchImage.rectTransform;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
rt, e.position, e.pressEventCamera, out var localPos))
return false;
var rect = rt.rect;
if (rect.width <= 0f || rect.height <= 0f) return false;
uv = new Vector2(
(localPos.x - rect.x) / rect.width,
(localPos.y - rect.y) / rect.height
);
return uv.x >= 0f && uv.x <= 1f && uv.y >= 0f && uv.y <= 1f;
}
public string CurrentAward => currentAward;
public bool IsRevealed => isRevealed;
private void OnDestroy()
{
// 面板销毁时恢复 WebSocket 推送刷新,避免影响其他功能
SuspendWsRefresh = false;
if (maskRT != null) { maskRT.Release(); Destroy(maskRT); }
if (instanceMaskMat != null) Destroy(instanceMaskMat);
if (instanceEraseMat != null) Destroy(instanceEraseMat);
if (progressReadTex != null) Destroy(progressReadTex);
}
}