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] public Texture2D MaskTex; private Canvas RootCanvas { get { if (canvas == null) { var canvas = GetComponentInParent(); if (canvas != null) { if (canvas.rootCanvas != null) { this.canvas = canvas.rootCanvas; } else { this.canvas = canvas; } } } return canvas; } } private void Awake() { maskImage = GetComponent(); rectTransform = GetComponent(); if (targetImageList == null || targetImageList.Length <= 0) { targetImageList = GetComponentsInChildren(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(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(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; } }