// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using UnityEngine; using static UnityEngine.Mathf; // ReSharper disable UnusedMember.Global // ReSharper disable MemberCanBePrivate.Global namespace Doozy.Runtime.Reactor { public static class ReactorUtils { public static MoveDirection Reverse(this MoveDirection target) { switch (target) { case MoveDirection.Left: return MoveDirection.Right; case MoveDirection.Top: return MoveDirection.Bottom; case MoveDirection.Right: return MoveDirection.Left; case MoveDirection.Bottom: return MoveDirection.Top; case MoveDirection.TopLeft: return MoveDirection.BottomRight; case MoveDirection.TopCenter: return MoveDirection.BottomCenter; case MoveDirection.TopRight: return MoveDirection.BottomLeft; case MoveDirection.MiddleLeft: return MoveDirection.MiddleRight; case MoveDirection.MiddleCenter: return MoveDirection.MiddleCenter; case MoveDirection.MiddleRight: return MoveDirection.MiddleLeft; case MoveDirection.BottomLeft: return MoveDirection.TopRight; case MoveDirection.BottomCenter: return MoveDirection.TopCenter; case MoveDirection.BottomRight: return MoveDirection.TopLeft; case MoveDirection.CustomPosition: return MoveDirection.CustomPosition; default: throw new ArgumentOutOfRangeException(nameof(target), target, null); } } /// Get the 'to' (end) position por a move out (hide) animation /// Target RectTransform /// Direction to where the animation goes to /// From (start) position for the move out (hide) animation public static Vector3 GetMoveOutPosition(RectTransform target, MoveDirection moveToDirection, Vector3 fromPosition) => GetTargetPosition(target, moveToDirection, fromPosition, target.localScale, target.localEulerAngles); /// Get the 'to' (end) position por a move out (hide) animation /// Target RectTransform /// Direction to where the animation goes to /// From (start) position for the move out (hide) animation /// If there is a to local scale (whe UIAnimation modifies the target's scale) the 'to' scale value needs to be passed /// If there is a 'to' local rotation (when UIAnimation modifies the target's rotation) the 'to' rotation value needs to be passed public static Vector3 GetMoveOutPosition(RectTransform target, MoveDirection moveToDirection, Vector3 fromPosition, Vector3 toLocalScale, Vector3 toLocalEulerAngles) => GetTargetPosition(target, moveToDirection, fromPosition, toLocalScale, toLocalEulerAngles); /// Get the 'from' (start) position for a move in (show) animation /// Target RectTransform /// Direction from where the animation comes from /// To (end) position for a move in (show) animation public static Vector3 GetMoveInPosition(RectTransform target, MoveDirection moveFromDirection, Vector3 toValue) => GetTargetPosition(target, moveFromDirection, toValue, target.localScale, target.localEulerAngles); /// Get the 'from' (start) position for a move in (show) animation /// Target RectTransform /// Direction from where the animation comes from /// To (end) position for a move in (show) animation /// If there is a 'from' local scale (when UIAnimation modifies the target's scale) the 'from' scale value needs to be passed /// If there is a 'from' local rotation (when UIAnimation modifies the target's rotation) the 'from' rotation value needs to be passed public static Vector3 GetMoveInPosition(RectTransform target, MoveDirection moveFromDirection, Vector3 toValue, Vector3 fromLocalScale, Vector3 fromLocalEulerAngles) => GetTargetPosition(target, moveFromDirection, toValue, fromLocalScale, fromLocalEulerAngles); public static Vector3 GetTargetPosition(RectTransform target, MoveDirection moveDirection, Vector3 startPosition, Vector3 targetLocalScale, Vector3 targetLocalEulerAngles) { if (target == null) return Vector3.zero; //null target // Canvas canvas = target.GetComponent(); //get canvas // if (canvas != null && canvas == canvas.rootCanvas) return Vector3.zero; //found canvas, but it's root canvas (cannot calculate) RectTransform parent = target.parent.GetComponent(); //get parent RectTransform parent.ForceUpdateRectTransforms(); Rect parentRect = parent.rect; Rect targetRect = target.rect; Vector2 targetPivot = target.pivot; //calculate xOffset float xOffset = 0; switch (moveDirection) { case MoveDirection.Left: case MoveDirection.TopLeft: case MoveDirection.MiddleLeft: case MoveDirection.BottomLeft: xOffset = targetRect.width * targetLocalScale.x * (1f - targetPivot.x) + parentRect.width * (1f - targetPivot.x) * target.anchorMin.x + parentRect.width * targetPivot.x * target.anchorMax.x; break; case MoveDirection.Right: case MoveDirection.TopRight: case MoveDirection.MiddleRight: case MoveDirection.BottomRight: xOffset = parentRect.width + targetRect.width * targetLocalScale.x * targetPivot.x - parentRect.width * (1f - targetPivot.x) * target.anchorMin.x - parentRect.width * targetPivot.x * target.anchorMax.x; break; } //calculate yOffset float yOffset = 0; switch (moveDirection) { case MoveDirection.Top: case MoveDirection.TopLeft: case MoveDirection.TopCenter: case MoveDirection.TopRight: yOffset = parentRect.height + targetRect.height * targetLocalScale.y * targetPivot.y - parentRect.height * (1f - targetPivot.y) * target.anchorMin.y - parentRect.height * targetPivot.y * target.anchorMax.y; break; case MoveDirection.Bottom: case MoveDirection.BottomLeft: case MoveDirection.BottomCenter: case MoveDirection.BottomRight: yOffset = targetRect.height * targetLocalScale.y * (1f - targetPivot.y) + parentRect.height * (1f - targetPivot.y) * target.anchorMin.y + parentRect.height * targetPivot.y * target.anchorMax.y; break; } //return calculated position float x = startPosition.x; float y = startPosition.y; float z = startPosition.z; Vector3 position; float xDirection = 0; float yDirection = 0; switch (moveDirection) { case MoveDirection.Left: position = new Vector3(-xOffset, y, z); xDirection = -1; yDirection = 0; break; case MoveDirection.Right: position = new Vector3(xOffset, y, z); xDirection = 1; yDirection = 0; break; case MoveDirection.Top: position = new Vector3(x, yOffset, z); xDirection = 0; yDirection = 1; break; case MoveDirection.Bottom: position = new Vector3(x, -yOffset, z); xDirection = 0; yDirection = -1; break; case MoveDirection.TopLeft: position = new Vector3(-xOffset, yOffset, z); xDirection = -1; yDirection = 1; break; case MoveDirection.TopCenter: position = new Vector3(0, yOffset, z); xDirection = 0; yDirection = 1; break; case MoveDirection.TopRight: position = new Vector3(xOffset, yOffset, z); xDirection = 1; yDirection = 1; break; case MoveDirection.MiddleLeft: position = new Vector3(-xOffset, 0, z); xDirection = -1; yDirection = 0; break; case MoveDirection.MiddleCenter: position = new Vector3(0, 0, z); xDirection = 0; yDirection = 0; break; case MoveDirection.MiddleRight: position = new Vector3(xOffset, 0, z); xDirection = 1; yDirection = 0; break; case MoveDirection.BottomLeft: position = new Vector3(-xOffset, -yOffset, z); xDirection = -1; yDirection = -1; break; case MoveDirection.BottomCenter: position = new Vector3(0, -yOffset, z); xDirection = 0; yDirection = -1; break; case MoveDirection.BottomRight: position = new Vector3(xOffset, -yOffset, z); xDirection = 1; yDirection = -1; break; // ReSharper disable once RedundantCaseLabel case MoveDirection.CustomPosition: default: position = startPosition; break; } if (Approximately(0, targetLocalEulerAngles.z)) return position; //Rotation offset calculation //https://en.wikipedia.org/wiki/Rotation_(mathematics)#Two_dimensions //https://iiif.io/api/annex/notes/rotation/ float angle = Abs(targetLocalEulerAngles.z % 180); float theta = angle * Deg2Rad; float width = targetRect.width * targetLocalScale.x; float height = targetRect.height * targetLocalScale.y; float newWidth; float newHeight; if (Approximately(angle, 0) || Approximately(angle, 90)) { newWidth = width; newHeight = height; } else if (angle < 90) { newWidth = width * Cos(theta) + height * Sin(theta); newHeight = width * Sin(theta) + height * Cos(theta); } else { angle -= 90; theta = angle * Deg2Rad; newWidth = height * Cos(theta) + width * Sin(theta); newHeight = height * Sin(theta) + width * Cos(theta); } float offsetX = (newWidth - width) / 2f; float offsetY = (newHeight - height) / 2f; position = new Vector3 ( position.x + offsetX * xDirection, position.y + offsetY * yDirection, position.z ); return position; //ToDo: re-check pivot options for rotation - it only works in limited cases (pivot should be x: 0.5f y: 0.5f to work as expected) } } }