지난번엔 Unity의 시간과 속도, 가속도에 대해 알아보았다.
지금까지는 수식이 별로 없었지만 여기서부터는 무시무시해보이는 놈이 튀어나왔다.

발사 위치와 타겟이 있을때,
s = 발사체의 초기 속력
g = 중력
x = 타겟의 x좌표
y = 타겟의 y좌표
이것을 기반으로 타겟에게 닿기 위한 발사각을 구하는 방정식이다.
+- 기호가 들어가 있기 때문에 같은 위치에 대해 최대 두개의 발사각이 나올 수 있다.
이를 유도하려면
먼저 궤도 방정식을 알아야 한다.

이게 궤도 방정식이다. 여기서의 v0가 위의 발사각을 구하는 공식에서의 s이다. 앞으로 v0가 아닌 s로 설명을 하도록 하겠다.
아무튼 각도와 속도, x좌표를 알고 있을 때 그에 해당하는 y좌표를 구할 수 있는 방정식이다.
여기서 우리가 상대방의 좌표를 알고 있을때( 이때 0,0 즉 기준점은 나의 위치이다. )
우리가 모르는 것은 초기발사각인 이므로 식을 에 대한 방정식으로 바꿔주면 된다.

먼저 삼각함수 공식중 하나인 cos = 1/(1+tan) 를 사용해 다음과 같이 변환한다.
여기에 양변에 -2s를 곱하고 에 대한 2차 함수로 만들면

이런 모습이 된다. 여기서 에 대해 짝수 근의 공식 ()/a 을 사용하면

여기서 분모 분자의 x를 하나씩 제거해주면

다음과 같이 최종적인 방정식이 나온다.
이제 위의 방정식을 게임에 실제로 적용해보자.
방정식을 활용해서 AI에게 타겟의 좌표값, 투사체 발사 속도 등의 정보를 주고 자동으로 포각을 조절해서 투사체를 발사하게 할 수 있다.
public class AICannon : MonoBehaviour
{
public GameObject bullet;
public GameObject enemy;
public float s;
// 방정식 사용 부분
float? CalculateAngle(bool low)
{
Vector3 targetDirection = enemy.transform.position - this.transform.position;
float y = targetDirection.y;
float x = targetDirection.magnitude;
float g = 9.8f;
// 루트안에 들어가는 수식
float underTheSqrRoot = (float)Math.Pow(s, 4) - g * (2 * (float)Math.Pow(s, 2) * y + g * (float)Math.Pow(x, 2) );
// 루트안의 값이 0보다 작으면 허수가 되기 때문에 이 경우는 배제
if (underTheSqrRoot >= 0f)
{
float root = Mathf.Sqrt(underTheSqrRoot);
//lowAngle과 highAngle 두개의 경우가 존재할 수 있다. 함수의 매개변수로 결정한다.
float lowAngle = (float)Math.Pow(s, 2) - root;
float highAngle = (float)Math.Pow(s, 2) + root;
// 각도 return
if (low) return (Mathf.Atan2(lowAngle, g * x) * Mathf.Rad2Deg);
else return (Mathf.Atan2(highAngle, g * x) * Mathf.Rad2Deg);
}
else return null;
}
void RotateTurret()
{
float? angle = CalculateAngle(true);
if (angle != null)
{
transform.localEulerAngles = new Vector3(0, 0f, 360 - (float)angle);
}
}
// Bullet 생성 및 발사
void CreateBullet()
{
GameObject unityChan = Instantiate(bullet, transform.position, transform.rotation);
// 포신 방향으로 s 만큼 속도 부여
unityChan.GetComponent<Rigidbody>().velocity = s * transform.up;
}
void Update()
{
RotateTurret();
if (Input.GetKeyDown(KeyCode.Space))
{
CreateBullet();
}
}
}
위와 같이 코드를 구성하고 포신에 넣으면

목표의 좌표에 따라서 포신의 각도를 자동으로 조절하는 것을 볼 수 있다!!
잘보면 같은 위치임에도 low angle 과 high angle 두가지 각도로 목표를 맞출 수 있다.
포트리스 같은 게임에서 유용하게 사용될 것 같다.
물론 상대방이 계속 움직인다면 상대방의 좌표와 Velocity를 받아서 목표값을 조금씩 바꿔줘야겠지만 말이다.
수식은 복잡했지만 막상 게임에 적용하고 보니 겁나 신기하다.
수학이나 물리적인건 잘 생각을 안하고 무지성 개발을 했었는데
이번 기회에 이런 것들을 더 알아보고 싶다는 생각이 들었다.
참고한 강의