add camera rig, click-to-move locomotion, and click interaction

Camera: Cinemachine-based rig with an anchor, a state director, point-of-
interest framing, pan input, and dialogue framing volumes.

Player: click-to-move over the NavMesh, a pointer input layer, and a motion
arbiter that reconciles direct stick input with navigation-driven movement.
The pure math is isolated in NightclubArcadia.Locomotion.Math with an EditMode
suite, so the arbitration rules are testable without a scene.

Interaction: IClickInteractable plus a bridge that routes pointer clicks to it.

Supporting project settings: adds the Cinemachine package, the Interactable and
Ground tags the raycasts depend on, and NavMesh agent dimensions matched to the
player capsule (radius 0.3, height 1.8, climb 0.25).

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-25 19:03:18 +02:00
co-authored by Claude Opus 5
parent 1ef1720bae
commit 932cd0582f
43 changed files with 1625 additions and 8 deletions
@@ -0,0 +1,61 @@
using System;
using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace NightclubArcadia.Player
{
/// <summary>
/// Receives pointer and camera-pan SendMessages from PlayerInput and
/// exposes them as C# events for navigation and camera systems.
/// </summary>
[DefaultExecutionOrder(-100)]
public sealed class PlayerPointerInput : MonoBehaviour
{
public Vector2 ScreenPosition { get; private set; }
public bool IsPanning { get; private set; }
public event Action Clicked;
public event Action PanStarted;
public event Action<Vector2> PanDelta;
public event Action PanEnded;
#if ENABLE_INPUT_SYSTEM
void OnPoint(InputValue value)
{
ScreenPosition = value.Get<Vector2>();
}
void OnClick(InputValue value)
{
if (value.isPressed)
{
Clicked?.Invoke();
}
}
void OnCameraPan(InputValue value)
{
if (value.isPressed && !IsPanning)
{
IsPanning = true;
PanStarted?.Invoke();
}
else if (!value.isPressed && IsPanning)
{
IsPanning = false;
PanEnded?.Invoke();
}
}
void OnPointerDelta(InputValue value)
{
if (IsPanning)
{
PanDelta?.Invoke(value.Get<Vector2>());
}
}
#endif
}
}