Project_WL/Assets/Script/UI/JoyStick/DSPhysicsSGT.cs

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;
}
}
}