using System; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using UnityEngine.Events; [RequireComponent(typeof(RawImage))] public class ScratchTicket : MonoBehaviour { public Camera uiCamera { get { return UIManager.Instance().UICamera; } } /// /// 笔刷半径,单位为Ui长度 /// public float radius = 20f; /// /// RenderTexture尺寸比例 /// public float renderTexRadio = 0.5f; public event UnityAction onCleanRatioUpdate; /// /// 当前清空程度 /// private float _cleanPixel; public float cleanRatio { get { if (_renderTexture == null) return 0f; else { var pixelCount = _renderTexture.width * _renderTexture.height; return pixelCount > 0 ? _cleanPixel / pixelCount : 1f; } } } private RawImage _rawImage; private Texture2D _renderTexture; private Material _renderMaterial; private Vector2 _min; private Vector2 _max; private bool _start; private void Awake() { _rawImage = GetComponent(); var rect = _rawImage.rectTransform.rect; _min = rect.min; _max = rect.max; var dragComponent = gameObject.EnsureComponent(); dragComponent.onDrag += OnDrag; } private void OnEnable() { if (_start) Reset(); } private void Start() { if (renderTexRadio <= 0f) renderTexRadio = 1f; if (_renderTexture == null) { var width = Mathf.FloorToInt((_max.x - _min.x) * renderTexRadio); var height = Mathf.FloorToInt((_max.y - _min.y) * renderTexRadio); _renderTexture = new Texture2D(width, height, TextureFormat.ARGB32, false); } _renderMaterial = _rawImage.material; if (_renderMaterial != null) { _renderMaterial = Instantiate(_renderMaterial); _rawImage.material = _renderMaterial; _renderMaterial.SetTexture("_ScratchTex", _renderTexture); } _start = true; OnEnable(); } private void OnDestroy() { if (_renderMaterial != null) Destroy(_renderMaterial); if (_renderTexture != null) Destroy(_renderTexture); } public void OnDrag(Vector2 lastPoint, Vector2 currentPoint) { var pixelRadius = radius / (_max.x - _min.x) * _renderTexture.width; if (pixelRadius > 0) { Vector2 start; Vector2 end; if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_rawImage.rectTransform, lastPoint, uiCamera, out start) && RectTransformUtility.ScreenPointToLocalPointInRectangle(_rawImage.rectTransform, currentPoint, uiCamera, out end)) { var radiusSqr = pixelRadius * pixelRadius; var width = _renderTexture.width; var height = _renderTexture.height; start = LocalPointToPixelPos(start); end = LocalPointToPixelPos(end); var dirty = false; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var distSqr = DistanceSqrToLine(new Vector2(x, y), start, end); if (distSqr < radiusSqr) { var ratio = distSqr / radiusSqr; if (ratio < 0.1f) ratio = 0f; var color = _renderTexture.GetPixel(x, y); if (color.a > ratio) { dirty = true; _cleanPixel += color.a - ratio; color.a = ratio; _renderTexture.SetPixel(x, y, color); } } } } if (dirty) { _renderTexture.Apply(); if (onCleanRatioUpdate != null) onCleanRatioUpdate.Invoke(cleanRatio); } } } } private Vector2 LocalPointToPixelPos(Vector2 localPoint) { var uv = new Vector2( (localPoint.x - _min.x) / (_max.x - _min.x), (localPoint.y - _min.y) / (_max.y - _min.y)); uv.x = uv.x * _renderTexture.width; uv.y = uv.y * _renderTexture.height; return uv; } private float DistanceSqrToLine(Vector2 point, Vector2 start, Vector2 end) { var a = DistanceSqrToPoint(point, start); if (a < 0.0001f) return 0f; var b = DistanceSqrToPoint(point, end); if (b < 0.0001f) return 0f; var c = DistanceSqrToPoint(start, end); if (c < 0.0001f) return a; if (a > b + c) return b; if (b > a + c) return a; var la = Mathf.Sqrt(a); var lb = Mathf.Sqrt(b); var lc = Mathf.Sqrt(c); var l = (la + lb + lc) * 0.5f; l = l * (l - la) * (l - lb) * (l - lc); return 4 * l / c; } private float DistanceSqrToPoint(Vector2 point, Vector2 end) { return (point.x - end.x) * (point.x - end.x) + (point.y - end.y) * (point.y - end.y); } public void Reset() { _cleanPixel = 0f; if (_renderTexture != null) { var colors = _renderTexture.GetPixels(); for (var i = 0; i < colors.Length; i++) { var color = colors[i]; color.a = 1f; colors[i] = color; } _renderTexture.SetPixels(colors); _renderTexture.Apply(); } } }