406 lines
15 KiB
C#
406 lines
15 KiB
C#
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
|
|
public class DSPhysicsSGT : MonoSingleton<DSPhysicsSGT>
|
|
{
|
|
// 유도 알고리즘
|
|
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;
|
|
}
|
|
}
|
|
|
|
} |