2025-11-05 17:34:40 +08:00

177 lines
4.5 KiB
C#

using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
[ExecuteInEditMode]
public class UIImageAlphaMask : MonoBehaviour
{
private Canvas canvas;
private RectTransform rectTransform;
private Image maskImage;
[SerializeField, HideInInspector]
private Image[] targetImageList;
[SerializeField, Tooltip("需要保证 mask 图边缘预留 1-2 像素空白")]
public Texture2D MaskTex;
private Canvas RootCanvas
{
get
{
if (canvas == null)
{
var canvas = GetComponentInParent<Canvas>();
if (canvas != null)
{
if (canvas.rootCanvas != null)
{
this.canvas = canvas.rootCanvas;
}
else
{
this.canvas = canvas;
}
}
}
return canvas;
}
}
private void Awake()
{
maskImage = GetComponent<Image>();
rectTransform = GetComponent<RectTransform>();
if (targetImageList == null || targetImageList.Length <= 0)
{
targetImageList = GetComponentsInChildren<Image>(true);
targetImageList = targetImageList.Where(o => o.transform != this.transform).ToArray();
}
}
private void Update()
{
#if UNITY_EDITOR
if (!IsValid())
{
return;
}
if (targetImageList == null || targetImageList.Length <= 0)
{
targetImageList = GetComponentsInChildren<Image>(true);
targetImageList = targetImageList.Where(o => o.transform != this.transform).ToArray();
return;
}
#endif
SetProperties();
}
private bool IsValid()
{
if (RootCanvas == null)
{
return false;
}
if (RootCanvas.renderMode == RenderMode.ScreenSpaceOverlay)
{
return false;
}
if(MaskTex == null)
{
#if UNITY_EDITOR
var path = AssetDatabase.GetAssetPath(maskImage.sprite);
if(!string.IsNullOrEmpty(path))
{
MaskTex = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
}
#endif
return false;
}
return true;
}
private void SetProperties()
{
var camera = GetCamera();
if (camera == null)
{
return;
}
var matrix = CalculateMatrix(camera);
for (int i = 0; i < targetImageList.Length; i++)
{
var imgMat = targetImageList[i].material;
imgMat.SetTexture("_Alpha_MaskTex", MaskTex);
imgMat.SetMatrix("_MaskMatrix", matrix);
}
}
private Camera GetCamera()
{
return RootCanvas.worldCamera;
}
private Matrix4x4 CalculateMatrix(Camera camera)
{
var rect = CalculateViewportRect(camera);
Matrix4x4 result = Matrix4x4.identity;
var halfSize = rect.size * 0.5f;
result = Matrix4x4.Ortho(
rect.center.x - halfSize.x,
rect.center.x + halfSize.x,
rect.center.y - halfSize.y,
rect.center.y + halfSize.y,
camera.nearClipPlane,
camera.farClipPlane
);
result = Matrix4x4.TRS(Vector3.one * 0.5f, Quaternion.identity, Vector3.one * 0.5f) * GL.GetGPUProjectionMatrix(result, false) * camera.worldToCameraMatrix;
return result;
}
private Rect CalculateViewportRect(Camera camera)
{
var corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
var p1 = RectTransformUtility.WorldToScreenPoint(camera, corners[1]);
var p3 = RectTransformUtility.WorldToScreenPoint(camera, corners[3]);
var x = p1.x - (Screen.width * 0.5f);
var y = p3.y - (Screen.height * 0.5f);
var width = p3.x - p1.x;
var height = p1.y - p3.y;
x /= (Screen.width * 0.5f);
width /= (Screen.width * 0.5f);
y /= (Screen.height * 0.5f);
height /= (Screen.height * 0.5f);
var size = camera.orthographicSize;
var aspect = Screen.width / (float)Screen.height;
x *= size * aspect;
width *= size * aspect;
y *= size;
height *= size;
var rect = new Rect()
{
x = x,
width = width,
y = y,
height = height
};
return rect;
}
}