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(); } return image; } } private RectTransform RectTransform { get { if (rectTransform == null) { rectTransform = GetComponent(); } 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("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); } } }