UP-Viagg-io/Viagg-io/Assets/UiRoundedCorners/ImageWithIndependentRoundedCorners.cs
2025-04-08 16:57:01 +02:00

149 lines
5.0 KiB
C#

using Nobi.UiRoundedCorners;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace Nobi.UiRoundedCorners {
[ExecuteInEditMode] //Required to do validation with OnEnable()
[DisallowMultipleComponent] //You can only have one of these in every object
[RequireComponent(typeof(RectTransform))]
public class ImageWithIndependentRoundedCorners : MonoBehaviour {
private static readonly int prop_halfSize = Shader.PropertyToID("_halfSize");
private static readonly int prop_radiuses = Shader.PropertyToID("_r");
private static readonly int prop_rect2props = Shader.PropertyToID("_rect2props");
private static readonly int prop_OuterUV = Shader.PropertyToID("_OuterUV");
// Vector2.right rotated clockwise by 45 degrees
private static readonly Vector2 wNorm = new Vector2(.7071068f, -.7071068f);
// Vector2.right rotated counter-clockwise by 45 degrees
private static readonly Vector2 hNorm = new Vector2(.7071068f, .7071068f);
public Vector4 r = new Vector4(40f, 40f, 40f, 40f);
private Material material;
private Vector4 outerUV = new Vector4(0, 0, 1, 1);
// xy - position,
// zw - halfSize
[HideInInspector, SerializeField] private Vector4 rect2props;
[HideInInspector, SerializeField] private MaskableGraphic image;
private void OnValidate() {
Validate();
Refresh();
}
private void OnEnable() {
//You can only add either ImageWithRoundedCorners or ImageWithIndependentRoundedCorners
//It will replace the other component when added into the object.
var other = GetComponent<ImageWithRoundedCorners>();
if (other != null) {
r = Vector4.one * other.radius; //When it does, transfer the radius value to this script
DestroyHelper.Destroy(other);
}
Validate();
Refresh();
}
private void OnRectTransformDimensionsChange() {
if (enabled && material != null) {
Refresh();
}
}
private void OnDestroy() {
if (image != null) {
image.material = null; //This makes so that when the component is removed, the UI material returns to null
}
DestroyHelper.Destroy(material);
image = null;
material = null;
}
public void Validate() {
if (material == null) {
material = new Material(Shader.Find("UI/RoundedCorners/IndependentRoundedCorners"));
}
if (image == null) {
TryGetComponent(out image);
}
if (image != null) {
image.material = material;
}
if (image is Image uiImage && uiImage.sprite != null) {
outerUV = UnityEngine.Sprites.DataUtility.GetOuterUV(uiImage.sprite);
}
}
public void Refresh() {
var rect = ((RectTransform)transform).rect;
RecalculateProps(rect.size);
material.SetVector(prop_rect2props, rect2props);
material.SetVector(prop_halfSize, rect.size * .5f);
material.SetVector(prop_radiuses, r);
material.SetVector(prop_OuterUV, outerUV);
}
private void RecalculateProps(Vector2 size) {
// Vector that goes from left to right sides of rect2
var aVec = new Vector2(size.x, -size.y + r.x + r.z);
// Project vector aVec to wNorm to get magnitude of rect2 width vector
var halfWidth = Vector2.Dot(aVec, wNorm) * .5f;
rect2props.z = halfWidth;
// Vector that goes from bottom to top sides of rect2
var bVec = new Vector2(size.x, size.y - r.w - r.y);
// Project vector bVec to hNorm to get magnitude of rect2 height vector
var halfHeight = Vector2.Dot(bVec, hNorm) * .5f;
rect2props.w = halfHeight;
// Vector that goes from left to top sides of rect2
var efVec = new Vector2(size.x - r.x - r.y, 0);
// Vector that goes from point E to point G, which is top-left of rect2
var egVec = hNorm * Vector2.Dot(efVec, hNorm);
// Position of point E relative to center of coord system
var ePoint = new Vector2(r.x - (size.x / 2), size.y / 2);
// Origin of rect2 relative to center of coord system
// ePoint + egVec == vector to top-left corner of rect2
// wNorm * halfWidth + hNorm * -halfHeight == vector from top-left corner to center
var origin = ePoint + egVec + wNorm * halfWidth + hNorm * -halfHeight;
rect2props.x = origin.x;
rect2props.y = origin.y;
}
}
}
/// <summary>
/// Display Vector4 as 4 separate fields for each corners.
/// It's way easier to use than w,x,y,z in Vector4.
/// </summary>
#if UNITY_EDITOR
[CustomEditor(typeof(ImageWithIndependentRoundedCorners))]
public class Vector4Editor : Editor {
public override void OnInspectorGUI() {
//DrawDefaultInspector();
serializedObject.Update();
SerializedProperty vector4Prop = serializedObject.FindProperty("r");
EditorGUILayout.PropertyField(vector4Prop.FindPropertyRelative("x"), new GUIContent("Top Left Corner"));
EditorGUILayout.PropertyField(vector4Prop.FindPropertyRelative("y"), new GUIContent("Top Right Corner"));
EditorGUILayout.PropertyField(vector4Prop.FindPropertyRelative("w"), new GUIContent("Bottom Left Corner"));
EditorGUILayout.PropertyField(vector4Prop.FindPropertyRelative("z"), new GUIContent("Bottom Right Corner"));
serializedObject.ApplyModifiedProperties();
}
}
#endif