311 lines
10 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|