using CSNetwork.GPDataType; using System; using TMPro; using Unity.VisualScripting; using UnityEngine; 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; } } public struct OtherPlayer_Move_Info { // Bounding sphere of avator public A3DVECTOR3 vCenter; public A3DVECTOR3 vExts; public float fStepHeight; public A3DVECTOR3 vVelocity; public float t; public bool bTraceGround; // Whether trace the ground public bool bTestTrnOnly; // Trace terrain only public A3DVECTOR3 vecGroundNormal; // if bTraceGround is true, this will contain the ground normal when returned };