using UnityEngine; namespace AppleHillsCamera { /// /// Anchors a game object at a fixed distance from a screen edge. /// [ExecuteInEditMode] // Make it run in the editor public class EdgeAnchor : MonoBehaviour { public enum AnchorEdge { Top, Bottom, Left, Right } [Tooltip("Reference marker that defines the screen edges and margins")] public ScreenReferenceMarker referenceMarker; [Tooltip("Which screen edge to anchor to")] public AnchorEdge anchorEdge = AnchorEdge.Top; [Tooltip("Whether to use the predefined margin from the reference marker")] public bool useReferenceMargin = true; [Tooltip("Custom margin to use if not using the reference margin")] public float customMargin = 1f; [Tooltip("Whether to adjust the position automatically on Start")] public bool adjustOnStart = true; [Tooltip("Whether to adjust the position when the screen size changes")] public bool adjustOnScreenResize = true; [Tooltip("Whether to preserve the object's position on other axes")] public bool preserveOtherAxes = true; [Header("Visualization")] [Tooltip("Whether to show the anchor visualization in the editor")] public bool showVisualization = true; [Tooltip("Color for the anchor visualization line")] public Color visualizationColor = new Color(1f, 0f, 0f, 0.8f); private Camera _camera; private int _lastScreenWidth; private int _lastScreenHeight; private Vector3 _originalPosition; private bool _initialized = false; #if UNITY_EDITOR private void OnDrawGizmos() { if (!showVisualization || referenceMarker == null) return; // Draw a line from the object to its anchor point Vector3 anchorPoint = GetAnchorPoint(); // Save original color Color originalColor = Gizmos.color; // Draw line to anchor point Gizmos.color = visualizationColor; Gizmos.DrawLine(gameObject.transform.position, anchorPoint); // Draw a small sphere at the anchor point Gizmos.DrawSphere(anchorPoint, 0.1f); // Restore original color Gizmos.color = originalColor; } #endif private void Awake() { _originalPosition = transform.position; FindCamera(); _lastScreenWidth = Screen.width; _lastScreenHeight = Screen.height; _initialized = true; } private void OnEnable() { if (!_initialized) { _originalPosition = transform.position; FindCamera(); _lastScreenWidth = Screen.width; _lastScreenHeight = Screen.height; _initialized = true; } // Adjust position immediately when enabled in editor #if UNITY_EDITOR if (!Application.isPlaying) { UpdatePosition(); } #endif } private void Start() { if (adjustOnStart && Application.isPlaying) { UpdatePosition(); } } private void Update() { bool shouldUpdate = false; // Update if screen size has changed if (adjustOnScreenResize && (Screen.width != _lastScreenWidth || Screen.height != _lastScreenHeight)) { shouldUpdate = true; _lastScreenWidth = Screen.width; _lastScreenHeight = Screen.height; } // In editor, check for reference marker changes or inspector changes #if UNITY_EDITOR if (!Application.isPlaying) { shouldUpdate = true; } #endif // Update position if needed if (shouldUpdate) { UpdatePosition(); } } private void FindCamera() { // Look for the main camera _camera = Camera.main; // If no main camera found, try to find any camera if (_camera == null) { _camera = FindAnyObjectByType(); } if (_camera == null) { Debug.LogError("EdgeAnchor: No camera found in the scene."); } } /// /// Manually trigger position adjustment based on the anchor settings. /// public void UpdatePosition() { if (referenceMarker == null) { Debug.LogWarning("EdgeAnchor: Missing reference marker."); return; } if (_camera == null) { FindCamera(); if (_camera == null) return; } // Get the margin value to use float margin = GetMarginValue(); // Calculate the new position based on anchor edge Vector3 newPosition = CalculateAnchoredPosition(margin); // If preserving other axes, keep their original values if (preserveOtherAxes) { switch (anchorEdge) { case AnchorEdge.Top: case AnchorEdge.Bottom: newPosition.x = transform.position.x; newPosition.z = transform.position.z; break; case AnchorEdge.Left: case AnchorEdge.Right: newPosition.y = transform.position.y; newPosition.z = transform.position.z; break; } } // Apply the new position transform.position = newPosition; } private float GetMarginValue() { if (!useReferenceMargin) { return customMargin; } switch (anchorEdge) { case AnchorEdge.Top: return referenceMarker.topMargin; case AnchorEdge.Bottom: return referenceMarker.bottomMargin; case AnchorEdge.Left: return referenceMarker.leftMargin; case AnchorEdge.Right: return referenceMarker.rightMargin; default: return customMargin; } } private Vector3 CalculateAnchoredPosition(float margin) { // Get the screen edges in world coordinates float cameraOrthoSize = _camera.orthographicSize; float screenAspect = (float)Screen.width / Screen.height; float screenHeight = cameraOrthoSize * 2f; float screenWidth = screenHeight * screenAspect; Vector3 cameraPosition = _camera.transform.position; Vector3 newPosition = transform.position; switch (anchorEdge) { case AnchorEdge.Top: // Position from the top of the screen newPosition.y = cameraPosition.y + cameraOrthoSize - margin; break; case AnchorEdge.Bottom: // Position from the bottom of the screen newPosition.y = cameraPosition.y - cameraOrthoSize + margin; break; case AnchorEdge.Left: // Position from the left of the screen newPosition.x = cameraPosition.x - (screenWidth / 2f) + margin; break; case AnchorEdge.Right: // Position from the right of the screen newPosition.x = cameraPosition.x + (screenWidth / 2f) - margin; break; } return newPosition; } /// /// Gets the anchor point on the edge for visualization /// private Vector3 GetAnchorPoint() { if (_camera == null) { FindCamera(); if (_camera == null) return transform.position; } // Get the screen edges in world coordinates float cameraOrthoSize = _camera.orthographicSize; float screenAspect = (float)Screen.width / Screen.height; float screenHeight = cameraOrthoSize * 2f; float screenWidth = screenHeight * screenAspect; Vector3 cameraPosition = _camera.transform.position; Vector3 anchorPoint = transform.position; switch (anchorEdge) { case AnchorEdge.Top: // Anchor at top edge with same X as the object anchorPoint.y = cameraPosition.y + cameraOrthoSize; break; case AnchorEdge.Bottom: // Anchor at bottom edge with same X as the object anchorPoint.y = cameraPosition.y - cameraOrthoSize; break; case AnchorEdge.Left: // Anchor at left edge with same Y as the object anchorPoint.x = cameraPosition.x - (screenWidth / 2f); break; case AnchorEdge.Right: // Anchor at right edge with same Y as the object anchorPoint.x = cameraPosition.x + (screenWidth / 2f); break; } return anchorPoint; } } }