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:
@@ -0,0 +1,114 @@
|
||||
using NightclubArcadia.Interaction;
|
||||
using NightclubArcadia.UI;
|
||||
using StarterAssets;
|
||||
using UnityEngine;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace NightclubArcadia.Player
|
||||
{
|
||||
public enum MotionSource
|
||||
{
|
||||
None,
|
||||
Direct,
|
||||
Navigation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sole writer of StarterAssetsInputs move/sprint. Picks one input source
|
||||
/// per frame: direct keyboard/gamepad or click-to-move navigation.
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-50)]
|
||||
public sealed class PlayerMotionArbiter : MonoBehaviour
|
||||
{
|
||||
[SerializeField] float directInputCancelThreshold = 0.15f;
|
||||
[SerializeField] float runDistanceThreshold = 6f;
|
||||
|
||||
StarterAssetsInputs playerInputs;
|
||||
PlayerControlLock controlLock;
|
||||
ClickToMoveController clickToMove;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
PlayerInput playerInput;
|
||||
#endif
|
||||
|
||||
public MotionSource ActiveSource { get; private set; }
|
||||
|
||||
void Awake()
|
||||
{
|
||||
playerInputs = GetComponent<StarterAssetsInputs>();
|
||||
controlLock = GetComponent<PlayerControlLock>();
|
||||
clickToMove = GetComponent<ClickToMoveController>();
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
playerInput = GetComponent<PlayerInput>();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
ActiveSource = MotionSource.None;
|
||||
clickToMove?.Cancel();
|
||||
WriteInput(Vector2.zero, false);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (controlLock != null && controlLock.IsLocked)
|
||||
{
|
||||
clickToMove?.Cancel();
|
||||
ActiveSource = MotionSource.None;
|
||||
WriteInput(Vector2.zero, false);
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
var raw = playerInput.actions["Move"].ReadValue<Vector2>();
|
||||
#else
|
||||
var raw = Vector2.zero;
|
||||
#endif
|
||||
|
||||
if (raw.sqrMagnitude > directInputCancelThreshold * directInputCancelThreshold)
|
||||
{
|
||||
clickToMove?.Cancel();
|
||||
ActiveSource = MotionSource.Direct;
|
||||
WriteInput(raw, ReadDirectSprint());
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickToMove != null && clickToMove.HasPath)
|
||||
{
|
||||
ActiveSource = MotionSource.Navigation;
|
||||
WriteInput(clickToMove.DesiredMoveInput, ShouldNavigationSprint());
|
||||
return;
|
||||
}
|
||||
|
||||
ActiveSource = MotionSource.None;
|
||||
WriteInput(Vector2.zero, false);
|
||||
}
|
||||
|
||||
bool ReadDirectSprint()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
return playerInput.actions["Sprint"].IsPressed();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ShouldNavigationSprint()
|
||||
{
|
||||
return clickToMove != null && clickToMove.WantsSprint;
|
||||
}
|
||||
|
||||
void WriteInput(Vector2 move, bool sprint)
|
||||
{
|
||||
if (playerInputs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerInputs.MoveInput(move);
|
||||
playerInputs.SprintInput(sprint);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user