using UnityEngine; using System.Collections; using System.Collections.Generic; public class DSPhysicsSGT : MonoSingleton { // 유도 알고리즘 public void GuidedMissile(ref Vector3 vPos, ref Vector3 vDirection, Vector3 vTarget, float fHomingAngle, float fSpeed, Vector3 vTargetDirection, bool bDirect = false, bool bRevision = true, bool bReSpeed = false) { // 예외처리 : 꺽이는 각도가 0이면 방향이 zero가되어 큰 문제가 생긴다. if (0.0f == fHomingAngle) fHomingAngle = 4.0f; // 요마이 작으면 임의축이 잘 안뽑히넹... if (0.1 > vTarget.magnitude) vTarget.z = -0.1f; // 타켓으로 향하는 방향벡터 Vector3 vTargetDist = (vTarget - vPos).normalized; // 예외처리 : 이미 타켓에 도착했는가? if (Vector3.zero == vTargetDist) return; float fAngle = -1; // 타겟 다음 위치 미리 예측하여 추적 if (vTargetDirection != Vector3.zero) { // 볼일 경우 y축 값이 있어서 내가 원하는 추적각이 계산이 제대로 안되므로 // y값은 0으로 한다. 선수들은 평지를 달리기 떄문에 x y 추적값만 있으면 된다. Vector3 v1 = vPos; Vector3 v2 = vTarget; Vector3 v3 = vTargetDirection; v1.y = v2.y = v3.y = 0; Vector3 vDist1 = (v1 - v2).normalized; Vector3 vDist2 = v3.normalized; fAngle = GetAngleToPosition(vDist1, vDist2); // 타겟이 달려 오는 방향 반대 방향에 있는 판단 기준 if(Mathf.Abs(fAngle) > 10 && Mathf.Abs(fAngle) <= 90) { fAngle *= 2; vDirection = Quaternion.Euler(0, -fAngle, 0) * (vDist2 * -1); } } if(vTargetDirection == Vector3.zero || Mathf.Abs(fAngle) <= 10 || Mathf.Abs(fAngle) > 90) { // 회전 전 타켓과 진행 방향간 X/Y 부호기록 bool bMarkX1 = (0.0f < vTargetDist.x - vDirection.x); bool bMarkY1 = (0.0f < vTargetDist.y - vDirection.y); if (true == bDirect) { // 다이렉트 회전 vDirection = vTargetDist; } else { // 임의축으로 회전 Vector3 vAxis = Vector3.Cross(vDirection, vTargetDist).normalized; // 예외처리 if(vAxis == Vector3.zero) { vAxis = Vector3.Cross(vDirection, vTargetDist - new Vector3(-0.01f,0,0)).normalized; } float fAngle1 = GetAngleVectorToVector(vDirection, vTargetDist); fHomingAngle = fHomingAngle > fAngle1 ? fAngle1 : fHomingAngle; Quaternion qRotate = Quaternion.AngleAxis(fHomingAngle, vAxis); vDirection = (qRotate * vDirection).normalized; } bool bMarkX2 = (0.0f < vTargetDist.x - vDirection.x); bool bMarkY2 = (0.0f < vTargetDist.y - vDirection.y); // 방향 부호가 변했다면 타켓방향보다 회전이 더 크게 된것으로 타켓방향으로 처리 안해주면 지그재그로 흔들림 if (bMarkX1 != bMarkX2) vDirection.x = vTargetDist.x; if (bMarkY1 != bMarkY2) vDirection.y = vTargetDist.y; } // 회전된 방향으로 진행 // 스피드가 목표 타겟 거리보다 높으면 목표타겟 거리만큼만 가게 처리 if(bReSpeed && (vPos - vTarget).magnitude < (fSpeed * Time.fixedDeltaTime)) fSpeed = (vPos - vTarget).magnitude * Time.fixedDeltaTime; Vector3 vSpeed = (vDirection * fSpeed); CalculationEuler(Vector3.zero, ref vPos, ref vSpeed, vTarget, bRevision); } // 두 벡터를 기준으로한 위치에 대한 각도를 구한다. public float GetAngleToPosition(Vector3 vPosition1, Vector3 vPosition2, bool bPlan = false) { if (bPlan) { vPosition1.y = 0; vPosition2.y = 0; } Vector3 vDirPos1 = vPosition1.normalized; Vector3 vDirPos2 = vPosition2.normalized; float fDot = Vector3.Dot(vDirPos1, vDirPos2); Vector3 vCross = Vector3.Cross(vDirPos1, vDirPos2); float fValue = (Mathf.Acos(fDot) * Mathf.Rad2Deg) * ((0.0f > vCross.y) ? -1.0f : 1.0f); return float.IsNaN(fValue) ? 0.0f : fValue; } public float GetAngleToPosition_Z(Vector3 vPosition1, Vector3 vPosition2, bool bPlan = false) { if (bPlan) { vPosition1.z = 0; vPosition2.z = 0; } Vector3 vDirPos1 = vPosition1.normalized; Vector3 vDirPos2 = vPosition2.normalized; float fDot = Vector3.Dot(vDirPos1, vDirPos2); Vector3 vCross = Vector3.Cross(vDirPos1, vDirPos2); float fValue = (Mathf.Acos(fDot) * Mathf.Rad2Deg) * ((0.0f > vCross.y) ? -1.0f : 1.0f); return float.IsNaN(fValue) ? 0.0f : fValue; } public float GetAngleToPositionZ(Vector3 vPosition1, Vector3 vPosition2, bool bPlan = false) { if (bPlan) { vPosition1.z = 0; vPosition2.z = 0; } Vector3 vDirPos1 = vPosition1.normalized; Vector3 vDirPos2 = vPosition2.normalized; float fDot = Vector3.Dot(vDirPos1, vDirPos2); Vector3 vCross = Vector3.Cross(vDirPos1, vDirPos2); float fValue = (Mathf.Acos(fDot) * Mathf.Rad2Deg) * ((0.0f > vCross.z) ? -1.0f : 1.0f); return float.IsNaN(fValue) ? 0.0f : fValue; } // 두 벡터를 기준으로한 위치에 대한 각도를 구한다. public float GetAngleVectorToVector(Vector3 vPosition1, Vector3 vPosition2, bool bFullAngle = true) { Vector3 vDirPos1 = vPosition1; Vector3 vDirPos2 = vPosition2; Vector3 vCross = Vector3.Cross(vDirPos1, vDirPos2); float fAngle = ((vDirPos1.x * vDirPos2.x) + (vDirPos1.y * vDirPos2.y) + (vDirPos1.z * vDirPos2.z)) / (vDirPos1.magnitude * vDirPos2.magnitude); fAngle = Mathf.Acos(fAngle) * Mathf.Rad2Deg; if (bFullAngle == false) fAngle = fAngle * ((0.0f > vCross.z) ? -1.0f : 1.0f); return fAngle; } public float GetAxisAngle(Vector3 vMainPos, Vector3 vMainDirection, Vector3 vTargetPos) { Vector3 vTargetDirection = DSMath.GetDirection(vMainPos, vTargetPos); return GetAngleToPosition(vMainDirection, vTargetDirection); } public float GetRadianToPositon(Vector3 vPosition) { return GetRadianToPositon(Vector3.forward, vPosition); } public float GetRadianToPositon(Vector3 vPosition1, Vector3 vPosition2) { return GetAngleToPosition(vPosition1, vPosition2) * Mathf.Deg2Rad; } // 원점을 기준으로한 위치에 대한 각도를 구한다. public float GetAngleToPosition(Vector3 vPosition) { return GetAngleToPosition(Vector3.forward, vPosition); } // 오일러해법( 운동방정식 ) public void CalculationEuler(Vector3 vForce, ref Vector3 vPoint, ref Vector3 vSpeed, Vector3 vTarget, bool bRevision) { // 속도 = 가속도 * 시간 vSpeed += (vForce * Time.fixedDeltaTime); Vector3 vMovePoint = vPoint; vMovePoint += (vSpeed * Time.fixedDeltaTime); if (vTarget != Vector3.zero && bRevision) { Vector3 v1 = (vMovePoint - vTarget); Vector3 v2 = (vPoint - vTarget); float fAngle = Mathf.Cos(1.570796f); v2 = Vector3.Normalize(v2); v1 = Vector3.Normalize(v1); float fDot = Vector3.Dot(v2, v1); vPoint = fDot < fAngle ? vTarget : vMovePoint; return; } vPoint = vMovePoint; } // 오일러해법( 운동방정식, 질량포함 ) public void CalculationEuler(Vector3 vForce, ref Vector3 vPoint, ref Vector3 vSpeed, float fMass, bool bSimul = false) { // 가속도 = 힘 / 질량이어야하는데,,, 가속도 = 힘 * 질량으로 한다. // woghwh //Vector3 vAccelerat = vForce * (fMass * 1.055555f); Vector3 vAccelerat = vForce * fMass; float fFixedDeltaTime = Time.fixedDeltaTime;//bSimul ? Time.fixedDeltaTime : Single.Timer.fixedDeltaTime; // 인게임 도중 엔진 슬로우 모션 동작시 보정 값 float fSlowValue = 1.0f; // 속도 = 가속도 * 시간 vSpeed += (vAccelerat * fFixedDeltaTime) * fSlowValue; // 위치 = 속도 * 시간 vPoint += (vSpeed * fFixedDeltaTime) * fSlowValue; } // 오일러해법 : Y축이 0이 되는 순간 도착 위치예측 public Vector3 GetPosToZeroHeight(Vector3 vPos, Vector3 vSpeed, Vector3 vGravity, float fMass) { Vector3 vForce = vGravity * fMass; float fDeltaT = -Mathf.Sqrt((vSpeed.y * vSpeed.y) + (2 * vForce.y) * -vPos.y); fDeltaT = (fDeltaT - vSpeed.y) / vForce.y; Vector3 vPredictionPos = new Vector3(); vPredictionPos.x = vPos.x + (vSpeed.x * fDeltaT); vPredictionPos.y = 0.0f; vPredictionPos.z = vPos.z + (vSpeed.z * fDeltaT); return vPredictionPos; } public bool CheckPlayerCollision(Vector3 vPos0, Vector3 vPos1, float fOffset, out RaycastHit pHitInfo, bool bPassInCollider = false) { float fY = vPos1.y; // 두 점이 아주 가까우면 충돌체크가 안될때가 있더라,,,, // 두 점의 중심을 기준으로 동등하게 Offset만큼 벌려준다. vPos0 += (DSMath.GetDirection(vPos1, vPos0) * 0.5f); vPos1 += (DSMath.GetDirection(vPos0, vPos1) * (fOffset * 0.5f)); // 바닥충돌 체크 안되기 위해서 올림 vPos0.y += 0.1f; vPos1.y += 0.1f; int iCheckCollider; if (bPassInCollider == false) { iCheckCollider = ((1 << LayerMask.NameToLayer("Collider")) | (1 << LayerMask.NameToLayer("InCollider"))); } else iCheckCollider = 1 << LayerMask.NameToLayer("Collider"); float fDist = Vector3.Distance(vPos0, vPos1); if (true == Physics.SphereCast(vPos0, 0.1f, DSMath.GetDirection(vPos0, vPos1), out pHitInfo, Mathf.Clamp(fDist, fOffset, fDist), iCheckCollider)) { pHitInfo.point += (pHitInfo.normal * (fOffset + 0.00001f)); return true; } return false; } public bool CheckMonsterCollision(Vector3 vPos0, Vector3 vPos1, float fOffset, out RaycastHit pHitInfo) { float fY = vPos1.y; // 두 점이 아주 가까우면 충돌체크가 안될때가 있더라,,,, // 두 점의 중심을 기준으로 동등하게 Offset만큼 벌려준다. vPos0 += (DSMath.GetDirection(vPos1, vPos0) * 0.5f); vPos1 += (DSMath.GetDirection(vPos0, vPos1) * (fOffset * 0.1f)); // 바닥충돌 체크 안되기 위해서 올림 vPos0.y += 0.1f; vPos1.y += 0.1f; float fDist = Vector3.Distance(vPos0, vPos1); if (true == Physics.SphereCast(vPos0, 0.1f, DSMath.GetDirection(vPos0, vPos1), out pHitInfo, Mathf.Clamp(fDist, fOffset, fDist), (1 << LayerMask.NameToLayer("MonsterCollider")) )) { pHitInfo.point += (pHitInfo.normal * (fOffset + 0.00001f)); return true; } return false; } public RaycastHit[] CheckRayCollision(Vector3 vPos0, Vector3 vPos1) { float fLen = (vPos0 - vPos1).magnitude; RaycastHit[] arrHit = Physics.RaycastAll(vPos0, DSMath.GetDirection(vPos0, vPos1), fLen, (1 << LayerMask.NameToLayer("InCollider"))); return arrHit; } public RaycastHit[] CheckRayVisibleCollision(Vector3 vPos0, Vector3 vPos1) { float fLen = (vPos0 - vPos1).magnitude; RaycastHit[] arrHit = Physics.RaycastAll(vPos0, DSMath.GetDirection(vPos0, vPos1), fLen, (1 << LayerMask.NameToLayer("VisibleCollsion"))); return arrHit; } public RaycastHit[] CheckRayThrowCollsion(Vector3 vPos0, Vector3 vPos1) { float fLen = (vPos0 - vPos1).magnitude; RaycastHit[] arrHit = Physics.RaycastAll(vPos0, DSMath.GetDirection(vPos0, vPos1), fLen, (1 << LayerMask.NameToLayer("ThrowCollsion"))); return arrHit; } public RaycastHit[] CheckRayPcCollsion(Vector3 vPos0, Vector3 vPos1) { float fLen = (vPos0 - vPos1).magnitude; RaycastHit[] arrHit = Physics.RaycastAll(vPos0, DSMath.GetDirection(vPos0, vPos1), fLen, (1 << LayerMask.NameToLayer("Hero"))); return arrHit; } public RaycastHit[] CheckRayAllCollision(Vector3 vPos0, Vector3 vPos1, bool bInColliderPass = false, bool bColliderPass = false) { int iCheckCollider; if (bInColliderPass == false) { iCheckCollider = ((1 << LayerMask.NameToLayer("Collider")) | (1 << LayerMask.NameToLayer("InCollider"))); } else if(bColliderPass == false) iCheckCollider = 1 << LayerMask.NameToLayer("Collider"); else iCheckCollider = 1 << LayerMask.NameToLayer("InCollider"); float fLen = (vPos0 - vPos1).magnitude; RaycastHit[] arrHit = Physics.RaycastAll(vPos0, DSMath.GetDirection(vPos0, vPos1), fLen, iCheckCollider ); return arrHit; } public RaycastHit[] CheckRayAllInCollision(Vector3 vPos0, Vector3 vPos1) { float fLen = (vPos0 - vPos1).magnitude; RaycastHit[] arrHit = Physics.RaycastAll(vPos0, DSMath.GetDirection(vPos0, vPos1), fLen, (1 << LayerMask.NameToLayer("InCollider")) ); return arrHit; } public bool RayCollisionToSphere(Vector3 v1, Vector3 v2, float fRadius) { v2.Normalize(); float fDot = Vector3.Dot(v1, v2); if (fDot > fRadius) return false; return true; } // Spring 알고리즘(Runge-Kutta Method) : 중심점을 기준으로 포인트위치가 스프링처럼 움직인다. // 단, 속도는 누적 갱신되는 값이므로 호출하는 곳에서 멤버변수로 가지고 있어야한다. public Vector3 CalculationSpring(Vector3 vCenter, Vector3 vPoint, ref Vector3 vSpeed, float fSpringPower, float fSpringDamping) { //Runge-Kutta Method Vector3 vK = fSpringPower * (vPoint - vCenter); // 스프링상수 Vector3 vB = vSpeed * fSpringDamping; // 감쇄폭 Vector3 vF = -vK - vB; CalculationEuler(vF, ref vPoint, ref vSpeed, Vector3.zero, false); return vPoint; } public bool is_intersection_circle_line(float radius,Vector2 vCenter, Vector2 startLine, Vector2 endLine) { Vector2 d = endLine - startLine; Vector2 f = startLine - vCenter; double a = d.x * d.x + d.y * d.y; double b = 2 * (f.x * d.x + f.y * d.y); double c = f.x * f.x + f.y * f.y - radius * radius; double discriminant = b * b - 4 * a * c; if (discriminant >= 0) { // 근이 있는 경우 교차 return true; } else { // 근이 없는 경우 교차하지 않음 return false; } } }