using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace CurvedUI { [AddComponentMenu("CurvedUI/CurvedUISettings")] [RequireComponent(typeof(Canvas))] public class CurvedUISettings : MonoBehaviour { public enum CurvedUIShape { CYLINDER = 0, RING = 1, SPHERE = 2, CYLINDER_VERTICAL = 3 } [SerializeField] private CurvedUIShape shape; [SerializeField] private float quality = 1f; [SerializeField] private bool interactable = true; [SerializeField] private bool blocksRaycasts = true; [SerializeField] private bool raycastMyLayerOnly = true; [SerializeField] private bool forceUseBoxCollider; [SerializeField] private int angle = 90; [SerializeField] private bool preserveAspect = true; [SerializeField] private int vertAngle = 90; [SerializeField] private float ringFill = 0.5f; [SerializeField] private int ringExternalDiamater = 1000; [SerializeField] private bool ringFlipVertical; private int baseCircleSegments = 16; private Vector2 savedRectSize; private float savedRadius; private Canvas myCanvas; private RectTransform m_rectTransform; private RectTransform RectTransform { get { if (m_rectTransform == null) { m_rectTransform = base.transform as RectTransform; } return m_rectTransform; } } public int BaseCircleSegments { get { return baseCircleSegments; } } public int Angle { get { return angle; } set { if (angle != value) { SetUIAngle(value); } } } public float Quality { get { return quality; } set { if (quality != value) { quality = value; SetUIAngle(angle); } } } public CurvedUIShape Shape { get { return shape; } set { if (shape != value) { shape = value; SetUIAngle(angle); } } } public int VerticalAngle { get { return vertAngle; } set { if (vertAngle != value) { vertAngle = value; SetUIAngle(angle); } } } public float RingFill { get { return ringFill; } set { if (ringFill != value) { ringFill = value; SetUIAngle(angle); } } } public float SavedRadius { get { if (savedRadius == 0f) { savedRadius = GetCyllinderRadiusInCanvasSpace(); } return savedRadius; } } public int RingExternalDiameter { get { return ringExternalDiamater; } set { if (ringExternalDiamater != value) { ringExternalDiamater = value; SetUIAngle(angle); } } } public bool RingFlipVertical { get { return ringFlipVertical; } set { if (ringFlipVertical != value) { ringFlipVertical = value; SetUIAngle(angle); } } } public bool PreserveAspect { get { return preserveAspect; } set { if (preserveAspect != value) { preserveAspect = value; SetUIAngle(angle); } } } public bool Interactable { get { return interactable; } set { interactable = value; } } public bool ForceUseBoxCollider { get { return forceUseBoxCollider; } set { forceUseBoxCollider = value; } } public bool BlocksRaycasts { get { return blocksRaycasts; } set { if (blocksRaycasts != value) { blocksRaycasts = value; if (Application.isPlaying && GetComponent() != null) { GetComponent().RebuildCollider(); } } } } public bool RaycastMyLayerOnly { get { return raycastMyLayerOnly; } set { raycastMyLayerOnly = value; } } public bool PointingAtCanvas { get { if (GetComponent() != null) { return GetComponent().PointingAtCanvas; } Debug.LogWarning("CURVEDUI: Can't check if user is pointing at this canvas - No CurvedUIRaycaster is added to this canvas."); return false; } } public CurvedUIInputModule.CUIControlMethod ControlMethod { get { return CurvedUIInputModule.ControlMethod; } set { CurvedUIInputModule.ControlMethod = value; } } public bool GazeUseTimedClick { get { return CurvedUIInputModule.Instance.GazeUseTimedClick; } set { CurvedUIInputModule.Instance.GazeUseTimedClick = value; } } public float GazeClickTimer { get { return CurvedUIInputModule.Instance.GazeClickTimer; } set { CurvedUIInputModule.Instance.GazeClickTimer = value; } } public float GazeClickTimerDelay { get { return CurvedUIInputModule.Instance.GazeClickTimerDelay; } set { CurvedUIInputModule.Instance.GazeClickTimerDelay = value; } } public float GazeTimerProgress { get { return CurvedUIInputModule.Instance.GazeTimerProgress; } } private void Awake() { if (RaycastMyLayerOnly && base.gameObject.layer == 0) { base.gameObject.layer = 5; } savedRectSize = RectTransform.rect.size; } private void Start() { if (Application.isPlaying) { GraphicRaycaster component = GetComponent(); if (component != null) { if (!(component is CurvedUIRaycaster)) { UnityEngine.Object.Destroy(component); base.gameObject.AddComponent(); } } else { base.gameObject.AddComponent(); } } if (myCanvas == null) { myCanvas = GetComponent(); } savedRadius = GetCyllinderRadiusInCanvasSpace(); } private void OnEnable() { Graphic[] componentsInChildren = GetComponentsInChildren(); foreach (Graphic graphic in componentsInChildren) { graphic.SetAllDirty(); } } private void OnDisable() { Graphic[] componentsInChildren = GetComponentsInChildren(); foreach (Graphic graphic in componentsInChildren) { graphic.SetAllDirty(); } } private void Update() { if (RectTransform.rect.size != savedRectSize) { savedRectSize = RectTransform.rect.size; SetUIAngle(angle); } if (savedRectSize.x == 0f || savedRectSize.y == 0f) { Debug.LogError("CurvedUI: Your Canvas size must be bigger than 0!"); } } private void SetUIAngle(int newAngle) { if (myCanvas == null) { myCanvas = GetComponent(); } if (newAngle == 0) { newAngle = 1; } angle = newAngle; savedRadius = GetCyllinderRadiusInCanvasSpace(); CurvedUIVertexEffect[] componentsInChildren = GetComponentsInChildren(); foreach (CurvedUIVertexEffect curvedUIVertexEffect in componentsInChildren) { curvedUIVertexEffect.SetDirty(); } Graphic[] componentsInChildren2 = GetComponentsInChildren(); foreach (Graphic graphic in componentsInChildren2) { graphic.SetAllDirty(); } if (Application.isPlaying && GetComponent() != null) { GetComponent().RebuildCollider(); } } private Vector3 CanvasToCyllinder(Vector3 pos) { float f = pos.x / savedRectSize.x * (float)Angle * ((float)Math.PI / 180f); pos.x = Mathf.Sin(f) * (SavedRadius + pos.z); pos.z += Mathf.Cos(f) * (SavedRadius + pos.z) - (SavedRadius + pos.z); return pos; } private Vector3 CanvasToCyllinderVertical(Vector3 pos) { float f = pos.y / savedRectSize.y * (float)Angle * ((float)Math.PI / 180f); pos.y = Mathf.Sin(f) * (SavedRadius + pos.z); pos.z += Mathf.Cos(f) * (SavedRadius + pos.z) - (SavedRadius + pos.z); return pos; } private Vector3 CanvasToRing(Vector3 pos) { float num = pos.y.Remap(savedRectSize.y * 0.5f * (float)(RingFlipVertical ? 1 : (-1)), (0f - savedRectSize.y) * 0.5f * (float)(RingFlipVertical ? 1 : (-1)), (float)RingExternalDiameter * (1f - RingFill) * 0.5f, (float)RingExternalDiameter * 0.5f); float f = (pos.x / savedRectSize.x).Remap(-0.5f, 0.5f, (float)Math.PI / 2f, (float)angle * ((float)Math.PI / 180f) + (float)Math.PI / 2f); pos.x = num * Mathf.Cos(f); pos.y = num * Mathf.Sin(f); return pos; } private Vector3 CanvasToSphere(Vector3 pos) { float num = SavedRadius; float num2 = VerticalAngle; if (PreserveAspect) { num2 = (float)angle * (savedRectSize.y / savedRectSize.x); num += ((Angle <= 0) ? pos.z : (0f - pos.z)); } else { num = savedRectSize.x / 2f + pos.z; if (num2 == 0f) { return Vector3.zero; } } float num3 = (pos.x / savedRectSize.x).Remap(-0.5f, 0.5f, (float)(180 - angle) / 2f - 90f, 180f - (float)(180 - angle) / 2f - 90f); num3 *= (float)Math.PI / 180f; float num4 = (pos.y / savedRectSize.y).Remap(-0.5f, 0.5f, (180f - num2) / 2f, 180f - (180f - num2) / 2f); num4 *= (float)Math.PI / 180f; pos.z = Mathf.Sin(num4) * Mathf.Cos(num3) * num; pos.y = (0f - num) * Mathf.Cos(num4); pos.x = Mathf.Sin(num4) * Mathf.Sin(num3) * num; if (PreserveAspect) { pos.z -= num; } return pos; } public void AddEffectToChildren() { Graphic[] componentsInChildren = GetComponentsInChildren(true); foreach (Graphic graphic in componentsInChildren) { if (graphic.GetComponent() == null) { graphic.gameObject.AddComponent(); graphic.SetAllDirty(); } } InputField[] componentsInChildren2 = GetComponentsInChildren(true); foreach (InputField inputField in componentsInChildren2) { if (inputField.GetComponent() == null) { inputField.gameObject.AddComponent(); } } } public Vector3 VertexPositionToCurvedCanvas(Vector3 pos) { switch (Shape) { case CurvedUIShape.CYLINDER: return CanvasToCyllinder(pos); case CurvedUIShape.CYLINDER_VERTICAL: return CanvasToCyllinderVertical(pos); case CurvedUIShape.RING: return CanvasToRing(pos); case CurvedUIShape.SPHERE: return CanvasToSphere(pos); default: return Vector3.zero; } } public Vector3 CanvasToCurvedCanvas(Vector3 pos) { pos = VertexPositionToCurvedCanvas(pos); if (float.IsNaN(pos.x) || float.IsInfinity(pos.x)) { return Vector3.zero; } return base.transform.localToWorldMatrix.MultiplyPoint3x4(pos); } public Vector3 CanvasToCurvedCanvasNormal(Vector3 pos) { pos = VertexPositionToCurvedCanvas(pos); switch (Shape) { case CurvedUIShape.CYLINDER: return base.transform.localToWorldMatrix.MultiplyVector((pos - new Vector3(0f, 0f, 0f - GetCyllinderRadiusInCanvasSpace())).ModifyY(0f)).normalized; case CurvedUIShape.CYLINDER_VERTICAL: return base.transform.localToWorldMatrix.MultiplyVector((pos - new Vector3(0f, 0f, 0f - GetCyllinderRadiusInCanvasSpace())).ModifyX(0f)).normalized; case CurvedUIShape.RING: return -base.transform.forward; case CurvedUIShape.SPHERE: { Vector3 vector = ((!PreserveAspect) ? Vector3.zero : new Vector3(0f, 0f, 0f - GetCyllinderRadiusInCanvasSpace())); return base.transform.localToWorldMatrix.MultiplyVector(pos - vector).normalized; } default: return Vector3.zero; } } public bool RaycastToCanvasSpace(Ray ray, out Vector2 o_positionOnCanvas) { CurvedUIRaycaster component = GetComponent(); o_positionOnCanvas = Vector2.zero; switch (Shape) { case CurvedUIShape.CYLINDER: return component.RaycastToCyllinderCanvas(ray, out o_positionOnCanvas, true); case CurvedUIShape.CYLINDER_VERTICAL: return component.RaycastToCyllinderVerticalCanvas(ray, out o_positionOnCanvas, true); case CurvedUIShape.RING: return component.RaycastToRingCanvas(ray, out o_positionOnCanvas, true); case CurvedUIShape.SPHERE: return component.RaycastToSphereCanvas(ray, out o_positionOnCanvas, true); default: return false; } } public float GetCyllinderRadiusInCanvasSpace() { float num = ((!PreserveAspect) ? (RectTransform.rect.size.x * 0.5f / Mathf.Sin(Mathf.Clamp(angle, -180f, 180f) * 0.5f * ((float)Math.PI / 180f))) : ((shape != CurvedUIShape.CYLINDER_VERTICAL) ? (RectTransform.rect.size.x / ((float)Math.PI * 2f * ((float)angle / 360f))) : (RectTransform.rect.size.y / ((float)Math.PI * 2f * ((float)angle / 360f))))); return (angle != 0) ? num : 0f; } public Vector2 GetTesslationSize(bool modifiedByQuality = true) { Vector2 size = RectTransform.rect.size; if (Angle != 0 || (!PreserveAspect && vertAngle != 0)) { switch (shape) { case CurvedUIShape.CYLINDER: case CurvedUIShape.RING: case CurvedUIShape.CYLINDER_VERTICAL: size /= GetSegmentsByAngle(angle); break; case CurvedUIShape.SPHERE: size.x /= GetSegmentsByAngle(angle); if (PreserveAspect) { size.y = size.x * RectTransform.rect.size.y / RectTransform.rect.size.x; } else { size.y /= GetSegmentsByAngle(VerticalAngle); } break; } } return size / ((!modifiedByQuality) ? 1f : Mathf.Clamp(Quality, 0.01f, 10f)); } private float GetSegmentsByAngle(float angle) { if (angle.Abs() <= 1f) { return 1f; } if (angle.Abs() < 90f) { return (float)baseCircleSegments * Mathf.Abs(angle).Remap(0f, 90f, 0.01f, 0.5f); } return (float)baseCircleSegments * Mathf.Abs(angle).Remap(90f, 360f, 0.5f, 1f); } public void SetAllChildrenDirty(bool recalculateCurveOnly = false) { CurvedUIVertexEffect[] componentsInChildren = GetComponentsInChildren(); foreach (CurvedUIVertexEffect curvedUIVertexEffect in componentsInChildren) { if (recalculateCurveOnly) { curvedUIVertexEffect.SetDirty(); } else { curvedUIVertexEffect.CurvingRequired = true; } } } public void Click() { if (GetComponent() != null) { GetComponent().Click(); } } public List GetObjectsUnderPointer() { if (GetComponent() != null) { return GetComponent().GetObjectsUnderPointer(); } return new List(); } public List GetObjectsUnderScreenPos(Vector2 pos, Camera eventCamera = null) { if (eventCamera == null) { eventCamera = myCanvas.worldCamera; } if (GetComponent() != null) { return GetComponent().GetObjectsUnderScreenPos(pos, eventCamera); } return new List(); } public List GetObjectsHitByRay(Ray ray) { if (GetComponent() != null) { return GetComponent().GetObjectsHitByRay(ray); } return new List(); } public void ActivateCurvedUIComponents(bool activate) { List list = new List(); list.AddRange(GetComponentsInChildren(true)); for (int i = 0; i < list.Count; i++) { list[i].enabled = activate; } List list2 = new List(); list2.AddRange(GetComponentsInChildren(true)); for (int j = 0; j < list2.Count; j++) { list2[j].enabled = activate; } List list3 = new List(); list3.AddRange(GetComponentsInChildren(true)); for (int k = 0; k < list3.Count; k++) { if (list3[k].GetComponent() != null) { list3[k].GetComponent().SetAllDirty(); } list3[k].enabled = activate; } List list4 = new List(); list4.AddRange(GetComponents()); for (int l = 0; l < list4.Count; l++) { list4[l].enabled = activate; } base.enabled = activate; } public void RemoveCurvedUIComponents() { List list = new List(); list.AddRange(GetComponentsInChildren(true)); for (int i = 0; i < list.Count; i++) { UnityEngine.Object.DestroyImmediate(list[i]); } List list2 = new List(); list2.AddRange(GetComponentsInChildren(true)); for (int j = 0; j < list2.Count; j++) { UnityEngine.Object.DestroyImmediate(list2[j]); } List list3 = new List(); list3.AddRange(GetComponentsInChildren(true)); for (int k = 0; k < list3.Count; k++) { if (list3[k].GetComponent() != null) { list3[k].GetComponent().SetAllDirty(); } UnityEngine.Object.DestroyImmediate(list3[k]); } List list4 = new List(); list4.AddRange(GetComponents()); for (int l = 0; l < list4.Count; l++) { UnityEngine.Object.DestroyImmediate(list4[l]); } UnityEngine.Object.DestroyImmediate(this); } } }