Files
AppleHillsProduction/Assets/Scripts/AppleHillsCamera/EdgeAnchor.cs
2025-10-13 15:34:07 +02:00

311 lines
10 KiB
C#

using UnityEngine;
namespace AppleHillsCamera
{
/// <summary>
/// Anchors a game object at a fixed distance from a screen edge.
/// </summary>
[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<Camera>();
}
if (_camera == null)
{
Debug.LogError("EdgeAnchor: No camera found in the scene.");
}
}
/// <summary>
/// Manually trigger position adjustment based on the anchor settings.
/// </summary>
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;
}
/// <summary>
/// Gets the anchor point on the edge for visualization
/// </summary>
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;
}
}
}