using BrewMonster.Network; using BrewMonster.Scripts; using BrewMonster.Scripts.World; using CSNetwork.GPDataType; using System; using UnityEngine; using WORD = System.UInt16; namespace BrewMonster { 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; } = 1 << 7; public static LayerMask TerrainMask { get; set; } = 1 << 6; public static LayerMask WaterMask { get; set; } = 1 << 8; public static RaycastHit[] hits = new RaycastHit[5]; public static RaycastHit[] fHitsTerrain = new RaycastHit[5]; public static RaycastHit[] fHitsWater = new RaycastHit[5]; const float LOCAL_EPSILON = 1e-5f; const float FLY_MAX_HEIGHT = 800.0f; // ·ÉÐеÄ×î´ó¸ß¶È£¡ 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; // change this array when some new submap can go! static uint[,] available_maps = { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {1, 1, 0, 0, 0, 0, 0, 0}, }; static uint[,] available_maps4x4 = { {0, 0, 0, 0}, {0, 1, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, }; static uint[,] available_maps3x3 = { {0, 0, 0}, {0, 1, 0}, {0, 0, 0}, }; static uint[,] available_maps2x2 = { {1, 1}, {0, 0}, }; static uint[,] available_maps_137 = { {0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0}, }; static uint[,] available_maps_161 = { {0, 0, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, }; static uint[,] available_maps_162 = { {0, 0, 0, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}, }; static uint[,] available_maps_163 = { {0, 0, 0, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 0}, }; //[Flags] public static class CDR_EVN { public const int CDR_BRUSH = 0x1, CDR_TERRAIN = 0x2, CDR_WATER = 0x4; } static LayerMask UsedMask_Ground() => TerrainMask; static RaycastHitDistanceComparer raycastHitDistanceComparer = new RaycastHitDistanceComparer(); public static bool CollideWithEnv(ref env_trace_t pEnvTrc, bool isNeedCheckGr = true) { pEnvTrc.fFraction = 100.0f; pEnvTrc.bStartSolid = false; pEnvTrc.dwClsFlag = 0; Vector3 vStart = EC_Utility.ToVector3(pEnvTrc.vStart); Vector3 vExt = EC_Utility.ToVector3(pEnvTrc.vExt); Vector3 vDelta = EC_Utility.ToVector3(pEnvTrc.vDelta); Vector3 vTerStart = EC_Utility.ToVector3(pEnvTrc.vTerStart); Vector3 vWatStart = EC_Utility.ToVector3(pEnvTrc.vWatStart); Vector3 dir = Vector3.zero; int countHits = 0; if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_BRUSH) == CDR_EVN.CDR_BRUSH) { float fFractionBrush = 100f; Vector3 v3Normal = Vector3.zero; dir = vDelta; countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask); for(int i = 0; i < countHits; i++) { if (hits[i].distance > 0f) { float fFraction = (hits[i].distance) / vDelta.magnitude; if(fFraction < fFractionBrush) { fFractionBrush = fFraction; v3Normal = hits[i].normal; } } } pEnvTrc.fFraction = fFractionBrush; pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(v3Normal); pEnvTrc.dwClsFlag = CDR_EVN.CDR_BRUSH; } if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_TERRAIN) == CDR_EVN.CDR_TERRAIN) { float fFractionTerrain = 100f; dir = vTerStart + vDelta; countHits = 0; if (isNeedCheckGr) { countHits = Physics.RaycastNonAlloc(vStart, Vector3.down, fHitsTerrain, vExt.y, TerrainMask); if(countHits > 0 && fHitsTerrain[0].point.y >= vTerStart.y) { vTerStart.y = fHitsTerrain[0].point.y; } } countHits = Physics.RaycastNonAlloc(vTerStart, dir.normalized, fHitsTerrain, vDelta.magnitude, TerrainMask); if ((countHits > 0 && Vector3.Distance(vTerStart, fHitsTerrain[0].point) > 0.0009f)) { fFractionTerrain = (fHitsTerrain[0].distance) / vDelta.magnitude; pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(fHitsTerrain[0].normal); pEnvTrc.dwClsFlag = CDR_EVN.CDR_TERRAIN; } if (fFractionTerrain >= (1.0f + 1E-4f) && fFractionTerrain < pEnvTrc.fFraction) { hits = fHitsTerrain; pEnvTrc.fFraction = fFractionTerrain; } } if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER) { float fFraction = 100f; Vector3 vWatNormal = Vector3.zero; bool bStart = false; if (pEnvTrc.bWaterSolid && (vDelta.y > LOCAL_EPSILON)) { fFraction = 0; } else if (!pEnvTrc.bWaterSolid && (vDelta.y < -LOCAL_EPSILON)) { fFraction = 0; } else if (vDelta.y < LOCAL_EPSILON && vDelta.y > -LOCAL_EPSILON) { //parallel the water plane fFraction = 0; } else { float h0 = 0f; int countHits0 = Physics.RaycastNonAlloc(vWatStart + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask); if (countHits0 > 0) { h0 = fHitsWater[0].point.y; } float h1 = 0f; countHits0 = Physics.RaycastNonAlloc((vWatStart + vDelta) + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask); if(countHits0 > 0) { h1 = fHitsWater[0].point.y; } float hWater = Mathf.Max(h0, h1); vWatNormal = Vector3.up; float t = (hWater - vWatStart.y) / pEnvTrc.vDelta.y; if (t >= 0.0f && t <= 1.0f) { fFraction = Mathf.Max(0.0f, t - 1E-2f); if (pEnvTrc.bWaterSolid && h0 > vStart.y) { fFraction = 0.0f; bStart = true; } if (!pEnvTrc.bWaterSolid && h0 < vStart.y) { fFraction = 0.0f; bStart = true; } if(fFraction < pEnvTrc.fFraction) { pEnvTrc.fFraction = fFraction; pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(vWatNormal); pEnvTrc.bStartSolid = bStart; pEnvTrc.dwClsFlag = CDR_EVN.CDR_WATER; } } } } return (pEnvTrc.fFraction < 1.0f + 1E-4f); } // == 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.zero; bSupport = false; float dist = fDeltaY + vExt.y;//Mathf.Max(fDeltaY, 0f) + vExt.y; Vector3 origin = vStart /*+ Vector3.down * vExt.y*/; int countHits = Physics.RaycastNonAlloc(origin, Vector3.down, hits, dist, mask); if (countHits > 0) { float minDistance = 100f; int idx = 0; for (int i = 0; i < countHits; i++) { if (hits[i].collider != null && hits[i].distance > 0f) { if(hits[i].distance < minDistance) { minDistance = hits[i].distance; idx = i; } } } vHitNormal = hits[idx].normal; vEnd = new Vector3(vStart.x, hits[idx].point.y + vExt.y, vStart.z); bSupport = (vHitNormal.y >= 0f); return true; } return true; } // ======= STATIC OnGroundMove – GIỮ NGUYÊN VAI TRÒ TOÀN CỤC (C API) ======= //public static void OnGroundMove(ref CDR_INFO CDRInfo) //{ // CDRInfo.fMoveDist = 0.0f; // bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh); // if (CDRInfo.fYVel < VEL_EPSILON && CDRInfo.fYVel > -VEL_EPSILON && CDRInfo.fSpeed < VEL_EPSILON && CDRInfo.fSpeed > -VEL_EPSILON && !bFreeFall) // return; // float fYVel = CDRInfo.fYVel; // bool bJump = (fYVel > 0.5f); // Vector3 vVelocity = CDRInfo.fSpeed * EC_Utility.ToVector3(CDRInfo.vXOZVelDir) + fYVel * Vector3.up; // if (bFreeFall) // { // vVelocity += -CDRInfo.fGravityAccel * CDRInfo.t * Vector3.up; // fYVel += -CDRInfo.fGravityAccel * CDRInfo.t; // } // A3DVECTOR3 vVelDir = EC_Utility.ToA3DVECTOR3(vVelocity); // float fVelSpeed = vVelDir.Normalize(); // if (!bFreeFall) // { // if (fVelSpeed > VEL_MAX_SPEED) // { // fVelSpeed = VEL_MAX_SPEED; // } // } // vVelocity = EC_Utility.ToVector3(vVelDir) * fVelSpeed; // float dtp = DotProduct(vVelDir, CDRInfo.vTPNormal); // if (dtp < 0f || !bJump) // { // vVelocity = EC_Utility.ToVector3((vVelDir -(CDRInfo.vTPNormal) * dtp - (CDRInfo.vTPNormal) * dtp * 0.01f) * fVelSpeed); // } // CDRInfo.vAbsVelocity = EC_Utility.ToA3DVECTOR3(vVelocity); // Vector3 vStart = EC_Utility.ToVector3(CDRInfo.vCenter); // Vector3 vExt = EC_Utility.ToVector3(CDRInfo.vExtent); // float fTime = CDRInfo.t; // Vector3 vDelta, vNormal = Vector3.zero, vFinalPos = vStart; // bool bPull = false; // bool bTryPull = false; // int nTry = 0; // LayerMask mask = UsedMask_Ground() | BrushMask; // env_trace_t trcInfo = new env_trace_t(); // trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH; // trcInfo.vExt = CDRInfo.vExtent; // int countHits = 0; // while (nTry < 1) // { // vDelta = vVelocity * (fTime); // float fDeltaDist = vDelta.magnitude; // Vector3 posFoot = vStart - Vector3.up * vExt.y; // vFinalPos = vStart; // if (fDeltaDist < DIST_EPSILON) break; // countHits = Physics.RaycastNonAlloc(vStart, (Vector3.down * vExt.y).normalized, hits, vExt.y, mask); // if (countHits > 0) // { // if (hits[0].point.y >= posFoot.y) // { // posFoot.y = hits[0].point.y; // } // } // //Debug.LogError("fDeltaDist = " + fDeltaDist + " vVelocity = " + vVelocity + " fTime = " + fTime + " speed = " + (fDeltaDist / fTime) + " posFoot + vDelta = " + (posFoot + vDelta) + " posFoot = " + posFoot); // countHits = Physics.RaycastNonAlloc(posFoot, (posFoot + vDelta).normalized, hits, fDeltaDist, mask); // bool bClear = !(countHits > 0); // nTry++; // if (bClear || (countHits > 0 && Vector3.Distance(hits[0].point, posFoot) < 0.0009f)) // Is 0.0009f the tolerance used to check if two points are the same? // { // vFinalPos = vStart + vDelta; // CDRInfo.fMoveDist += fDeltaDist; // break; // } // vStart = hits[0].point + Vector3.up * vExt.y; // vFinalPos = vStart; // countHits = Physics.RaycastNonAlloc(vStart, (Vector3.down).normalized, hits, vExt.y, mask); // if (countHits > 0) // { // vNormal = hits[0].normal; // } // else // { // vNormal = Vector3.zero; // } // // Step-up (giữ tinh thần bản gốc) // if (!bFreeFall && !bTryPull && !bJump) // { // posFoot = vStart - Vector3.up * vExt.y; // countHits = Physics.RaycastNonAlloc(vStart, (vStart + Vector3.down).normalized, hits, vExt.y, mask); // if (countHits > 0) // { // if (hits[0].point.y > posFoot.y) // { // posFoot.y = hits[0].point.y; // } // } // countHits = Physics.RaycastNonAlloc(posFoot, (Vector3.up).normalized, hits, CDRInfo.fStepHeight, mask); // bPull = !(countHits > 0); // if (bPull) // { // vStart += Vector3.up * CDRInfo.fStepHeight; // posFoot = vStart - Vector3.up * vExt.y; // fDeltaDist = (vVelocity.normalized).magnitude; // countHits = Physics.RaycastNonAlloc(posFoot, (posFoot + vVelocity).normalized, hits, fDeltaDist, mask); // bool bMove = !(countHits > 0); // if (!bMove) // { // fDeltaDist *= Vector3.Distance(vFinalPos, (hits[0].point + Vector3.up * vExt.y)) / fDeltaDist; // vFinalPos = hits[0].point + Vector3.up * vExt.y; // } // else // { // vFinalPos += vDelta; // } // if (fDeltaDist < (vExt.x * vExt.x * 4)) // { // vFinalPos.y -= CDRInfo.fStepHeight; // bPull = false; // } // } // bTryPull = true; // } // if (!bPull) // { // fVelSpeed = Normalize(EC_Utility.ToA3DVECTOR3(vVelocity), ref vVelDir); // fVelSpeed *= (1 - nTry * 0.1f); // dtp = Vector3.Dot(vNormal, EC_Utility.ToVector3(vVelDir)); // float fRelSpeed = Mathf.Min(fVelSpeed, 5.0f); // if (dtp >= 0f && dtp < 1e-4f) // { // vVelocity += vNormal * VEL_REFLECT * fRelSpeed; // } // else // { // vVelocity = (EC_Utility.ToVector3(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; // //mask = BrushMask; // 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 = new A3DVECTOR3(0, 1, 0); // return; // } // if (bSupport) // { // vFinal = vEnd; // if (!bJump) // { // vTPNormal = groundNormal; // } // } // } // if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f) || (!bJump && fYVel > 0.0f)) // { // fYVel = 0.0f; // } // vDelta = vFinalPos - EC_Utility.ToVector3(CDRInfo.vCenter); // CDRInfo.fMoveDist = vDelta.magnitude; // CDRInfo.vCenter = EC_Utility.ToA3DVECTOR3(vFinal); // CDRInfo.fYVel = fYVel; // CDRInfo.vTPNormal = EC_Utility.ToA3DVECTOR3(vTPNormal); //} public static void OnGroundMove(ref CDR_INFO CDRInfo) { float VEL_EPSILON = 1E-4f; float DIST_EPSILON = 1e-4f; float NORMAL_EPSILON = 1e-2f; float MAX_TRY = 4; float VEL_MAX_SPEED = 200.0f; float VEL_REFLECT = 0.3f; CDRInfo.fMoveDist = 0.0f; //clear the moving dist bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh); if (CDRInfo.fYVel < VEL_EPSILON && CDRInfo.fYVel > -VEL_EPSILON && CDRInfo.fSpeed < VEL_EPSILON && CDRInfo.fSpeed > -VEL_EPSILON && !bFreeFall) { return; } float fYVel = CDRInfo.fYVel; //save the y velocity; //@todo : refine the speed to determine the jumping state. By Kuiwu[14/9/2005] bool bJump = (fYVel > 0.5f); //bool bJump = (fYVel > CDRInfo.fGravityAccel * 0.1f); A3DVECTOR3 vVelocity = new A3DVECTOR3(CDRInfo.fSpeed* CDRInfo.vXOZVelDir +fYVel * GPDataTypeHelper.g_vAxisY ); if (bFreeFall) { vVelocity -= (CDRInfo.fGravityAccel * CDRInfo.t * GPDataTypeHelper.g_vAxisY); fYVel -= CDRInfo.fGravityAccel * CDRInfo.t; } A3DVECTOR3 vVelDir = (vVelocity); float fVelSpeed = vVelDir.Normalize(); if (!bFreeFall) { AAssist.a_ClampRoof(ref fVelSpeed, VEL_MAX_SPEED); } vVelocity = vVelDir * fVelSpeed; //@note : clip the velocity or dir? By Kuiwu[8/9/2005] float dtp = DotProduct(vVelDir, CDRInfo.vTPNormal); if (dtp < 0.0f || !bJump) { vVelocity = (vVelDir - CDRInfo.vTPNormal * dtp - CDRInfo.vTPNormal * dtp * 0.01f) * fVelSpeed; //a_LogOutput(1, "dtp < 0.0f || !bJump vVelocity = (%f, %f, %f) || CDRInfo.vTPNormal = (%f, %f, %f)", vVelocity.x, vVelocity.y, vVelocity.z, CDRInfo.vTPNormal.x, CDRInfo.vTPNormal.y, CDRInfo.vTPNormal.z); } CDRInfo.vAbsVelocity = vVelocity; A3DVECTOR3 vStart = (CDRInfo.vCenter); A3DVECTOR3 vExt = (CDRInfo.vExtent); float fTime = CDRInfo.t; //A3DVECTOR3 vDelta(vVelocity * fTime); A3DVECTOR3 vDelta; bool bClear = true; int nTry = 0; env_trace_t trcInfo = new env_trace_t(); trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH; trcInfo.vExt = vExt; //A3DVECTOR3 vNormal, vFinalPos(vStart); A3DVECTOR3 vNormal, vFinalPos = new A3DVECTOR3(); bool bPull = false; bool bTryPull = false; while (nTry < MAX_TRY) { //hits = new RaycastHit[5]; vDelta = vVelocity * fTime; vFinalPos = vStart; float fDeltaDist = vDelta.Magnitude(); //a_LogOutput(1, "fDeltaDist = %f || vVelocity = (%f, %f, %f) || fTime = %f", fDeltaDist, vVelocity.x, vVelocity.y, vVelocity.z, fTime); //if (vDelta.SquaredMagnitude() < DIST_EPSILON ) if (fDeltaDist < DIST_EPSILON) { break; } trcInfo.vStart = vStart; trcInfo.vDelta = vDelta; trcInfo.vTerStart = vStart; trcInfo.vTerStart.y -= vExt.y; //foot bClear = !CollideWithEnv(ref trcInfo); ++nTry; if (trcInfo.bStartSolid) { CDRInfo.fMoveDist = 0.0f; if (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh) { CDRInfo.vTPNormal = new A3DVECTOR3(0.0f, 1.0f, 0.0f); } //a_LogOutput(1, "bClear = true but trcInfo.bStartSolid = true"); return; } if (bClear) { //a_LogOutput(1, "bClear = true"); vFinalPos = vStart + vDelta; break; } vStart += vDelta * trcInfo.fFraction; fTime -= fTime * trcInfo.fFraction; vNormal = trcInfo.vHitNormal; if (!bFreeFall && !bTryPull && !bJump) { env_trace_t tmpInfo = new env_trace_t(); tmpInfo.vStart = vStart; tmpInfo.vDelta = new A3DVECTOR3(0.0f, CDRInfo.fStepHeight, 0.0f); tmpInfo.vExt = new A3DVECTOR3(vExt.x, 0.01f, vExt.z); //@note : need check terrain?? By Kuiwu[8/10/2005] tmpInfo.dwCheckFlag = CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_TERRAIN; tmpInfo.vTerStart = vStart; tmpInfo.vTerStart.y -= vExt.y; //bPull = !CollideWithEnv(ref tmpInfo); bPull = !CheckFootStepUp(tmpInfo); //int count1 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(tmpInfo.vStart), EC_Utility.ToVector3(tmpInfo.vDelta).normalized, hits, EC_Utility.ToVector3(tmpInfo.vDelta).magnitude, BrushMask | TerrainMask); //bPull = !(count1 < 0); //if(count1 > 0) //{ // Debug.DrawLine(EC_Utility.ToVector3(tmpInfo.vStart), hits[0].point, Color.yellow, 10f); //} //if(bPull == false) //{ // BoxCastDrawer.Draw(EC_Utility.ToVector3(tmpInfo.vStart), EC_Utility.ToVector3(tmpInfo.vExt), EC_Utility.ToVector3(tmpInfo.vDelta).normalized, // Quaternion.identity, EC_Utility.ToVector3(tmpInfo.vDelta).magnitude, hits, 1, nTry == 0 ? Color.red : Color.green, 10f); //} if (bPull) { vStart.y += CDRInfo.fStepHeight; //vDelta = vVelocity * fTime; vDelta = vVelocity; tmpInfo.vStart = vStart; tmpInfo.vDelta = vDelta; tmpInfo.vTerStart = vStart; tmpInfo.vTerStart.y -= vExt.y; bool bMove = !CollideWithEnv(ref tmpInfo, false); if (!bMove) { vDelta *= tmpInfo.fFraction; } float value = vDelta.SquaredMagnitude(); if (value < (vExt.x * vExt.x * 4)) { vStart.y -= CDRInfo.fStepHeight; bPull = false; } } bTryPull = true; } if (!bPull) { fVelSpeed = Normalize(vVelocity, ref vVelDir); fVelSpeed *= (1 - nTry * 0.1f); dtp = DotProduct(vNormal, vVelDir); float fRelSpeed = EC_Utility.a_Min(fVelSpeed, 5.0f); if ((dtp < 1E-4f) && (dtp >= 0.0f)) {//@note : special parallel tangent plane case, rarely happen. By Kuiwu[20/10/2005] vVelocity += vNormal * VEL_REFLECT * fRelSpeed; } else { vVelocity = (vVelDir - vNormal * dtp) * fVelSpeed - vNormal * dtp * VEL_REFLECT * fRelSpeed; //vVelocity = (vVelDir - vNormal * dtp - vNormal*dtp * VEL_REFLECT) * fVelSpeed; } //CDRInfo.fYVel = vVelocity.y; if (fYVel > VEL_EPSILON) { if ((vNormal.y >= CDRInfo.fSlopeThresh || vNormal.y < -NORMAL_EPSILON)) { fYVel = 0.0f; } } else if (fYVel < -VEL_EPSILON) { if ((vNormal.y >= CDRInfo.fSlopeThresh)) { fYVel = 0.0f; } } else { //@note : additional handle something??? By Kuiwu[13/9/2005] } } } ////@note : prevent moving to the invalid area. By Kuiwu[20/9/2005] //if (!IsPosInAvailableMap(vFinalPos)) //{ // CDRInfo.fMoveDist = 0.0f; // return; //} A3DVECTOR3 vTPNormal = new A3DVECTOR3(); vTPNormal.Clear(); ground_trace_t groundTrc = new ground_trace_t(); groundTrc.vStart = vFinalPos; groundTrc.vExt = vExt; groundTrc.fDeltaY = 0.3f; if (bPull) { groundTrc.fDeltaY = CDRInfo.fStepHeight + 0.1f; } if (bJump) { groundTrc.fDeltaY = 0.0f; } if (!RetrieveSupportPlane(ref groundTrc, true)) {//@note : do NOT change position. By Kuiwu[14/9/2005] CDRInfo.fMoveDist = 0.0f; // if (groundTrc.bSupport) // { // CDRInfo.vTPNormal = groundTrc.vHitNormal; // } CDRInfo.vTPNormal = new A3DVECTOR3(0.0f, 1.0f, 0.0f); return; } if (groundTrc.bSupport) { vFinalPos = groundTrc.vEnd; if (!bJump) { vTPNormal = groundTrc.vHitNormal; } } if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f) || (!bJump && fYVel > 0.0f)) { fYVel = 0.0f; } CDRInfo.vCenter = vFinalPos; CDRInfo.fYVel = fYVel; //set back the y velocity CDRInfo.vTPNormal = vTPNormal; } private static bool CheckFootStepUp(env_trace_t pEnvTrc) { Vector3 vExt = EC_Utility.ToVector3(pEnvTrc.vExt); Vector3 vStart = EC_Utility.ToVector3(pEnvTrc.vStart); Vector3 vDelta = EC_Utility.ToVector3(pEnvTrc.vDelta); Vector3 dir = vDelta; int countHits = 0; countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask); if (countHits > 0) { for(int i = 0; i < countHits; i++) { if (hits[i].collider != null && hits[i].point.y > (vStart.y + vExt.y)) { return true; } } } return false; } //@desc : used to retrieve support plane (ground or brush), By Kuiwu[12/9/2005] public struct ground_trace_t { public A3DVECTOR3 vStart; public A3DVECTOR3 vExt; public float fDeltaY; //down (-y) public A3DVECTOR3 vEnd; public A3DVECTOR3 vHitNormal; public bool bSupport; //false if ground missed }; public static bool RetrieveSupportPlane(ref ground_trace_t pTrc, bool check = false) { A3DVECTOR3 vTerrainPos = new A3DVECTOR3(), vTerrainNormal = new A3DVECTOR3(); GetTerrainInfo(pTrc.vStart, ref vTerrainPos, ref vTerrainNormal); pTrc.bSupport = false; // pTrc.vEnd = pTrc.vStart; // pTrc.vEnd.y -= pTrc.fDeltaY; BrushTraceInfo trcInfo = new BrushTraceInfo(); trcInfo.Init(pTrc.vStart, new A3DVECTOR3(0.0f, -pTrc.fDeltaY, 0.0f), pTrc.vExt); Vector3 hitPoint = new Vector3(); if (AABBCollideWithBrush(ref trcInfo, ref hitPoint, check)) { if (trcInfo.bStartSolid) { return false; } pTrc.vEnd = trcInfo.vStart + trcInfo.vDelta * trcInfo.fFraction; pTrc.vEnd.y = hitPoint.y + pTrc.vExt.y + 0.1f; //if((pTrc.vEnd.y + 0.17f) < (hitPoint.y + pTrc.vExt.y + 0.1f)) //{ // pTrc.vEnd.y = hitPoint.y + pTrc.vExt.y + 0.1f; // need add delta Y for Support Plane when check with brush mask. //} //else //{ // pTrc.vEnd.y += 0.17f; //} pTrc.vHitNormal = trcInfo.normal; float fUp = pTrc.vExt.y; if (pTrc.vEnd.y > vTerrainPos.y + fUp) { pTrc.vStart = trcInfo.vStart; pTrc.bSupport = true; return true; } } //if (vTerrainPos.y > pTrc.vStart.y- pTrc.vExt.y ) if (vTerrainPos.y > pTrc.vStart.y - pTrc.vExt.y || (vTerrainPos.y <= pTrc.vStart.y - pTrc.vExt.y && vTerrainPos.y >= pTrc.vStart.y - pTrc.vExt.y - pTrc.fDeltaY)) { pTrc.vEnd = vTerrainPos; //@note : maybe sink in the sleep ground, but this is what I need. By Kuiwu[14/9/2005] pTrc.vEnd.y += (pTrc.vExt.y + 0.01f); A3DVECTOR3 vDelta = new A3DVECTOR3(pTrc.vEnd -pTrc.vStart); trcInfo.Init(pTrc.vStart, vDelta, pTrc.vExt); pTrc.vHitNormal = vTerrainNormal; pTrc.bSupport = true; return !AABBCollideWithBrush(ref trcInfo, ref hitPoint, check); } return true; } public static void GetTerrainInfo(A3DVECTOR3 vPos, ref A3DVECTOR3 vPosOnSurface, ref A3DVECTOR3 vNormal) { vPosOnSurface = vPos; int countHits = 0; countHits = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vPosOnSurface), Vector3.down, fHitsTerrain, maxDistance: 1000f, TerrainMask); if (countHits > 0) { int idx = 0; float maxDistance = 100f; //System.Array.Sort(fHitsTerrain, 0, fHitsTerrain.Length, raycastHitDistanceComparer); for (int i = 0; i < countHits; i++) { if (fHitsTerrain[i].collider != null && fHitsTerrain[i].distance > 0f) { if(fHitsTerrain[i].distance < maxDistance) { maxDistance = fHitsTerrain[i].distance; idx = i; } } } vPosOnSurface.y = fHitsTerrain[idx].point.y; vNormal = EC_Utility.ToA3DVECTOR3(fHitsTerrain[idx].normal); return; } } // Get normalize static float Normalize(A3DVECTOR3 vIn, ref A3DVECTOR3 vOut) { float fMag = vIn.Magnitude(); if (fMag < 1e-6 && fMag > -1e-6) { vOut.Clear(); fMag = 0.0f; } else { float f = 1.0f / fMag; vOut = vIn * f; } return fMag; } static bool SegmentTriangleIntersect(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, A3DVECTOR3[] vert, ref float fFraction, bool bCull) { float dist = 0f; A3DVECTOR3 vDir = new A3DVECTOR3(vDelta); dist = vDir.Normalize(); if (dist < 1E-5f) { //assert(0 && "too small dist!"); fFraction = 0.0f; return true; } float t = 0f, u = 0f, v = 0f; if (RayTriangleIntersect(vStart, vDir, vert, ref t, ref u, ref v, bCull) && (t >= 0.0f) && (t <= dist)) { //fFraction = t/dist; //fFraction = a_Max( 0.0f, fFraction -1E-4f); //put back fFraction = (t - 5E-4f) / dist; AAssist.a_ClampFloor(ref fFraction, 0.0f); return true; } return false; } static bool RayTriangleIntersect(A3DVECTOR3 vOrigin, A3DVECTOR3 vDir, A3DVECTOR3[] vert, ref float t, ref float u, ref float v, bool bCull) { // find vectors for two edges sharing vert0 A3DVECTOR3 edge1 = vert[1] - vert[0]; A3DVECTOR3 edge2 = vert[2] - vert[0]; // begin calculating determinant - also used to calculate U parameter A3DVECTOR3 pvec = A3DVECTOR3.CrossProduct(vDir, edge2); // if determinant is near zero, ray lies in plane of triangle float det = A3DVECTOR3.DotProduct(edge1, pvec); if (bCull) { if (det < LOCAL_EPSILON) return false; // From here, det is > 0. // Calculate distance from vert0 to ray origin A3DVECTOR3 tvec = vOrigin - vert[0]; // Calculate U parameter and test bounds u = A3DVECTOR3.DotProduct(tvec, pvec); if ((u < 0.0f) || (u > det)) return false; // prepare to test V parameter A3DVECTOR3 qvec = A3DVECTOR3.CrossProduct(tvec, edge1); // calculate V parameter and test bounds v = A3DVECTOR3.DotProduct(vDir, qvec); if ((v < 0.0f) || (u + v > det)) return false; // calculate t, ray intersects triangle t = A3DVECTOR3.DotProduct(edge2, qvec); // Det > 0 so we can early exit here // Intersection point is valid if distance is positive // (else it can just be a face behind the orig point) if (t < 0.0f) { return false; } float OneOverDet = 1.0f / det; t *= OneOverDet; u *= OneOverDet; v *= OneOverDet; } else { if (det > -LOCAL_EPSILON && det < LOCAL_EPSILON) return false; float OneOverDet = 1.0f / det; // Calculate distance from vert0 to ray origin A3DVECTOR3 tvec = vOrigin - vert[0]; // calculate U parameter and test bounds u = A3DVECTOR3.DotProduct(tvec, pvec) * OneOverDet; if ((u < 0.0f) || (u > 1.0f)) return false; // prepare to test V parameter A3DVECTOR3 qvec = A3DVECTOR3.CrossProduct(tvec, edge1); // calculate V parameter and test bounds v = A3DVECTOR3.DotProduct(vDir, qvec) * OneOverDet; if ((v < 0.0f) || (u + v > 1.0f)) return false; // calculate t, ray intersects triangle t = A3DVECTOR3.DotProduct(edge2, qvec) * OneOverDet; } return true; } public static bool CollideWithTerrain(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, ref float fFraction, ref A3DVECTOR3 vHitNormal, ref bool bStart) { CECWorld pWorld = CECGameRun.Instance.GetWorld(); //g_pGame.GetGameRun().GetWorld(); A3DTerrain2 pTerrain = pWorld.GetTerrain(); bStart = false; float h1 = pTerrain.GetPosHeight(vStart, ref vHitNormal); if (h1 > vStart.y + 1E-4f) {//start under terrain bStart = true; fFraction = 0.0f; return true; } int nWid, nHei; // in grid, 2 meters float fMag = vDelta.Magnitude(); nWid = (int)Math.Ceiling(fMag / 2.0f); nWid = Math.Max(3, nWid); nHei = nWid; int nTriangles = nWid * nHei * 2; A3DVECTOR3[] pVerts = new A3DVECTOR3[(nWid + 1) * (nHei + 1)]; //assert(pVerts != NULL); //memset(pVerts, 0, sizeof(A3DVECTOR3)* (nWid + 1) * (nHei + 1)); WORD[] pIndices = new WORD[nTriangles * 3]; //assert(pIndices != NULL); //memset(pIndices, 0, sizeof(WORD)* nTriangles * 3); if (!pTerrain.GetFacesOfArea(vStart, nWid, nHei, ref pVerts, ref pIndices)) { //a_freetemp(pVerts); // a_freetemp(pIndices); return false; } int i; A3DVECTOR3[] vert = new A3DVECTOR3[3]; //@note : Here init the fraction. By Kuiwu[9/10/2005] fFraction = 100.0f; float tmpFraction = fFraction; for (i = 0; i < nTriangles; i++) { vert[0] = pVerts[pIndices[i * 3]]; vert[1] = pVerts[pIndices[i * 3 + 1]]; vert[2] = pVerts[pIndices[i * 3 + 2]]; //A3DVECTOR3 vPt; //@note: Tomas Moller's JGT code : By Kuiwu[9/10/2005] //@note: discard the engine version because it put back the hit point too much. By Kuiwu[13/10/2005] // if(CLS_RayToTriangle(vStart, vDelta, *vert[0], *vert[1], *vert[2], vPt, true, &tmpFraction) // && (tmpFraction <= 1.0f) && (tmpFraction < fFraction)) if (SegmentTriangleIntersect(vStart, vDelta, vert, ref tmpFraction, true) && (tmpFraction < fFraction)) { //get the triangle normal A3DVECTOR3 vEdge1 = vert[1] - vert[0]; A3DVECTOR3 vEdge2 = vert[2] - vert[0]; vHitNormal = A3DVECTOR3.CrossProduct(vEdge1, vEdge2); vHitNormal.Normalize(); //@note : may be redundant, but to assure. By Kuiwu[17/10/2005] A3DVECTOR3 vDir = new A3DVECTOR3(); A3DVECTOR3.Normalize(vDelta, out vDir); if (A3DVECTOR3.DotProduct(vHitNormal, vDir) > 0.01f) {//leave the hit plane //assert(0 && "hit a plane with same direction!"); continue; } fFraction = Math.Max(0.0f, tmpFraction); } } //a_freetemp(pVerts); // a_freetemp(pIndices); return (fFraction <= 1.0f); } public static bool AABBCollideWithBrush(ref BrushTraceInfo brushTraceInfo) { Vector3 vExt = EC_Utility.ToVector3(brushTraceInfo.vExtents); Vector3 vStart = EC_Utility.ToVector3(brushTraceInfo.vStart); Vector3 vDelta = EC_Utility.ToVector3(brushTraceInfo.vDelta); Vector3 dir = vDelta; int countHits = 0; float fFraction = 100f; Vector3 normal = Vector3.zero; Vector3 hitP = Vector3.zero; countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask); for (int i = 0; i < countHits; i++) { if (hits[i].collider != null && hits[i].distance > 0f) { float value = (hits[i].distance) / vDelta.magnitude; if (value < fFraction) { fFraction = value; normal = hits[i].normal; hitP = hits[i].point; } } } if (fFraction < 100f) { if (fFraction > 0f && fFraction <= 1f) { brushTraceInfo.fFraction = fFraction; brushTraceInfo.normal = EC_Utility.ToA3DVECTOR3(normal); } if (Math.Abs(fFraction - 0f) < float.Epsilon) { brushTraceInfo.bStartSolid = true; } return true; } brushTraceInfo.normal = new A3DVECTOR3(0f); brushTraceInfo.fFraction = 100f; return false; } public static bool AABBCollideWithBrush(ref BrushTraceInfo brushTraceInfo, ref Vector3 hitPoint, bool check) { Vector3 vExt = EC_Utility.ToVector3(brushTraceInfo.vExtents); Vector3 vStart = EC_Utility.ToVector3(brushTraceInfo.vStart); Vector3 vDelta = EC_Utility.ToVector3(brushTraceInfo.vDelta); Vector3 dir = vDelta; int countHits = 0; float fFraction = 100f; Vector3 normal = Vector3.zero; Vector3 hitP = Vector3.zero; countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask); for (int i = 0; i < countHits; i++) { if (hits[i].collider != null && hits[i].distance > 0f) { float value = (hits[i].distance) / vDelta.magnitude; if(value < fFraction) { fFraction = value; normal = hits[i].normal; hitP = hits[i].point; } } } if(fFraction < 100f) { if(fFraction > 0f && fFraction <= 1f) { brushTraceInfo.fFraction = fFraction; brushTraceInfo.normal = EC_Utility.ToA3DVECTOR3(normal); hitPoint = hitP; } if (Math.Abs(fFraction - 0f) < float.Epsilon) { brushTraceInfo.bStartSolid = true; } return true; } brushTraceInfo.normal = new A3DVECTOR3(0f); brushTraceInfo.fFraction = 100f; hitPoint = Vector3.zero; return false; } public static void OnAirMove(ref ON_AIR_CDR_INFO awmInfo) { //assert(0 && "Not ready yet"); if (awmInfo.bOnAir) { AirMove(ref awmInfo); } else { WaterMove(ref awmInfo); } } static void AirMove(ref ON_AIR_CDR_INFO awmInfo) { float DIST_EPSILON = 1e-4f; int MAX_TRY = 4; float VEL_REFLECT = 0.0f; float fTime = awmInfo.t; //@todo : is it necessary to clamp the speed? By Kuiwu[20/9/2005] float fSpeed = awmInfo.fSpeed; if (fSpeed * fTime < DIST_EPSILON) { //@todo : set the output param. By Kuiwu[20/9/2005] return; } A3DVECTOR3 vStart = new A3DVECTOR3(awmInfo.vCenter); A3DVECTOR3 vExt = new A3DVECTOR3(awmInfo.vExtent); A3DVECTOR3 vVelDir = new A3DVECTOR3(awmInfo.vVelDir); float dtp = 0f; A3DVECTOR3 vVelocity = new A3DVECTOR3(vVelDir * fSpeed); if ((dtp = A3DVECTOR3.DotProduct(vVelDir, awmInfo.vTPNormal)) < 0.0f) { //vVelocity = (vVelDir - awmInfo.vTPNormal * dtp - awmInfo.vTPNormal*dtp * 0.01f) * fSpeed; vVelocity = (vVelDir - awmInfo.vTPNormal * dtp) * fSpeed; } A3DVECTOR3 vDelta = new A3DVECTOR3(vVelocity * fTime), vNormal = new A3DVECTOR3(), vFinalPos = new A3DVECTOR3(vStart); int nTry = 0; bool bClear = true; env_trace_t trcInfo = new env_trace_t(); trcInfo.bWaterSolid = true; trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER; trcInfo.vExt = vExt; while (nTry < MAX_TRY) { if (vDelta.SquaredMagnitude() < DIST_EPSILON) { break; } trcInfo.vStart = vStart; trcInfo.vDelta = vDelta; trcInfo.vTerStart = vStart; trcInfo.vTerStart.y -= vExt.y; trcInfo.vWatStart = vStart; trcInfo.vWatStart.y -= vExt.y; bClear = !CollideWithEnv(ref trcInfo); ++nTry; if (bClear) { vFinalPos = vStart + vDelta; break; } vStart += vDelta * trcInfo.fFraction; vFinalPos = vStart; fTime -= fTime * trcInfo.fFraction; vNormal = trcInfo.vHitNormal; fSpeed = Normalize(vVelocity,ref vVelDir); fSpeed *= (1 - nTry * 0.1f); dtp = DotProduct(vNormal, vVelDir); vVelocity = (vVelDir - vNormal * dtp - vNormal * dtp * VEL_REFLECT) * fSpeed; vDelta = vVelocity * fTime; } //@note : prevent moving to the invalid area. By Kuiwu[20/9/2005] if (!IsPosInAvailableMap(vFinalPos)) { //@todo : set some flag to notify the caller? By Kuiwu[20/9/2005] return; } //too high if (vFinalPos.y > FLY_MAX_HEIGHT - 2.0f) { return; } //see if meet height thresh float fDeltaY = awmInfo.fHeightThresh + 0.1f; LayerMask mask = TerrainMask | BrushMask; if (!DoGroundProbe(EC_Utility.ToVector3(vFinalPos), EC_Utility.ToVector3(vExt), fDeltaY, mask, out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport)) { return; } A3DVECTOR3 vTpNormal = new A3DVECTOR3(0.0f); A3DVECTOR3 vOverTp = vFinalPos; bool bAdjust = false; awmInfo.bMeetHeightThresh = true; float fHWater = 0f; int countHits0 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vFinalPos) + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask); if (countHits0 > 0) { fHWater = fHitsWater[0].point.y; } if (bSupport) { bAdjust = true; vOverTp = EC_Utility.ToA3DVECTOR3(vEnd); vTpNormal = EC_Utility.ToA3DVECTOR3(groundNormal); if (fHWater > vEnd.y) { vOverTp.y = fHWater; vTpNormal = GPDataTypeHelper.g_vAxisY; } } else if (vFinalPos.y < fHWater + awmInfo.fHeightThresh) { bAdjust = true; vOverTp = vFinalPos; vOverTp.y = fHWater; vTpNormal = GPDataTypeHelper.g_vAxisY; } if (bAdjust && (vOverTp.y + awmInfo.fHeightThresh > vFinalPos.y)) { BrushTraceInfo brushTrc = new BrushTraceInfo(); vDelta.Clear(); vDelta.y = vOverTp.y + awmInfo.fHeightThresh - vFinalPos.y; float fAllow = (float)Math.Abs(awmInfo.vCenter.y - vFinalPos.y) + 0.001f; fAllow = EC_Utility.a_Max(fAllow, 0.15f); AAssist.a_ClampRoof(ref vDelta.y, fAllow); brushTrc.Init(vFinalPos, vDelta, vExt); if (AABBCollideWithBrush(ref brushTrc)) { vFinalPos += (vDelta * brushTrc.fFraction); } else { vFinalPos += vDelta; } awmInfo.bMeetHeightThresh = (vFinalPos.y - vOverTp.y > awmInfo.fHeightThresh); } awmInfo.vCenter = vFinalPos; awmInfo.vTPNormal = vTpNormal; } static void WaterMove(ref ON_AIR_CDR_INFO awmInfo) { float fTime = awmInfo.t; //@todo : is it necessary to clamp the speed? By Kuiwu[20/9/2005] float fSpeed = awmInfo.fSpeed; if (fSpeed * fTime < DIST_EPSILON) { //@todo : set the output param. By Kuiwu[20/9/2005] return; } A3DVECTOR3 vStart = awmInfo.vCenter; A3DVECTOR3 vExt = awmInfo.vExtent; A3DVECTOR3 vVelDir = awmInfo.vVelDir; float dtp = 0f; A3DVECTOR3 vVelocity = vVelDir* fSpeed; if ((dtp = DotProduct(vVelDir, awmInfo.vTPNormal)) < 0.0f) { vVelocity = (vVelDir - awmInfo.vTPNormal * dtp - awmInfo.vTPNormal * dtp * 0.01f) * fSpeed; } A3DVECTOR3 vDelta = (vVelocity* fTime), vNormal, vFinalPos = vStart; int nTry = 0; bool bClear = true; env_trace_t trcInfo = new env_trace_t(); trcInfo.bWaterSolid = false; trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER; trcInfo.vExt = vExt; while (nTry < 4) { if (vDelta.SquaredMagnitude() < DIST_EPSILON) { break; } trcInfo.vStart = vStart; trcInfo.vDelta = vDelta; trcInfo.vTerStart = vStart; trcInfo.vTerStart.y -= vExt.y; trcInfo.vWatStart = vStart; trcInfo.vWatStart.y += awmInfo.fUnderWaterDistThresh; //shoulder bClear = !CollideWithEnv(ref trcInfo); ++nTry; if (bClear || (trcInfo.bStartSolid && ((trcInfo.dwClsFlag & CDR_EVN.CDR_WATER) != CDR_EVN.CDR_WATER))) { vFinalPos = vStart + vDelta; break; } vStart += vDelta * trcInfo.fFraction; fTime -= fTime * trcInfo.fFraction; fSpeed = Normalize(vVelocity,ref vVelDir); fSpeed *= (1 - nTry * 0.1f); if ((trcInfo.dwClsFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER) { if (trcInfo.bStartSolid) {//rescue from solid //@note : it may cause some problems. By Kuiwu[11/10/2005] float fHWater = 0f; int countHits0 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vStart) + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask); if(countHits0 > 0) { fHWater = fHitsWater[0].point.y; } vStart.y = fHWater; } vVelDir.y = 0.0f; vVelocity = vVelDir * fSpeed; } else { vNormal = trcInfo.vHitNormal; dtp = DotProduct(vNormal, vVelDir); vVelocity = (vVelDir - vNormal * dtp - vNormal * dtp * VEL_REFLECT) * fSpeed; } vDelta = vVelocity * fTime; vFinalPos = vStart; } //@note : prevent moving to the invalid area. By Kuiwu[20/9/2005] if (!IsPosInAvailableMap(vFinalPos)) { //@todo : set some flag to notify the caller? By Kuiwu[20/9/2005] return; } //see if meet height thresh Vector3 v3Start = EC_Utility.ToVector3(vFinalPos); Vector3 v3Ext = EC_Utility.ToVector3(vExt); float fDeltaY = awmInfo.fHeightThresh + 0.1f; if (!DoGroundProbe(v3Start, v3Ext, fDeltaY, UsedMask_Ground(), out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport)) { return; } A3DVECTOR3 vTpNormal = new A3DVECTOR3(0.0f); A3DVECTOR3 vOverTp = vFinalPos; bool bAdjust = false; awmInfo.bMeetHeightThresh = true; if (bSupport) { bAdjust = true; vOverTp = EC_Utility.ToA3DVECTOR3(vEnd); vTpNormal = EC_Utility.ToA3DVECTOR3(groundNormal); } if (bAdjust && (vOverTp.y + awmInfo.fHeightThresh > vFinalPos.y)) { float fHWater = 0f; int countHits = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vFinalPos), Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask); if (countHits > 0) { fHWater = fHitsWater[0].point.y; } float fAllow = fHWater - awmInfo.fUnderWaterDistThresh - vFinalPos.y; if (fAllow > 1E-4f) { vDelta.Clear(); vDelta.y = vOverTp.y + awmInfo.fHeightThresh - vFinalPos.y; fAllow = EC_Utility.a_Min(fAllow, 0.15f); AAssist.a_ClampRoof(ref vDelta.y, fAllow); BrushTraceInfo brushTrc = new BrushTraceInfo(); brushTrc.Init(vFinalPos, vDelta, vExt); if (AABBCollideWithBrush(ref brushTrc)) { vFinalPos += (vDelta * brushTrc.fFraction); } else { vFinalPos += vDelta; } awmInfo.bMeetHeightThresh = (vFinalPos.y - vOverTp.y > awmInfo.fHeightThresh); } } awmInfo.vCenter = vFinalPos; awmInfo.vTPNormal = vTpNormal; } ////////////////////////////////////////////////////////////////////////// // Note by wenfeng, 05-09-16 // This function is only for the Big world but not applicable for the // Instance world! // ////////////////////////////////////////////////////////////////////////// static bool IsPosInAvailableMap(A3DVECTOR3 vPos) { float x, z; int su, sv; //bool bFlag = true; CECWorld pWorld = EC_Game.GetGameRun().GetWorld(); if (pWorld != null) { int idInst = pWorld.GetInstanceID(); CECInstance pInst = EC_Game.GetGameRun().GetInstance(idInst); if (pInst == null) return false; x = vPos.x + pInst.GetColNum() * 512.0f; z = pInst.GetRowNum() * 512.0f - vPos.z; su = (int)(x / 1024.0f); sv = (int)(z / 1024.0f); if (su >= pInst.GetColNum() || su < 0 || sv >= pInst.GetRowNum() || sv < 0) return false; switch (idInst) { case 1: return available_maps[sv, su] != 0 && vPos.x <= 3877.0f; // ½ûÖ¹ëÊ×åÁÙ½ü¿´µ½µØÍ¼±ßÔµ case 121: case 122: return available_maps4x4[sv, su] != 0 ? true : false; case 118: case 119: case 120: case 123: case 125: return available_maps3x3[sv, su] != 0 ? true : false; case 134: return available_maps2x2[sv, su] != 0 ? true : false; case 137: return available_maps_137[sv, su] != 0 ? true : false; case 161: return available_maps_161[sv, su] != 0; case 162: return available_maps_162[sv, su] != 0 ? true : false; case 163: return available_maps_163[sv, su] != 0 ? true : false; default: return true; } } else return true; } static float DotProduct(A3DVECTOR3 v1, A3DVECTOR3 v2) { return v1.x* v2.x + v1.y* v2.y + v1.z* v2.z;} } 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 }; //@desc :used to trace the environment, brush&terrain&water By Kuiwu[8/10/2005] public struct env_trace_t { public A3DVECTOR3 vStart; // brush start public A3DVECTOR3 vExt; public A3DVECTOR3 vDelta; public A3DVECTOR3 vTerStart; public A3DVECTOR3 vWatStart; public uint dwCheckFlag; public bool bWaterSolid; public float fFraction; public A3DVECTOR3 vHitNormal; public bool bStartSolid; //start in solid public uint dwClsFlag; //collision flag }; // for on-air move case //@note : change to AABB. By Kuiwu[22/9/2005] public struct ON_AIR_CDR_INFO { public A3DVECTOR3 vCenter; public A3DVECTOR3 vExtent; // Hold a height from the surface of terrain or building public float fHeightThresh; // Velocity Info public A3DVECTOR3 vVelDir; public float fSpeed; // time span ( sec ) public float t; //@note : SlopeThresh seems useless on air or under water. By Kuiwu[22/9/2005] // Slope Thresh //float fSlopeThresh; // Distance Thresh under the water surface public float fUnderWaterDistThresh; public A3DVECTOR3 vTPNormal; // On air or water, true if on air, false for on water case. public bool bOnAir; // After the move action is done, If the fHeightThresh // still be satisfied, bMeetHeightThresh is set to true. public bool bMeetHeightThresh; }; public static class BoxCastDrawer { /// /// Vẽ BoxCast để Debug /// /// Thời gian tồn tại của đường kẻ (giây). Mặc định là 0 (1 frame) public static void Draw( Vector3 center, Vector3 halfExtents, Vector3 direction, Quaternion orientation, float distance, RaycastHit[] hits, int hitCount, Color color, float duration = 0f) // Thêm tham số duration ở đây { // 1. Tính toán các góc Vector3[] startCorners = GetBoxCorners(center, halfExtents, orientation); float drawDistance = distance; if (hitCount > 0) { float minDistance = distance; for (int i = 0; i < hitCount; i++) { if (hits[i].distance < minDistance && hits[i].distance > 0) minDistance = hits[i].distance; } drawDistance = minDistance; } Vector3 endCenter = center + direction.normalized * drawDistance; Vector3[] endCorners = GetBoxCorners(endCenter, halfExtents, orientation); // 2. Thiết lập màu sắc //Color color = hitCount > 0 ? Color.red : Color.green; //Color sweepColor = new Color(0.5f, 0.5f, 0.5f, 0.5f); // Màu xám mờ Color sweepColor = color; // Màu xám mờ // 3. VẼ TÂM (CENTER) - Hiển thị trục tọa độ Local của hộp DrawCenterAxis(center, orientation, 0.3f, duration); // Tâm bắt đầu DrawCenterAxis(endCenter, orientation, 0.3f, duration); // Tâm kết thúc // 3. Vẽ các đường Line với Duration for (int i = 0; i < 4; i++) { // Vẽ hộp bắt đầu Debug.DrawLine(startCorners[i], startCorners[(i + 1) % 4], color, duration); Debug.DrawLine(startCorners[i + 4], startCorners[((i + 1) % 4) + 4], color, duration); Debug.DrawLine(startCorners[i], startCorners[i + 4], color, duration); // Vẽ hộp kết thúc/va chạm Debug.DrawLine(endCorners[i], endCorners[(i + 1) % 4], color, duration); Debug.DrawLine(endCorners[i + 4], endCorners[((i + 1) % 4) + 4], color, duration); Debug.DrawLine(endCorners[i], endCorners[i + 4], color, duration); // Vẽ các đường nối (Sweep lines) Debug.DrawLine(startCorners[i], endCorners[i], sweepColor, duration); Debug.DrawLine(startCorners[i + 4], endCorners[i + 4], sweepColor, duration); } // 4. Vẽ chi tiết va chạm for (int i = 0; i < hitCount; i++) { // Vẽ pháp tuyến (Normal) tại điểm chạm Debug.DrawRay(hits[i].point, hits[i].normal * 0.5f, Color.yellow, duration); // Vẽ một dấu X nhỏ tại điểm va chạm chính xác DrawCross(hits[i].point, 0.1f, Color.cyan, duration); } } private static void DrawCross(Vector3 point, float size, Color color, float duration) { Debug.DrawLine(point + Vector3.up * size, point - Vector3.up * size, color, duration); Debug.DrawLine(point + Vector3.right * size, point - Vector3.right * size, color, duration); Debug.DrawLine(point + Vector3.forward * size, point - Vector3.forward * size, color, duration); } private static Vector3[] GetBoxCorners(Vector3 center, Vector3 halfExtents, Quaternion orientation) { Vector3[] corners = new Vector3[8]; Vector3 h = halfExtents; corners[0] = center + orientation * new Vector3(-h.x, -h.y, h.z); corners[1] = center + orientation * new Vector3(h.x, -h.y, h.z); corners[2] = center + orientation * new Vector3(h.x, h.y, h.z); corners[3] = center + orientation * new Vector3(-h.x, h.y, h.z); corners[4] = center + orientation * new Vector3(-h.x, -h.y, -h.z); corners[5] = center + orientation * new Vector3(h.x, -h.y, -h.z); corners[6] = center + orientation * new Vector3(h.x, h.y, -h.z); corners[7] = center + orientation * new Vector3(-h.x, h.y, -h.z); return corners; } public static void DrawCenterAxis(Vector3 pos, Quaternion rot, float size, float duration) { Debug.DrawRay(pos, rot * Vector3.right * size, Color.red, duration); Debug.DrawRay(pos, rot * Vector3.up * size, Color.green, duration); Debug.DrawRay(pos, rot * Vector3.forward * size, Color.blue, duration); // Vẽ thêm một khối diamond nhỏ màu trắng tại tâm để dễ nhận diện float s = size * 0.2f; Debug.DrawLine(pos + rot * Vector3.up * s, pos + rot * Vector3.right * s, Color.white, duration); Debug.DrawLine(pos + rot * Vector3.right * s, pos - rot * Vector3.up * s, Color.white, duration); Debug.DrawLine(pos - rot * Vector3.up * s, pos - rot * Vector3.left * s, Color.white, duration); Debug.DrawLine(pos - rot * Vector3.left * s, pos + rot * Vector3.up * s, Color.white, duration); } } }