117 lines
3.5 KiB
C#
117 lines
3.5 KiB
C#
using TMPro;
|
|
using UnityEngine;
|
|
|
|
namespace AibisDream.MiniGame.Language
|
|
{
|
|
/// <summary>
|
|
/// Gives one TMP object its own hard rectangular clip material. The screen rect is
|
|
/// converted from world corners into the TMP object's local coordinate system every
|
|
/// frame, so clipping remains correct while the text moves, scales or rotates.
|
|
/// </summary>
|
|
[DisallowMultipleComponent]
|
|
public sealed class TMPRectClipper : MonoBehaviour
|
|
{
|
|
private static readonly int ClipRectId = Shader.PropertyToID("_ClipRect");
|
|
|
|
private TMP_Text target;
|
|
private RectTransform screenRect;
|
|
private Material sourceMaterial;
|
|
private Material runtimeMaterial;
|
|
|
|
public void Initialize(TMP_Text text, RectTransform rect)
|
|
{
|
|
target = text;
|
|
screenRect = rect;
|
|
RebuildMaterial();
|
|
UpdateClipRect();
|
|
}
|
|
|
|
public void SetScreenRect(RectTransform rect)
|
|
{
|
|
screenRect = rect;
|
|
UpdateClipRect();
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
UpdateClipRect();
|
|
}
|
|
|
|
public void UpdateClipRect()
|
|
{
|
|
if (target == null || screenRect == null)
|
|
return;
|
|
|
|
if (runtimeMaterial == null)
|
|
RebuildMaterial();
|
|
if (runtimeMaterial == null)
|
|
return;
|
|
|
|
runtimeMaterial.SetVector(ClipRectId, CalculateLocalClipRect(target.transform, screenRect));
|
|
}
|
|
|
|
public static Vector4 CalculateLocalClipRect(Transform textTransform, RectTransform rect)
|
|
{
|
|
if (textTransform == null || rect == null)
|
|
return new Vector4(-32767f, -32767f, 32767f, 32767f);
|
|
|
|
Vector3[] corners = new Vector3[4];
|
|
rect.GetWorldCorners(corners);
|
|
|
|
float minX = float.PositiveInfinity;
|
|
float minY = float.PositiveInfinity;
|
|
float maxX = float.NegativeInfinity;
|
|
float maxY = float.NegativeInfinity;
|
|
|
|
for (int i = 0; i < corners.Length; i++)
|
|
{
|
|
Vector3 local = textTransform.InverseTransformPoint(corners[i]);
|
|
minX = Mathf.Min(minX, local.x);
|
|
minY = Mathf.Min(minY, local.y);
|
|
maxX = Mathf.Max(maxX, local.x);
|
|
maxY = Mathf.Max(maxY, local.y);
|
|
}
|
|
|
|
return new Vector4(minX, minY, maxX, maxY);
|
|
}
|
|
|
|
public void ReleaseMaterial()
|
|
{
|
|
if (target != null && sourceMaterial != null)
|
|
target.fontSharedMaterial = sourceMaterial;
|
|
|
|
if (runtimeMaterial != null)
|
|
{
|
|
if (Application.isPlaying)
|
|
Destroy(runtimeMaterial);
|
|
else
|
|
DestroyImmediate(runtimeMaterial);
|
|
}
|
|
|
|
runtimeMaterial = null;
|
|
sourceMaterial = null;
|
|
}
|
|
|
|
private void RebuildMaterial()
|
|
{
|
|
ReleaseMaterial();
|
|
if (target == null || target.fontSharedMaterial == null)
|
|
return;
|
|
|
|
sourceMaterial = target.fontSharedMaterial;
|
|
runtimeMaterial = new Material(sourceMaterial)
|
|
{
|
|
name = $"{sourceMaterial.name} (Screen Clip)",
|
|
hideFlags = HideFlags.DontSave
|
|
};
|
|
runtimeMaterial.EnableKeyword("MASK_HARD");
|
|
target.fontSharedMaterial = runtimeMaterial;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
ReleaseMaterial();
|
|
}
|
|
}
|
|
}
|