diff --git a/Assets/InputSystem_Actions.inputactions b/Assets/InputSystem_Actions.inputactions index 1a12cb91b6..89ea999893 100644 --- a/Assets/InputSystem_Actions.inputactions +++ b/Assets/InputSystem_Actions.inputactions @@ -1,4 +1,5 @@ { + "version": 1, "name": "InputSystem_Actions", "maps": [ { diff --git a/Assets/Scripts/CharacterCtrl.cs b/Assets/Scripts/CharacterCtrl.cs index 7a014bedc7..590e8f24ee 100644 --- a/Assets/Scripts/CharacterCtrl.cs +++ b/Assets/Scripts/CharacterCtrl.cs @@ -1,4 +1,5 @@ -using CSNetwork.Protocols.RPCData; +using CSNetwork.Protocols; +using CSNetwork.Protocols.RPCData; using System.Data; using System.Text; using TMPro; @@ -17,6 +18,9 @@ public class CharacterCtrl : MonoBehaviour [SerializeField] private Button btnJump; [SerializeField] private Button btnRun; + PlayerStateMachine playerStateMachine; + PlayerMoveState moveState; + float playerSpeed = 5.0f; float jumpHeight = 1.5f; float gravityValue = -9.81f; @@ -25,8 +29,16 @@ public class CharacterCtrl : MonoBehaviour bool isGrounded = false; bool isRun = false; + private void Awake() + { + moveState = new PlayerMoveState(this); + playerStateMachine = new PlayerStateMachine(); + } + private void Start() { + playerStateMachine.InitState(moveState); + //btnJump.onClick.AddListener(HandleJump); } @@ -71,6 +83,10 @@ public class CharacterCtrl : MonoBehaviour } private void Update() + { + playerStateMachine.UpdateState(); + } + public void HandleMovement() { isGrounded = controller.isGrounded; @@ -119,9 +135,7 @@ public class CharacterCtrl : MonoBehaviour playerVelocity.y += gravityValue * Time.deltaTime; Vector3 finalMove = (move * playerSpeed) + (playerVelocity.y * Vector3.up); controller.Move(finalMove * Time.deltaTime); - } - private void SetAnimIdle() { if(stateAnim == StateAnim.Idle || !isGrounded) diff --git a/Assets/Scripts/Move.meta b/Assets/Scripts/Move.meta new file mode 100644 index 0000000000..1d4696aed4 --- /dev/null +++ b/Assets/Scripts/Move.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 975de416ff502c34cac627a81815e2b0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Move/AAssist.cs b/Assets/Scripts/Move/AAssist.cs new file mode 100644 index 0000000000..0947f3f467 --- /dev/null +++ b/Assets/Scripts/Move/AAssist.cs @@ -0,0 +1,25 @@ +public static class AAssist +{ + // Bản generic (giống template C++) + public static void a_ClampRoof(ref T x, T max) where T : System.IComparable + { + if (x.CompareTo(max) > 0) x = max; + } + + // Tiện dụng cho float (Unity) + public static void a_ClampRoof(ref float x, float max) + { + if (x > max) x = max; + } + + // (Tuỳ chọn) giữ luôn 2 hàm “họ hàng” như trong header gốc: + public static void a_ClampFloor(ref T x, T min) where T : System.IComparable + { + if (x.CompareTo(min) < 0) x = min; + } + public static void a_Clamp(ref T x, T min, T max) where T : System.IComparable + { + if (x.CompareTo(min) < 0) x = min; + if (x.CompareTo(max) > 0) x = max; + } +} diff --git a/Assets/Scripts/Move/AAssist.cs.meta b/Assets/Scripts/Move/AAssist.cs.meta new file mode 100644 index 0000000000..3588cc9424 --- /dev/null +++ b/Assets/Scripts/Move/AAssist.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 43ba3172dd47c0a44b7194919c6104c4 \ No newline at end of file diff --git a/Assets/Scripts/Move/CECHostMove.cs b/Assets/Scripts/Move/CECHostMove.cs new file mode 100644 index 0000000000..44236c5eee --- /dev/null +++ b/Assets/Scripts/Move/CECHostMove.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Runtime.ConstrainedExecution; +using System.Text; +using UnityEngine; + + +public class CECHostMove +{ + // Giữ reference tới CECHostPlayer – y như bản gốc +} +public struct CDR_INFO +{ + //the aabb + public Vector3 vCenter; + //@note : the caller should make sure ext(.x, .y, .z) > 0. By Kuiwu[22/9/2005] + public Vector3 vExtent; + + public float fStepHeight; + + // Velocity Info + public Vector3 vXOZVelDir; + public float fYVel; + public float fSpeed; + + // time span ( sec ) + public float t; + + // Gravity acceleration + public float fGravityAccel; + + // the Climb Slope Thresh + public float fSlopeThresh; + + // Tangent plane Info + public Vector3 vTPNormal; + + // Absolute Velocity: output for forcast! + public Vector3 vAbsVelocity; + + + //the moving dist + public float fMoveDist; +}; \ No newline at end of file diff --git a/Assets/Scripts/Move/CECHostMove.cs.meta b/Assets/Scripts/Move/CECHostMove.cs.meta new file mode 100644 index 0000000000..c728d4381e --- /dev/null +++ b/Assets/Scripts/Move/CECHostMove.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cd5fc9aa768e77b4f90b7b8a4c5514c2 \ No newline at end of file diff --git a/Assets/Scripts/Move/EC_CDR.cs b/Assets/Scripts/Move/EC_CDR.cs new file mode 100644 index 0000000000..f159787ac8 --- /dev/null +++ b/Assets/Scripts/Move/EC_CDR.cs @@ -0,0 +1,239 @@ +using System; +using TMPro; +using Unity.VisualScripting; +using UnityEngine; +using UnityEngine.LightTransport; +public static class EC_CDR +{ + // Cho phép CECHostMove gán mask theo scene (giữ linh hoạt nhưng không phá cấu trúc) + public static LayerMask BrushMask { get; set; } = ~0; + public static LayerMask TerrainMask { get; set; } = ~0; + + static LayerMask UsedMask_Ground() => BrushMask | TerrainMask; + + // == Thay CollideWithEnv (C++) bằng BoxCast == + static bool CollideWithEnv_BoxCast(Vector3 vStart, Vector3 vDelta, Vector3 vExt, + LayerMask mask, + out RaycastHit hit, out float fFraction, out Vector3 vHitNormal, out bool bStartSolid, + float skin = 0.01f) + { + hit = default; + vHitNormal = Vector3.up; + bStartSolid = false; + fFraction = 1.0f; + + float dist = vDelta.magnitude; + if (dist <= 1e-6f) return false; + + // start-in-solid + var overlapped = Physics.OverlapBox(vStart, vExt - Vector3.one * skin, Quaternion.identity, mask, QueryTriggerInteraction.Ignore); + if (overlapped != null && overlapped.Length > 0) + { + bStartSolid = true; + return true; + } + + // sweep AABB + Vector3 dir = vDelta / Mathf.Max(dist, 1e-6f); + if (Physics.BoxCast(vStart, vExt - Vector3.one * skin, dir, out hit, Quaternion.identity, dist, mask, QueryTriggerInteraction.Ignore)) + { + fFraction = Mathf.Clamp01(hit.distance / Mathf.Max(dist, 1e-6f)); + vHitNormal = hit.normal; + return true; + } + return false; + } + + // == Thay RetrieveSupportPlane (C++) bằng Raycast xuống == + static bool DoGroundProbe(Vector3 vStart, Vector3 vExt, float fDeltaY, LayerMask mask, + out Vector3 vEnd, out Vector3 vHitNormal, out bool bSupport, + float skin = 0.01f) + { + vEnd = vStart; + vHitNormal = Vector3.up; + bSupport = false; + + float dist = Mathf.Max(fDeltaY, 0f) + vExt.y + skin + 0.05f; + Vector3 origin = vStart + Vector3.up * 0.05f; + + if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, dist, mask, QueryTriggerInteraction.Ignore)) + { + vHitNormal = hit.normal; + vEnd = new Vector3(vStart.x, hit.point.y + vExt.y + skin, vStart.z); + bSupport = (vHitNormal.y >= 0f); + return true; + } + return true; // không thấy ground → bSupport=false + } + + // ======= STATIC OnGroundMove – GIỮ NGUYÊN VAI TRÒ TOÀN CỤC (C API) ======= + public static void OnGroundMove(ref CDR_INFO CDRInfo) + { + const float VEL_EPSILON = 1e-4f; + const float DIST_EPSILON = 1e-4f; + const float NORMAL_EPSILON = 1e-2f; + const float VEL_MAX_SPEED = 200f; + const float VEL_REFLECT = 0.3f; + + CDRInfo.fMoveDist = 0.0f; + + bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh); + if (Mathf.Abs(CDRInfo.fYVel) < VEL_EPSILON && Mathf.Abs(CDRInfo.fSpeed) < VEL_EPSILON && !bFreeFall) + return; + + float fYVel = CDRInfo.fYVel; + bool bJump = (fYVel > 0.5f); + + Vector3 vVelocity = CDRInfo.fSpeed * new Vector3(CDRInfo.vXOZVelDir.x, 0f, CDRInfo.vXOZVelDir.z) + fYVel * Vector3.up; + if (bFreeFall) + { + vVelocity += -CDRInfo.fGravityAccel * CDRInfo.t * Vector3.up; + fYVel += -CDRInfo.fGravityAccel * CDRInfo.t; + } + + Vector3 vVelDir = vVelocity; + float fVelSpeed = vVelDir.magnitude; + if (fVelSpeed > 1e-6f) vVelDir /= fVelSpeed; else vVelDir = Vector3.zero; + if (!bFreeFall) fVelSpeed = Mathf.Min(fVelSpeed, VEL_MAX_SPEED); + vVelocity = vVelDir * fVelSpeed; + + float dtp = Vector3.Dot(vVelDir, CDRInfo.vTPNormal); + if (dtp < 0f || !bJump) + { + vVelocity = (vVelDir - CDRInfo.vTPNormal * dtp - CDRInfo.vTPNormal * dtp * 0.01f) * fVelSpeed; + } + + CDRInfo.vAbsVelocity = vVelocity; + + Vector3 vStart = CDRInfo.vCenter; + Vector3 vExt = CDRInfo.vExtent; + float fTime = CDRInfo.t; + + Vector3 vDelta, vNormal = Vector3.up, vFinalPos = vStart; + bool bPull = false; + bool bTryPull = false; + + int nTry = 0; + LayerMask mask = UsedMask_Ground(); + + while (nTry < 4) + { + vDelta = vVelocity * fTime; + float fDeltaDist = vDelta.magnitude; + if (fDeltaDist < DIST_EPSILON) break; + + bool hasHit = CollideWithEnv_BoxCast(vStart, vDelta, vExt, mask, + out RaycastHit hit, out float fFraction, out Vector3 hitNormal, out bool bStartSolid); + + nTry++; + + if (bStartSolid) + { + CDRInfo.fMoveDist = 0f; + if (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh) CDRInfo.vTPNormal = Vector3.up; + return; + } + + if (!hasHit) + { + vFinalPos = vStart + vDelta; + CDRInfo.fMoveDist += fDeltaDist; + break; + } + + vStart += vDelta * fFraction; + CDRInfo.fMoveDist += (fDeltaDist * fFraction); + fTime -= fTime * fFraction; + vNormal = hitNormal; + + // Step-up (giữ tinh thần bản gốc) + if (!bFreeFall && !bTryPull && !bJump) + { + float skin = 0.01f; + Vector3 vStartUp = vStart + new Vector3(0f, CDRInfo.fStepHeight, 0f); + bool upBlocked = Physics.CheckBox(vStartUp, vExt - Vector3.one * skin, Quaternion.identity, mask, QueryTriggerInteraction.Ignore); + if (!upBlocked) + { + Vector3 vDelta2 = vVelocity; + bool hasHit2 = CollideWithEnv_BoxCast(vStartUp, vDelta2, vExt, mask, + out RaycastHit hit2, out float frac2, out Vector3 hitNormal2, out bool bStartSolid2); + if (hasHit2) vDelta2 *= frac2; + + if (vDelta2.sqrMagnitude >= (vExt.x * vExt.x * 4f)) + { + vStart = vStartUp; + vDelta = vDelta2; + + float distAll = vVelocity.magnitude; + float distMoved = vDelta.magnitude; + if (distAll > 1e-6f) fTime *= Mathf.Clamp01(distMoved / Mathf.Max(distAll, 1e-6f)); + + bPull = true; + } + } + bTryPull = true; + } + + if (!bPull) + { + if (vVelocity.sqrMagnitude > 1e-12f) + { + vVelDir = vVelocity.normalized; + fVelSpeed = vVelocity.magnitude * (1f - nTry * 0.1f); + + dtp = Vector3.Dot(vNormal, vVelDir); + float fRelSpeed = Mathf.Min(fVelSpeed, 5.0f); + + if (dtp >= 0f && dtp < 1e-4f) + { + vVelocity += vNormal * VEL_REFLECT * fRelSpeed; + } + else + { + vVelocity = (vVelDir - vNormal * dtp) * fVelSpeed - vNormal * dtp * VEL_REFLECT * fRelSpeed; + } + } + + if (fYVel > VEL_EPSILON) + { + if (vNormal.y >= CDRInfo.fSlopeThresh || vNormal.y < -NORMAL_EPSILON) fYVel = 0f; + } + else if (fYVel < -VEL_EPSILON) + { + if (vNormal.y >= CDRInfo.fSlopeThresh) fYVel = 0f; + } + } + } + + // “vertical ground trace” – thay RetrieveSupportPlane + Vector3 vTPNormal = Vector3.zero; + Vector3 vFinal = vFinalPos; + + float downDist = 0.3f; + if (bPull) downDist = CDRInfo.fStepHeight + 0.1f; + if (bJump) downDist = 0.0f; + + if (downDist > 0f) + { + if (!DoGroundProbe(vFinalPos, vExt, downDist, mask, out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport)) + { + CDRInfo.fMoveDist = 0f; + CDRInfo.vTPNormal = Vector3.up; + return; + } + + if (bSupport) + { + vFinal = vEnd; + if (!bJump) vTPNormal = groundNormal; + } + } + + if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f) || (!bJump && fYVel > 0.0f)) + fYVel = 0.0f; + + CDRInfo.vCenter = vFinal; + CDRInfo.fYVel = fYVel; + if (vTPNormal != Vector3.zero) CDRInfo.vTPNormal = vTPNormal; + } +} diff --git a/Assets/Scripts/Move/EC_CDR.cs.meta b/Assets/Scripts/Move/EC_CDR.cs.meta new file mode 100644 index 0000000000..f24d1c3ca6 --- /dev/null +++ b/Assets/Scripts/Move/EC_CDR.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 83067442406d6e5459471cca0b1ff2d3 \ No newline at end of file diff --git a/Assets/Scripts/Move/EC_HostPlayer.cs b/Assets/Scripts/Move/EC_HostPlayer.cs new file mode 100644 index 0000000000..ff1dca6122 --- /dev/null +++ b/Assets/Scripts/Move/EC_HostPlayer.cs @@ -0,0 +1,7 @@ +using UnityEngine; + +public class CECHostPlayer : MonoBehaviour +{ + + +} diff --git a/Assets/Scripts/Move/EC_HostPlayer.cs.meta b/Assets/Scripts/Move/EC_HostPlayer.cs.meta new file mode 100644 index 0000000000..fe44f0f5ea --- /dev/null +++ b/Assets/Scripts/Move/EC_HostPlayer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7cd2ee670dc30154685fd720c71d3712 \ No newline at end of file diff --git a/Assets/Scripts/Move/EC_Player.cs b/Assets/Scripts/Move/EC_Player.cs new file mode 100644 index 0000000000..052a19326b --- /dev/null +++ b/Assets/Scripts/Move/EC_Player.cs @@ -0,0 +1,6 @@ +using UnityEngine; + +public class EC_Player +{ + public Bounds m_aabbServer; +} diff --git a/Assets/Scripts/Move/EC_Player.cs.meta b/Assets/Scripts/Move/EC_Player.cs.meta new file mode 100644 index 0000000000..165da5cb56 --- /dev/null +++ b/Assets/Scripts/Move/EC_Player.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: be4a22babee7846459b0421234a44c99 \ No newline at end of file diff --git a/Assets/Scripts/PlayerState.meta b/Assets/Scripts/PlayerState.meta new file mode 100644 index 0000000000..5e82186982 --- /dev/null +++ b/Assets/Scripts/PlayerState.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9ef8d28320e272c47b341e1a2aca2ff5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/PlayerState/PlayerMoveState.cs b/Assets/Scripts/PlayerState/PlayerMoveState.cs new file mode 100644 index 0000000000..b7f4e92fae --- /dev/null +++ b/Assets/Scripts/PlayerState/PlayerMoveState.cs @@ -0,0 +1,23 @@ +using UnityEngine; + +public class PlayerMoveState : PlayerState +{ + public PlayerMoveState(CharacterCtrl characterCtrl) : base(characterCtrl) + { + } + + public override void Enter() + { + + } + + public override void Exit() + { + + } + + public override void Update() + { + _characterCtrl.HandleMovement(); + } +} diff --git a/Assets/Scripts/PlayerState/PlayerMoveState.cs.meta b/Assets/Scripts/PlayerState/PlayerMoveState.cs.meta new file mode 100644 index 0000000000..1ee3be87e9 --- /dev/null +++ b/Assets/Scripts/PlayerState/PlayerMoveState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 979df0dcb6a16cf47a77106a9c9ebb8f \ No newline at end of file diff --git a/Assets/Scripts/PlayerState/PlayerState.cs b/Assets/Scripts/PlayerState/PlayerState.cs new file mode 100644 index 0000000000..3c41a61c20 --- /dev/null +++ b/Assets/Scripts/PlayerState/PlayerState.cs @@ -0,0 +1,13 @@ +using UnityEngine; + +public abstract class PlayerState +{ + protected readonly CharacterCtrl _characterCtrl; + public PlayerState(CharacterCtrl characterCtrl) + { + _characterCtrl = characterCtrl; + } + public abstract void Enter(); + public abstract void Exit(); + public abstract void Update(); +} diff --git a/Assets/Scripts/PlayerState/PlayerState.cs.meta b/Assets/Scripts/PlayerState/PlayerState.cs.meta new file mode 100644 index 0000000000..037ea5a784 --- /dev/null +++ b/Assets/Scripts/PlayerState/PlayerState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1c3e8f4466ed88340aeb27f86dc0c172 \ No newline at end of file diff --git a/Assets/Scripts/PlayerStateMachine.cs b/Assets/Scripts/PlayerStateMachine.cs new file mode 100644 index 0000000000..76c3e17f6f --- /dev/null +++ b/Assets/Scripts/PlayerStateMachine.cs @@ -0,0 +1,35 @@ +using UnityEngine; + +public class PlayerStateMachine +{ + PlayerState _state; + public void InitState(PlayerState state) + { + if (_state != null) + { + Debug.LogWarning("_state is already inited"); + return; + } + _state = state; + } + + public void ChangeState(PlayerState state) + { + if (_state == null ) + { + Debug.LogError("you need to init state first "); + return; + } + if (_state == state) + { + Debug.LogWarning("Unchanged state"); + } + _state.Exit(); + _state = state; + _state.Enter(); + } + public void UpdateState() + { + _state.Update(); + } +} diff --git a/Assets/Scripts/PlayerStateMachine.cs.meta b/Assets/Scripts/PlayerStateMachine.cs.meta new file mode 100644 index 0000000000..1de8cadd00 --- /dev/null +++ b/Assets/Scripts/PlayerStateMachine.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 99f098b6660da2c4f8ff06b5043a254b \ No newline at end of file