207 lines
6.0 KiB
C#
207 lines
6.0 KiB
C#
|
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; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 笔刷半径,单位为Ui长度
|
|||
|
/// </summary>
|
|||
|
public float radius = 20f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// RenderTexture尺寸比例
|
|||
|
/// </summary>
|
|||
|
public float renderTexRadio = 0.5f;
|
|||
|
|
|||
|
public event UnityAction<float> onCleanRatioUpdate;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 当前清空程度
|
|||
|
/// </summary>
|
|||
|
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<RawImage>();
|
|||
|
var rect = _rawImage.rectTransform.rect;
|
|||
|
_min = rect.min;
|
|||
|
_max = rect.max;
|
|||
|
var dragComponent = gameObject.EnsureComponent<UiDragComponent>();
|
|||
|
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();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|