Files
2026-05-26 16:15:54 +08:00

349 lines
9.6 KiB
C#

using UnityEngine;
using UnityEngine.UI;
namespace game
{
[RequireComponent(typeof(RawImage))]
[RequireComponent(typeof(RectTransform))]
public class ScratchCardMaskUGUI : ScratchCardMask
{
private static readonly int OffsetScalePropertyId = Shader.PropertyToID("_OffsetScale");
internal RawImage Image
{
get
{
if (image == null)
{
image = GetComponent<RawImage>();
}
return image;
}
}
private RectTransform RectTransform
{
get
{
if (rectTransform == null)
{
rectTransform = GetComponent<RectTransform>();
}
return rectTransform;
}
}
[Header("Brush")]
[SerializeField]
private Material m_BrushMaterial;
[SerializeField] private Vector2 m_BrushSize = new Vector2(50f, 50f);
[SerializeField]
private int m_InterpolationMaxCount = 4;
[SerializeField]
private int m_InterpolationMinDistance = 8;
[Header("Mask Texture")]
[SerializeField]
private float maskTextureScale = 1.0f;
[SerializeField]
private float maxMaskTextureSize = 512;
private bool baseTextureSaved;
private Material brush;
private RectTransform rectTransform;
private RenderTexture targetTexture;
private Texture baseTexture;
private RawImage image;
private Vector2 prevDrawPosition;
private float m_Progress = 0f;
#region
private Texture2D m_TempTexture;
private void CreateTempTexture()
{
m_TempTexture = ScratchCardUtils.GetReadableCopy(targetTexture);
}
public void UpdateTempTexture()
{
if (m_Threshold <= 0f)
{
//Debug.LogWarning("---- " + gameObject.name + " ok");
return;
}
m_TempTexture.Reinitialize(targetTexture.width, targetTexture.height);
ScratchCardUtils.UpdateTexture2D(m_TempTexture, targetTexture, targetTexture.width, targetTexture.height);
Color[] colorArray = m_TempTexture.GetPixels();
int count = 0;
int total = colorArray.Length;
for (int i = 0; i < total; ++i)
{
if (colorArray[i].a <= m_AlphaThreshold)
{
count++;
}
}
m_Progress = (float)count / total;
//if (m_Progress >= m_Threshold)
//{
// Debug.LogWarning("---- " + gameObject.name + " ok 当前进度为: " + m_Progress);
//}
//else
//{
// Debug.LogWarning("---- " + gameObject.name + " 当前进度为: " + m_Progress);
//}
}
#endregion
#region
private void Awake()
{
SaveBaseTexture();
m_Progress = 0f;
}
private void OnEnable()
{
Reload();
m_Progress = 0f;
}
private void OnDisable()
{
DisposeTargetTexture();
DisposeBrush();
}
private void OnRectTransformDimensionsChange()
{
SaveBaseTexture();
Reload();
m_Progress = 0f;
}
public void UpdateScratchCardMask(Texture texture)
{
baseTexture = texture;
baseTextureSaved = true;
m_Progress = 0f;
}
public void InitMaskUGUI(Material brushMaterial, Vector2 brushSize, int interpolationMaxCount, int interpolationMinDistance)
{
this.m_BrushMaterial = brushMaterial;
this.m_BrushSize = brushSize;
this.m_InterpolationMaxCount = interpolationMaxCount;
this.m_InterpolationMinDistance = interpolationMinDistance;
CreateBrush();
}
#endregion
public void Reload()
{
var rect = RectTransform.rect;
if (rect.width <= 0 || rect.height <= 0)
{
return;
}
AcquireTargetTexture(rect);
}
private void SaveBaseTexture()
{
if (!baseTextureSaved)
{
if (Image.texture != null)
{
baseTexture = Image.texture;
}
baseTextureSaved = true;
}
}
private void CreateBrush()
{
if (m_BrushMaterial == null)
{
m_BrushMaterial = Resources.Load<Material>("Materials/ScratchCard_DefaultBrush");
}
DisposeBrush();
brush = new Material(m_BrushMaterial);
}
private void DisposeBrush()
{
if (brush != null)
{
Destroy(brush);
brush = null;
}
}
private void AcquireTargetTexture(Rect rect)
{
var aspect = rect.height / rect.width;
var width = (int)Mathf.Min(maxMaskTextureSize, rect.width * maskTextureScale);
var height = (int)(width * aspect);
if (targetTexture != null && (targetTexture.width != width || targetTexture.height != height))
{
DisposeTargetTexture();
}
if (targetTexture == null)
{
targetTexture = RenderTexture.GetTemporary(width, height, -1, RenderTextureFormat.ARGB32);
Image.texture = targetTexture;
ClearTexture();
CreateTempTexture();
}
}
private void DisposeTargetTexture()
{
if (targetTexture != null)
{
RenderTexture.ReleaseTemporary(targetTexture);
targetTexture = null;
}
}
private void ClearTargetWithColor(Color color)
{
var tmp = RenderTexture.active;
RenderTexture.active = targetTexture;
GL.Clear(true, true, color);
RenderTexture.active = tmp;
}
private void ClearTexture()
{
if (baseTexture != null)
{
ClearTargetWithColor(Color.clear);
Graphics.Blit(baseTexture, targetTexture, Image.material);
}
else
{
ClearTargetWithColor(Color.white);
}
}
public override float GetRevealProgress()
{
return m_Progress;
}
public bool IsComputeShaderSupported()
{
return false;
}
[ContextMenu("Restore")]
public void Restore()
{
ClearTexture();
m_Progress = 0f;
}
public void OnMaskBeginDrag(Vector2 position)
{
prevDrawPosition = position;
}
public bool OnMaskDrag(Vector2 position)
{
if (targetTexture == null || brush == null)
{
return false;
}
var drawPosition = position;
var progressChanged = false;
var delta = drawPosition - prevDrawPosition;
if (delta != drawPosition)
{
var distance = delta.magnitude;
var count = Mathf.Min(m_InterpolationMaxCount, (int)distance / m_InterpolationMinDistance);
for (int i = 1; i < count; ++i)
{
progressChanged |= DrawAt(prevDrawPosition + delta * ((float)i / count));
}
}
progressChanged |= DrawAt(drawPosition);
prevDrawPosition = drawPosition;
if (progressChanged)
{
//OnRevealProgressChanged();
return true;
}
return false;
}
private bool DrawAt(Vector2 screenPoint)
{
var point = EventScratchTicketConfig.ScreenToGraphicLocalPoint(Image, screenPoint); // 将屏幕坐标转换为Image的本地坐标
var rect = RectTransform.rect;
Vector2 brushScale = new Vector2(m_BrushSize.x / rect.width, m_BrushSize.y / rect.height);
brush.SetVector(OffsetScalePropertyId, CalculateOffsetScale(RectTransform, point, brushScale));
Graphics.Blit(brush.mainTexture, targetTexture, brush);
return true;
}
internal static Vector4 CalculateOffsetScale(RectTransform rt, Vector2 localPoint, Vector2 scale)
{
var rect = rt.rect;
localPoint += rt.pivot * rect.size; // 从左到右,从下到上,距离左下角原点位置
var aspect = rect.height / rect.width;
var sx = scale.x * aspect;
var sy = scale.y;
var px = (localPoint.x / rect.width) - (0.5f * sx);
var py = (localPoint.y / rect.height) - (0.5f * sy);
return new Vector4(px, py, 1.0f / sx, 1.0f / sy);
}
private void OnRevealProgressChangedCallback(ScratchCardMask mask, float progress, bool isRevealed)
{
Debug.LogError("--- mask:" + mask + ", progress:" + progress + ", isRevealed:" + isRevealed);
}
public void DrawInCenter()
{
int width = UnityEngine.Screen.width;
int height = UnityEngine.Screen.height;
Vector2 center = new Vector2(width >> 1, height >> 1);
DrawAt(center);
}
}
}