diff --git a/NightclubArcadia/Assets/Editor/PlayerControlSetup.cs b/NightclubArcadia/Assets/Editor/PlayerControlSetup.cs new file mode 100644 index 0000000..882be97 --- /dev/null +++ b/NightclubArcadia/Assets/Editor/PlayerControlSetup.cs @@ -0,0 +1,316 @@ +using NightclubArcadia.Cinematics; +using NightclubArcadia.Interaction; +using NightclubArcadia.Player; +using StarterAssets; +using Unity.AI.Navigation; +using Unity.Cinemachine; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.AI; + +/// +/// Wires player control, navigation, and camera systems into DialogueTest.unity. +/// Run via Tools → Nightclub Arcadia → Set Up Player Control & Camera. +/// +public static class PlayerControlSetup +{ + const string ScenePath = "Assets/Scenes/DialogueTest.unity"; + const string PlayerPrefabPath = "Assets/StarterAssets/ThirdPersonController/Prefabs/PlayerArmature.prefab"; + const string SettingsFolder = "Assets/Settings"; + + [MenuItem("Tools/Nightclub Arcadia/Set Up Player Control & Camera")] + public static void SetUp() + { + EnsurePlayerPrefab(); + WireScene(); + AssetDatabase.SaveAssets(); + Debug.Log("[PlayerControlSetup] Player control and camera setup complete."); + } + + static void EnsurePlayerPrefab() + { + var prefab = AssetDatabase.LoadAssetAtPath(PlayerPrefabPath); + if (prefab == null) + { + Debug.LogError($"[PlayerControlSetup] Missing prefab at {PlayerPrefabPath}"); + return; + } + + var root = PrefabUtility.LoadPrefabContents(PlayerPrefabPath); + + EnsureComponent(root); + EnsureComponent(root); + EnsureComponent(root); + EnsureComponent(root); + EnsureComponent(root); + + var inputs = root.GetComponent(); + if (inputs != null) + { + inputs.analogMovement = true; + inputs.cursorLocked = false; + } + + var controller = root.GetComponent(); + if (controller != null) + { + controller.LockCameraPosition = true; + } + + var clickToMove = root.GetComponent(); + var pointerNav = root.GetComponent(); + var pointerInput = root.GetComponent(); + var arbiter = root.GetComponent(); + + WireSerializedReference(clickToMove, "brainCamera", null); + WireSerializedReference(pointerNav, "pointerInput", pointerInput); + WireSerializedReference(pointerNav, "clickToMove", clickToMove); + WireSerializedReference(pointerNav, "motionArbiter", arbiter); + + PrefabUtility.SaveAsPrefabAsset(root, PlayerPrefabPath); + PrefabUtility.UnloadPrefabContents(root); + } + + static void WireScene() + { + var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); + var player = GameObject.FindGameObjectWithTag("Player"); + if (player == null) + { + Debug.LogError("[PlayerControlSetup] No Player-tagged object found in scene."); + return; + } + + EnsureComponent(player); + EnsureComponent(player); + EnsureComponent(player); + EnsureComponent(player); + EnsureComponent(player); + + var inputs = player.GetComponent(); + if (inputs != null) + { + inputs.analogMovement = true; + inputs.cursorLocked = false; + } + + var controller = player.GetComponent(); + if (controller != null) + { + controller.LockCameraPosition = true; + } + + var brainCamera = Camera.main; + var pointerInput = player.GetComponent(); + var clickToMove = player.GetComponent(); + var pointerNav = player.GetComponent(); + var arbiter = player.GetComponent(); + + WireSerializedReference(clickToMove, "brainCamera", brainCamera); + WireSerializedReference(pointerNav, "brainCamera", brainCamera); + WireSerializedReference(pointerNav, "pointerInput", pointerInput); + WireSerializedReference(pointerNav, "clickToMove", clickToMove); + WireSerializedReference(pointerNav, "motionArbiter", arbiter); + + var groundLayer = LayerMask.NameToLayer("Ground"); + var interactableLayer = LayerMask.NameToLayer("Interactable"); + SetLayerIfExists("Floor", groundLayer); + SetLayerIfExists("Room_101", groundLayer); + SetLayerIfExists("Wall_Back", groundLayer); + SetLayerIfExists("Wall_Front", groundLayer); + SetLayerIfExists("Table", groundLayer); + + WireBartender("Bartender", interactableLayer); + WireBartender("Bartender (1)", interactableLayer); + + EnsureNavigation(player.transform.position); + EnsureCameraAnchor(player.transform, brainCamera); + + EditorSceneManager.MarkSceneDirty(scene); + EditorSceneManager.SaveScene(scene); + } + + static void WireBartender(string objectName, int interactableLayer) + { + var bartender = GameObject.Find(objectName); + if (bartender == null) + { + return; + } + + if (interactableLayer >= 0) + { + bartender.layer = interactableLayer; + } + + EnsureComponent(bartender); + EnsureComponent(bartender); + } + + static void EnsureNavigation(Vector3 sampleOrigin) + { + var navigation = GameObject.Find("Navigation"); + if (navigation == null) + { + navigation = new GameObject("Navigation"); + } + + var surface = EnsureComponent(navigation); + surface.agentTypeID = 0; + surface.collectObjects = CollectObjects.All; + surface.useGeometry = NavMeshCollectGeometry.RenderMeshes; + + var included = 0; + var defaultLayer = LayerMask.NameToLayer("Default"); + var groundLayer = LayerMask.NameToLayer("Ground"); + if (defaultLayer >= 0) + { + included |= 1 << defaultLayer; + } + + if (groundLayer >= 0) + { + included |= 1 << groundLayer; + } + + surface.layerMask = included; + + var table = GameObject.Find("Table"); + if (table != null) + { + var modifier = EnsureComponent(table); + modifier.overrideArea = true; + modifier.area = NavMesh.GetAreaFromName("Not Walkable"); + } + + surface.BuildNavMesh(); + + if (!NavMesh.SamplePosition(sampleOrigin, out _, 2f, NavMesh.AllAreas)) + { + Debug.LogWarning("[PlayerControlSetup] NavMesh bake did not cover the player start position."); + } + } + + static void EnsureCameraAnchor(Transform followTarget, Camera brainCamera) + { + var vcamObject = GameObject.Find("CinemachineCamera"); + if (vcamObject == null) + { + Debug.LogError("[PlayerControlSetup] CinemachineCamera not found."); + return; + } + + var vcam = vcamObject.GetComponent(); + var composer = vcamObject.GetComponent(); + + var anchorObject = GameObject.Find("CameraAnchor"); + if (anchorObject == null) + { + anchorObject = new GameObject("CameraAnchor"); + } + + anchorObject.transform.position = followTarget.position; + + var anchor = EnsureComponent(anchorObject); + EnsureComponent(anchorObject); + EnsureComponent(anchorObject); + EnsureComponent(anchorObject); + + WireSerializedReference(anchor, "followTarget", followTarget); + WireSerializedReference(anchor, "composer", composer); + + if (vcam != null) + { + vcam.Target.TrackingTarget = anchorObject.transform; + vcam.Priority.Value = 10; + } + + var pan = anchorObject.GetComponent(); + WireSerializedReference(pan, "brainCamera", brainCamera); + WireSerializedReference(pan, "anchor", anchor); + WireSerializedReference(pan, "stateDirector", anchorObject.GetComponent()); + + var poiDirector = anchorObject.GetComponent(); + WireSerializedReference(poiDirector, "anchor", anchor); + WireSerializedReference(poiDirector, "stateDirector", anchorObject.GetComponent()); + + var stateDirector = anchorObject.GetComponent(); + WireSerializedReference(stateDirector, "anchor", anchor); + + EnsureRevealCamera(vcamObject, followTarget); + } + + static void EnsureRevealCamera(GameObject followVcam, Transform followTarget) + { + var revealName = "CM_Reveal_Room101"; + var revealObject = GameObject.Find(revealName); + if (revealObject == null) + { + revealObject = new GameObject(revealName); + revealObject.transform.SetPositionAndRotation( + followVcam.transform.position + new Vector3(-4f, 2f, -2f), + followVcam.transform.rotation); + } + + var revealCam = EnsureComponent(revealObject); + revealCam.Target.TrackingTarget = followTarget; + revealCam.Priority.Value = 0; + revealCam.Lens.FieldOfView = followVcam.GetComponent().Lens.FieldOfView; + + var volumeObject = GameObject.Find("RevealVolume_Room101"); + if (volumeObject == null) + { + volumeObject = new GameObject("RevealVolume_Room101"); + volumeObject.transform.position = followTarget.position + new Vector3(0f, 1f, 2f); + } + + var box = EnsureComponent(volumeObject); + box.isTrigger = true; + box.size = new Vector3(6f, 3f, 4f); + + var volume = EnsureComponent(volumeObject); + WireSerializedReference(volume, "revealCamera", revealCam); + } + + static T EnsureComponent(GameObject target) where T : Component + { + var component = target.GetComponent(); + if (component == null) + { + component = target.AddComponent(); + } + + return component; + } + + static void SetLayerIfExists(string objectName, int layer) + { + if (layer < 0) + { + return; + } + + var obj = GameObject.Find(objectName); + if (obj != null) + { + obj.layer = layer; + } + } + + static void WireSerializedReference(Object target, string propertyName, Object value) + { + if (target == null) + { + return; + } + + var so = new SerializedObject(target); + var property = so.FindProperty(propertyName); + if (property != null) + { + property.objectReferenceValue = value; + so.ApplyModifiedPropertiesWithoutUndo(); + } + } +} diff --git a/NightclubArcadia/Assets/Editor/PlayerControlSetup.cs.meta b/NightclubArcadia/Assets/Editor/PlayerControlSetup.cs.meta new file mode 100644 index 0000000..6ca74c5 --- /dev/null +++ b/NightclubArcadia/Assets/Editor/PlayerControlSetup.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: eedfabcda58c94099b2ad3e19f141b09 \ No newline at end of file diff --git a/NightclubArcadia/Assets/StarterAssets/InputSystem/StarterAssets.inputactions b/NightclubArcadia/Assets/StarterAssets/InputSystem/StarterAssets.inputactions index b6642bf..fbd89e0 100644 --- a/NightclubArcadia/Assets/StarterAssets/InputSystem/StarterAssets.inputactions +++ b/NightclubArcadia/Assets/StarterAssets/InputSystem/StarterAssets.inputactions @@ -36,6 +36,46 @@ "expectedControlType": "", "processors": "", "interactions": "" + }, + { + "name": "Menu", + "type": "Button", + "id": "e81d9782-4d9d-4a8b-b5a3-bc2f31e1cdcc", + "expectedControlType": "Button", + "processors": "", + "interactions": "" + }, + { + "name": "Point", + "type": "PassThrough", + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "" + }, + { + "name": "Click", + "type": "Button", + "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "expectedControlType": "Button", + "processors": "", + "interactions": "" + }, + { + "name": "CameraPan", + "type": "Button", + "id": "c3d4e5f6-a7b8-9012-cdef-123456789012", + "expectedControlType": "Button", + "processors": "", + "interactions": "" + }, + { + "name": "PointerDelta", + "type": "PassThrough", + "id": "d4e5f6a7-b8c9-0123-def0-234567890123", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "" } ], "bindings": [ @@ -214,6 +254,83 @@ "action": "Sprint", "isComposite": false, "isPartOfComposite": false + }, + { + "name": "", + "id": "0f5e00e8-de3a-454f-8338-4348e69ac91a", + "path": "/tab", + "interactions": "", + "processors": "", + "groups": "KeyboardMouse", + "action": "Menu", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8533b9e3-7efc-442a-9d07-8fe1b29a3bf0", + "path": "/start", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Menu", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e5f6a7b8-c9d0-1234-ef01-345678901234", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "KeyboardMouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "f6a7b8c9-d0e1-2345-f012-456789012345", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": "KeyboardMouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "a7b8c9d0-e1f2-3456-0123-567890123456", + "path": "/middleButton", + "interactions": "", + "processors": "", + "groups": "KeyboardMouse", + "action": "CameraPan", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b8c9d0e1-f2a3-4567-1234-678901234567", + "path": "/rightButton", + "interactions": "", + "processors": "", + "groups": "KeyboardMouse", + "action": "CameraPan", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c9d0e1f2-a3b4-5678-2345-789012345678", + "path": "/delta", + "interactions": "", + "processors": "", + "groups": "KeyboardMouse", + "action": "PointerDelta", + "isComposite": false, + "isPartOfComposite": false } ] } diff --git a/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Prefabs/PlayerArmature.prefab b/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Prefabs/PlayerArmature.prefab index 87ce6fa..af0da32 100644 --- a/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Prefabs/PlayerArmature.prefab +++ b/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Prefabs/PlayerArmature.prefab @@ -1037,6 +1037,11 @@ GameObject: - component: {fileID: 5885216827943657505} - component: {fileID: 135756642000475821} - component: {fileID: 4416926081852918493} + - component: {fileID: 4411642300945204457} + - component: {fileID: 2899400238410355188} + - component: {fileID: 5730189035290769348} + - component: {fileID: 1531343089306214720} + - component: {fileID: 4718598727395454326} m_Layer: 8 m_Name: PlayerArmature m_TagString: Player @@ -1155,7 +1160,7 @@ MonoBehaviour: TopClamp: 70 BottomClamp: -30 CameraAngleOverride: 0 - LockCameraPosition: 0 + LockCameraPosition: 1 --- !u!114 &5885216827943657505 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1189,8 +1194,8 @@ MonoBehaviour: look: {x: 0, y: 0} jump: 0 sprint: 0 - analogMovement: 0 - cursorLocked: 1 + analogMovement: 1 + cursorLocked: 0 cursorInputForLook: 1 --- !u!114 &4416926081852918493 MonoBehaviour: @@ -1290,6 +1295,87 @@ MonoBehaviour: m_DefaultActionMap: Player m_SplitScreenIndex: -1 m_Camera: {fileID: 0} +--- !u!114 &4411642300945204457 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4416926081852918481} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ff77faf7e79154095ad57ad50df8c605, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Player.PlayerPointerInput +--- !u!114 &2899400238410355188 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4416926081852918481} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 91bf24256c6694185b4247a736593727, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Player.PlayerMotionArbiter + directInputCancelThreshold: 0.15 + runDistanceThreshold: 6 +--- !u!195 &5730189035290769348 +NavMeshAgent: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4416926081852918481} + m_Enabled: 1 + m_AgentTypeID: 0 + m_Radius: 0.5 + m_Speed: 3.5 + m_Acceleration: 8 + avoidancePriority: 50 + m_AngularSpeed: 120 + m_StoppingDistance: 0 + m_AutoTraverseOffMeshLink: 1 + m_AutoBraking: 1 + m_AutoRepath: 1 + m_Height: 2 + m_BaseOffset: 0 + m_WalkableMask: 4294967295 + m_ObstacleAvoidanceType: 4 +--- !u!114 &1531343089306214720 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4416926081852918481} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c2762b30721644611b9672bd4781cf15, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Player.ClickToMoveController + brainCamera: {fileID: 0} + runDistanceThreshold: 6 +--- !u!114 &4718598727395454326 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4416926081852918481} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f62460e35fda4829bd6ffeffe633a7e, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Player.PointerNavigationInput + brainCamera: {fileID: 0} + clickMask: + serializedVersion: 2 + m_Bits: 4294967295 + pointerInput: {fileID: 4411642300945204457} + clickToMove: {fileID: 1531343089306214720} + motionArbiter: {fileID: 2899400238410355188} --- !u!1 &4523139366846529165 GameObject: m_ObjectHideFlags: 0 diff --git a/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs b/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs index f551708..e48b582 100644 --- a/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs +++ b/NightclubArcadia/Assets/StarterAssets/ThirdPersonController/Scripts/ThirdPersonController.cs @@ -248,7 +248,11 @@ namespace StarterAssets _speed = targetSpeed; } - _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.deltaTime * SpeedChangeRate); + // NightclubArcadia: blend toward speed scaled by input magnitude so + // partial stick / click-to-move arrival uses walk cycles, not a + // slowed run clip via MotionSpeed alone. + _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed * inputMagnitude, + Time.deltaTime * SpeedChangeRate); if (_animationBlend < 0.01f) _animationBlend = 0f; // normalise input direction