Add a system for setting up "real" camera framing to work between devices (#26)

- In-level authoring utility to designate level bounds
- Camera Adapter component to be placed on a level's camera to perform the adjustments
- EdgeAnchor tool, which allows anchoring of game objects to the screen bounds

Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com>
Reviewed-on: #26
This commit is contained in:
2025-10-14 04:56:00 +00:00
parent aefff3d050
commit 2573e7f80e
12 changed files with 1261 additions and 134 deletions

View File

@@ -1,6 +1,7 @@
using UnityEngine;
using AppleHills.Core.Settings;
using Input;
using AppleHillsCamera;
namespace Minigames.DivingForPictures
{
@@ -10,6 +11,10 @@ namespace Minigames.DivingForPictures
/// </summary>
public class PlayerController : MonoBehaviour, ITouchInputConsumer
{
[Tooltip("Reference to the edge anchor that this player should follow for Y position")]
[SerializeField] private EdgeAnchor edgeAnchor;
// Settings reference
private IDivingMinigameSettings _settings;
@@ -42,6 +47,38 @@ namespace Minigames.DivingForPictures
_targetFingerX = transform.position.x;
_isTouchActive = false;
// Try to find edge anchor if not assigned
if (edgeAnchor == null)
{
// First try to find edge anchor on the same object or parent
edgeAnchor = GetComponentInParent<EdgeAnchor>();
// If not found, find any edge anchor in the scene
if (edgeAnchor == null)
{
edgeAnchor = FindObjectOfType<EdgeAnchor>();
if (edgeAnchor == null)
{
Debug.LogWarning("[PlayerController] No EdgeAnchor found in scene. Origin Y position won't update with camera changes.");
}
else
{
Debug.Log($"[PlayerController] Auto-connected to EdgeAnchor on {edgeAnchor.gameObject.name}");
}
}
}
// Subscribe to edge anchor events if it exists
if (edgeAnchor != null)
{
// Unsubscribe first to prevent duplicate subscriptions
edgeAnchor.OnPositionUpdated -= UpdateOriginYFromAnchor;
edgeAnchor.OnPositionUpdated += UpdateOriginYFromAnchor;
// Update origin Y based on current anchor position
UpdateOriginYFromAnchor();
}
DivingGameManager.Instance.OnGameInitialized += Initialize;
// If game is already initialized, initialize immediately
@@ -70,6 +107,12 @@ namespace Minigames.DivingForPictures
private void OnDestroy()
{
DivingGameManager.Instance.OnGameInitialized -= Initialize;
// Unsubscribe from edge anchor events
if (edgeAnchor != null)
{
edgeAnchor.OnPositionUpdated -= UpdateOriginYFromAnchor;
}
}
/// <summary>
@@ -185,5 +228,31 @@ namespace Minigames.DivingForPictures
}
transform.position = new Vector3(newX, newY, transform.position.z);
}
/// <summary>
/// Updates the origin Y position based on camera adjustments
/// </summary>
public void UpdateOriginY(float newOriginY)
{
_originY = newOriginY;
}
/// <summary>
/// Updates the origin Y position based on the current position of the player
/// This method is intended to be called by the camera adapter when the camera is adjusted.
/// </summary>
private void UpdateOriginYFromCurrentPosition()
{
_originY = transform.position.y;
}
/// <summary>
/// Updates the origin Y position based on the current position of the edge anchor
/// This method is intended to be called by the edge anchor when its position is updated.
/// </summary>
private void UpdateOriginYFromAnchor()
{
_originY = edgeAnchor.transform.position.y;
}
}
}

View File

@@ -16,12 +16,43 @@ public class RockFollower : MonoBehaviour
/// </summary>
public bool useWobbleOffset = true;
/// <summary>
/// The base Y position for the rock.
/// The vertical distance between the rock and the bottle.
/// </summary>
public float baseY = -6f;
[SerializeField] private float verticalDistance = 6f;
private float velocityX; // For SmoothDamp
#if UNITY_EDITOR
/// <summary>
/// Called in editor when properties are changed.
/// Updates the object's position when verticalDistance is modified.
/// </summary>
private void OnValidate()
{
// Only update position if playing or in prefab mode
if (Application.isPlaying || UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this))
return;
if (bottleTransform != null)
{
// Calculate the new Y position based on bottle's position and the updated verticalDistance
float newY = bottleTransform.position.y - verticalDistance;
// Apply the wobble offset if enabled
if (useWobbleOffset && bottleWobble != null)
{
newY += bottleWobble.VerticalOffset;
}
// Update only the Y position, keeping X and Z unchanged
transform.position = new Vector3(transform.position.x, newY, transform.position.z);
// Mark the scene as dirty to ensure changes are saved
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene());
}
}
#endif
void Update()
{
if (bottleTransform == null) return;
@@ -33,8 +64,8 @@ public class RockFollower : MonoBehaviour
// Smoothly follow bottle's X with stiffer motion
float newX = Mathf.SmoothDamp(currentX, targetX, ref velocityX, 1f / followStiffness);
// Calculate Y position
float newY = baseY;
// Calculate Y position based on bottle's position and vertical distance
float newY = bottleTransform.position.y - verticalDistance;
if (useWobbleOffset && bottleWobble != null)
{
newY += bottleWobble.VerticalOffset;