diff --git a/.idea/.idea.HALLUCINATE/.idea/workspace.xml b/.idea/.idea.HALLUCINATE/.idea/workspace.xml index ba05eaad..50e59e0f 100644 --- a/.idea/.idea.HALLUCINATE/.idea/workspace.xml +++ b/.idea/.idea.HALLUCINATE/.idea/workspace.xml @@ -5,11 +5,18 @@ - - - - - + + + + + + + + + + + + - { - "keyToString": { - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.MCP Project settings loaded": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", - "RunOnceActivity.git.unshallow": "true", - "RunOnceActivity.typescript.service.memoryLimit.init": "true", - "com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true", - "git-widget-placeholder": "main", - "junie.onboarding.icon.badge.shown": "true", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "settings.editor.selected.configurable": "preferences.pluginManager", - "to.speed.mode.migration.done": "true", - "vue.rearranger.settings.migration": "true" + +}]]> diff --git a/Assets/Scenes/Lobby.unity b/Assets/Scenes/Lobby.unity index 566ba552..3014fb62 100644 --- a/Assets/Scenes/Lobby.unity +++ b/Assets/Scenes/Lobby.unity @@ -3158,7 +3158,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ca752d01bdc2c5e42938776307031da3, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::BasicSpawner - LobbyManager: {fileID: 0} + LobbyManager: {fileID: 2023167162} _playerPrefab: RawGuidValue: 761bdf2e5c0cff4488527355acb975e5 --- !u!1660057539 &9223372036854775807 diff --git a/Assets/Scenes/Main Scene.unity b/Assets/Scenes/Main Scene.unity index dc448259..27abaec9 100644 --- a/Assets/Scenes/Main Scene.unity +++ b/Assets/Scenes/Main Scene.unity @@ -704,8 +704,10 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 2116497667} + - component: {fileID: 2116497669} + - component: {fileID: 2116497668} m_Layer: 0 - m_Name: _MANAGER + m_Name: Lobby Manager m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -726,6 +728,34 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2116497668 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2116497666} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b5aeb4670d7bf41499d3aaf409820260, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::LobbyManager + roomListContent: {fileID: 2116497667} +--- !u!114 &2116497669 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2116497666} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ca752d01bdc2c5e42938776307031da3, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::_BasicSpawner + LobbyManager: {fileID: 0} + _playerPrefab: + RawGuidValue: 761bdf2e5c0cff4488527355acb975e5 --- !u!1001 &3886963620680427248 PrefabInstance: m_ObjectHideFlags: 0 @@ -893,7 +923,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3154409663696148700, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3} propertyPath: m_LocalPosition.x - value: -0.12540007 + value: 5.34 objectReference: {fileID: 0} - target: {fileID: 3154409663696148700, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3} propertyPath: m_LocalPosition.y @@ -901,7 +931,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3154409663696148700, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3} propertyPath: m_LocalPosition.z - value: -0.13893032 + value: -1.89 objectReference: {fileID: 0} - target: {fileID: 3154409663696148700, guid: 761bdf2e5c0cff4488527355acb975e5, type: 3} propertyPath: m_LocalRotation.w diff --git a/Assets/Scripts/Player Controller/PlayerAirDashState.cs b/Assets/Scripts/Player Controller/PlayerAirDashState.cs index afebed27..b3f8a6dd 100644 --- a/Assets/Scripts/Player Controller/PlayerAirDashState.cs +++ b/Assets/Scripts/Player Controller/PlayerAirDashState.cs @@ -20,7 +20,7 @@ namespace OnlyScove.Scripts if (input != Vector2.zero) { dashDirection = new Vector3(input.x, 0, input.y).normalized; - dashDirection = stateMachine.NetworkedCameraRotation * dashDirection; + dashDirection = stateMachine.CameraRotation * dashDirection; } else { diff --git a/Assets/Scripts/Player Controller/PlayerDashState.cs b/Assets/Scripts/Player Controller/PlayerDashState.cs index 7315a190..af59fb2f 100644 --- a/Assets/Scripts/Player Controller/PlayerDashState.cs +++ b/Assets/Scripts/Player Controller/PlayerDashState.cs @@ -25,7 +25,7 @@ namespace OnlyScove.Scripts if (input != Vector2.zero) { dashDirection = new Vector3(input.x, 0f, input.y).normalized; - dashDirection = stateMachine.NetworkedCameraRotation * dashDirection; + dashDirection = stateMachine.CameraRotation * dashDirection; dashDirection.y = 0; dashDirection.Normalize(); diff --git a/Assets/Scripts/Player Controller/PlayerDodgeState.cs b/Assets/Scripts/Player Controller/PlayerDodgeState.cs index c673a942..2604da02 100644 --- a/Assets/Scripts/Player Controller/PlayerDodgeState.cs +++ b/Assets/Scripts/Player Controller/PlayerDodgeState.cs @@ -20,7 +20,7 @@ namespace OnlyScove.Scripts if (input != Vector2.zero) { dodgeDirection = new Vector3(input.x, 0, input.y).normalized; - dodgeDirection = stateMachine.NetworkedCameraRotation * dodgeDirection; + dodgeDirection = stateMachine.CameraRotation * dodgeDirection; } else { diff --git a/Assets/Scripts/Player Controller/PlayerFallState.cs b/Assets/Scripts/Player Controller/PlayerFallState.cs index 2646549b..78710cc8 100644 --- a/Assets/Scripts/Player Controller/PlayerFallState.cs +++ b/Assets/Scripts/Player Controller/PlayerFallState.cs @@ -37,7 +37,7 @@ namespace OnlyScove.Scripts // ĐÚNG: Sử dụng MoveInput đã đồng bộ mạng Vector2 input = stateMachine.MoveInput; Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized; - Vector3 moveDirection = stateMachine.NetworkedCameraRotation * inputDir; + Vector3 moveDirection = stateMachine.CameraRotation * inputDir; moveDirection.y = 0; moveDirection.Normalize(); diff --git a/Assets/Scripts/Player Controller/PlayerJumpState.cs b/Assets/Scripts/Player Controller/PlayerJumpState.cs index 268bb046..c054eb02 100644 --- a/Assets/Scripts/Player Controller/PlayerJumpState.cs +++ b/Assets/Scripts/Player Controller/PlayerJumpState.cs @@ -43,7 +43,7 @@ namespace OnlyScove.Scripts Vector2 input = stateMachine.MoveInput; Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized; - Vector3 moveDirection = stateMachine.NetworkedCameraRotation * inputDir; + Vector3 moveDirection = stateMachine.CameraRotation * inputDir; moveDirection.y = 0; moveDirection.Normalize(); diff --git a/Assets/Scripts/Player Controller/PlayerMoveState.cs b/Assets/Scripts/Player Controller/PlayerMoveState.cs index bd9dddec..fb07d4ab 100644 --- a/Assets/Scripts/Player Controller/PlayerMoveState.cs +++ b/Assets/Scripts/Player Controller/PlayerMoveState.cs @@ -37,7 +37,7 @@ namespace OnlyScove.Scripts } Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized; - Vector3 moveDirection = stateMachine.NetworkedCameraRotation * inputDir; + Vector3 moveDirection = stateMachine.CameraRotation * inputDir; moveDirection.y = 0; moveDirection.Normalize(); diff --git a/Assets/Scripts/Player Controller/PlayerRunState.cs b/Assets/Scripts/Player Controller/PlayerRunState.cs index 234dedf7..ad9472f0 100644 --- a/Assets/Scripts/Player Controller/PlayerRunState.cs +++ b/Assets/Scripts/Player Controller/PlayerRunState.cs @@ -36,7 +36,7 @@ namespace OnlyScove.Scripts } Vector3 inputDir = new Vector3(input.x, 0, input.y).normalized; - Vector3 moveDirection = stateMachine.NetworkedCameraRotation * inputDir; + Vector3 moveDirection = stateMachine.CameraRotation * inputDir; moveDirection.y = 0; moveDirection.Normalize(); diff --git a/Assets/Scripts/Player Controller/PlayerStateMachine.cs b/Assets/Scripts/Player Controller/PlayerStateMachine.cs index 715a5b7b..c0aa9ece 100644 --- a/Assets/Scripts/Player Controller/PlayerStateMachine.cs +++ b/Assets/Scripts/Player Controller/PlayerStateMachine.cs @@ -47,6 +47,22 @@ namespace OnlyScove.Scripts [field: SerializeField] public LayerMask InteractionMask { get; private set; } [Networked] public Quaternion NetworkedCameraRotation { get; set; } + + // Thuộc tính hỗ trợ lấy rotation của Camera an toàn cho cả Online và Offline + public Quaternion CameraRotation + { + get + { + if (Runner != null && Runner.IsRunning && Object != null) + return NetworkedCameraRotation; + + if (Cam != null) + return Cam.PlanarRotation; + + return transform.rotation; + } + } + [Networked] public Vector2 NetworkedMoveInput { get; set; } [Networked] public float NetworkedSpeed { get; set; } [Networked] public Vector3 NetworkedPosition { get; set; } @@ -94,11 +110,39 @@ namespace OnlyScove.Scripts velocityZHash = Animator.StringToHash(velocityZParamName); } + private void Start() + { + // Nếu chạy Offline (kéo prefab vào scene), Spawned() sẽ không được gọi. + // Chúng ta khởi tạo tại đây để đảm bảo nhân vật hoạt động. + if (Runner == null || !Runner.IsRunning) + { + InitializePlayer(); + } + } + public override void Spawned() { - SwitchState(new PlayerIdleState(this)); + // Fusion gọi Spawned khi object được nạp vào mạng. + InitializePlayer(); - if (Object.HasInputAuthority) + // Nếu không có quyền điều khiển và đang ở Client, tắt Controller để tránh xung đột + if (Object != null && !Object.HasInputAuthority && Runner.IsClient) + { + if (Controller != null) Controller.enabled = false; + } + } + + private void InitializePlayer() + { + if (currentState == null) + { + SwitchState(new PlayerIdleState(this)); + } + + bool isOffline = Runner == null || !Runner.IsRunning; + bool hasAuthority = Object != null && Object.HasInputAuthority; + + if (isOffline || hasAuthority) { Local = this; @@ -112,11 +156,9 @@ namespace OnlyScove.Scripts Input.OnNextInteractEvent += OnNextInteract; Input.OnPreviousInteractEvent += OnPreviousInteract; - } - else - { - // Vô hiệu hóa Controller của người chơi khác trên máy khách để tránh xung đột vật lý - if (Runner.IsClient && Controller != null) Controller.enabled = false; + + // Đảm bảo Controller được bật + if (Controller != null) Controller.enabled = true; } } @@ -136,19 +178,26 @@ namespace OnlyScove.Scripts public void Move(Vector3 velocity, float animatorSpeed, float deltaTime) { - // CHỈ thực hiện di chuyển nếu có quyền điều khiển hoặc là Server - if (!Object.HasInputAuthority && !Runner.IsServer) return; + // Cho phép di chuyển nếu: + // 1. Không có mạng (Offline test) + // 2. Có quyền điều khiển (Input Authority) + // 3. Là Server (State Authority) + bool canMove = (Runner == null || !Runner.IsRunning) || Object.HasInputAuthority || Runner.IsServer; + if (!canMove) return; if (Controller != null && Controller.enabled) { Controller.Move(velocity * deltaTime); - // Cập nhật vị trí mạng ngay sau khi di chuyển để tick sau quay lại đây - NetworkedPosition = transform.position; + // Cập nhật vị trí mạng ngay sau khi di chuyển + if (Object != null && Runner != null && Runner.IsRunning) + { + NetworkedPosition = transform.position; + } } localAnimatorSpeed = animatorSpeed; - if (Object.HasStateAuthority) + if (Object != null && Object.HasStateAuthority) { NetworkedSpeed = animatorSpeed; NetworkedMoveInput = MoveInput; @@ -164,7 +213,7 @@ namespace OnlyScove.Scripts float speedValue; Vector2 inputVector; - if (Object.HasInputAuthority) + if (Runner == null || !Runner.IsRunning || Object.HasInputAuthority) { speedValue = localAnimatorSpeed; inputVector = MoveInput; @@ -183,15 +232,14 @@ namespace OnlyScove.Scripts public override void FixedUpdateNetwork() { - if (Object == null) return; + bool isRunning = Runner != null && Runner.IsRunning; + if (Object == null && isRunning) return; // ĐỒNG BỘ VỊ TRÍ: Ép nhân vật về vị trí mạng trước khi tính toán tick mới - // Điều này cực kỳ quan trọng để CharacterController không bị nhân đôi vận tốc khi Resimulation - if (NetworkedPosition != Vector3.zero) + if (isRunning && NetworkedPosition != Vector3.zero) { - if (Controller != null) + if (Controller != null && !Object.HasInputAuthority) { - // Tạm thời tắt Controller để dịch chuyển Transform chính xác Controller.enabled = false; transform.position = NetworkedPosition; Controller.enabled = true; @@ -202,7 +250,15 @@ namespace OnlyScove.Scripts { MoveInput = data.Direction; IsSprintHeld = data.sprint; - NetworkedCameraRotation = data.rot; + // Chỉ gán biến Networked nếu đang chạy mạng + if (isRunning) NetworkedCameraRotation = data.rot; + } + else if (!isRunning) + { + // FALLBACK INPUT: Nếu không có Fusion, lấy input trực tiếp từ Unity để Test + MoveInput = new Vector2(UnityEngine.Input.GetAxisRaw("Horizontal"), UnityEngine.Input.GetAxisRaw("Vertical")); + IsSprintHeld = UnityEngine.Input.GetKey(KeyCode.LeftShift); + // Ở chế độ offline, chúng ta không gán vào NetworkedCameraRotation nữa } else { @@ -210,7 +266,9 @@ namespace OnlyScove.Scripts IsSprintHeld = false; } - if (!Object.HasInputAuthority && !Runner.IsServer) + bool isSimulating = !isRunning || Object.HasInputAuthority || Runner.IsServer; + + if (!isSimulating) { UpdateAnimator(Runner.DeltaTime); return; @@ -222,7 +280,18 @@ namespace OnlyScove.Scripts CheckGround(); UpdateInteractablesList(); - currentState?.Tick(Runner.DeltaTime); + float dt = isRunning ? Runner.DeltaTime : Time.fixedDeltaTime; + currentState?.Tick(dt); + } + + private void Update() + { + // Nếu không có NetworkRunner, Fusion sẽ không gọi FixedUpdateNetwork. + // Chúng ta gọi thủ công để logic StateMachine vẫn chạy được khi Test Offline. + if (Runner == null || !Runner.IsRunning) + { + FixedUpdateNetwork(); + } } private void CheckGround()