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,42 @@
using UnityEngine;
namespace NightclubArcadia.Player.Math
{
/// <summary>
/// Pure math helpers shared by click-to-move and camera pan.
/// </summary>
public static class MotionInputMath
{
/// <summary>
/// Converts a NavMeshAgent desired velocity into the camera-relative
/// Vector2 that ThirdPersonController expects on StarterAssetsInputs.move.
/// </summary>
public static Vector2 WorldToCameraRelativeInput(Vector3 worldVelocity, float agentSpeed, float cameraYaw)
{
if (worldVelocity.sqrMagnitude < 0.0001f || agentSpeed <= 0f)
{
return Vector2.zero;
}
var horizontal = new Vector3(worldVelocity.x, 0f, worldVelocity.z);
var magnitude = horizontal.magnitude;
var fraction = Mathf.Clamp01(magnitude / agentSpeed);
var local = Quaternion.Euler(0f, -cameraYaw, 0f) * horizontal.normalized;
return new Vector2(local.x, local.z) * fraction;
}
/// <summary>
/// Converts screen-space pointer delta into a world-plane pan offset.
/// Drag direction is inverted so the world follows the cursor.
/// </summary>
public static Vector3 ScreenDeltaToWorldPan(
Vector2 pixelDelta,
Vector3 camRight,
Vector3 camForward,
float metresPerPixel)
{
var forward = Vector3.ProjectOnPlane(camForward, Vector3.up).normalized;
return (-pixelDelta.x * camRight + -pixelDelta.y * forward) * metresPerPixel;
}
}
}