상세 컨텐츠

본문 제목

0912 TIL - 미니 프로젝트 완성!

스파르타 코딩캠프/'24 Today I Learned

by lucar 2024. 9. 12. 21:46

본문

역시 하루가 지나고 나니까 머리가 맑아져서 그런지 버그를 찾아서 고칠 수 있게 되었다.

 

정답은 다름이 아닌 게임오브젝트 설정이 잘못되어있었다...

 

그래서 오늘은 게임오브젝트 설정을 완료하고 다시 코드를 살펴보았다.

 

그렇게 완성한 공격 패턴 3가지는?

 

 void ShotToPlayer() //플레이어를 향해 5발을 발사
 {
     Vector3 targetPos = playerTransform.position;
     Vector3 myPos = new Vector3(0f, 1.4f, 0);
     Vector3[] vectorToTarget = new Vector3[5];
     for (int i = 0; i < 5; i ++)
     {
         vectorToTarget[i] = new Vector3(targetPos.x - 2 + i, targetPos.y - myPos.y, targetPos.z - myPos.z).normalized;
     }

     for (int i = 0; i < 5; i ++)
     {
         GameObject Shot = Instantiate(bullet, myPos, Quaternion.identity);
         Shot.GetComponent<Rigidbody2D>().AddForce(vectorToTarget[i] * 150f);
     }
 }

 void ShootCircle() //플레이어를 향해 좌우에서 발사
 {
     Vector3 targetPos = playerTransform.position;
     Vector3 myPos = new Vector3(0f, 1.4f, 0f);
     Vector3 myPos_left = new Vector3(1.6f, 2f, 0f);
     Vector3 myPos_right = new Vector3(-1.6f, 2f, 0f);
     Vector3 vectorToTarget = (targetPos - myPos).normalized;
     for (int i = 0; i < 10; i++)
     {
         if (i % 2 == 0)
         {
             GameObject Shot = Instantiate(bullet, myPos_left, Quaternion.identity);
             Shot.GetComponent<Rigidbody2D>().AddForce(vectorToTarget * 60f);
         }
         else
         {
             GameObject Shot = Instantiate(bullet, myPos_right, Quaternion.identity);
             Shot.GetComponent<Rigidbody2D>().AddForce(vectorToTarget * 60f);
         }
     }
 }

 void MakeCircle() //반원 모양으로 발사
 {
     Vector3 targetPos = playerTransform.position;
     Vector3 myPos = new Vector3(0f, 1.4f, 0);
     Vector3 vectorToTarget = (targetPos - myPos).normalized;
     int n = 30;

     for(int i = 0; i < n; i ++)
     {
         GameObject MakeCircle = Instantiate(bullet, myPos, Quaternion.identity);
         Vector2 circleVec = new Vector2(Mathf.Cos((Mathf.PI) * i / n ), Mathf.Sin(-(Mathf.PI) * i / n ));
         MakeCircle.GetComponent<Rigidbody2D>().AddForce(circleVec * 30f);
     }
     
 }

오늘은 이 코드를 씹고 뜯어보자.

 

코드를 보면 당연히 이상한 부분이 많을텐데

 

당연하다! 아직 그 정도 실력은 아니다!

 

그래도 나의 최선이니까 하나하나 뜯어보자

 

우선 ShotToPlayer() 함수를 살펴보자.

 

void ShotToPlayer()
{
    Vector3 targetPos = playerTransform.position;
    Vector3 myPos = new Vector3(0f, 1.4f, 0);
    Vector3[] vectorToTarget = new Vector3[5];
    for (int i = 0; i < 5; i ++)
    {
        vectorToTarget[i] = new Vector3(targetPos.x - 2 + i, targetPos.y - myPos.y, targetPos.z - myPos.z).normalized;
    }

    for (int i = 0; i < 5; i ++)
    {
        GameObject Shot = Instantiate(bullet, myPos, Quaternion.identity);
        Shot.GetComponent<Rigidbody2D>().AddForce(vectorToTarget[i] * 150f);
    }
}

게임매니저를 통해서 플레이어의 좌표를 얻어오는데

계속 실패한 나머지 화가 잔뜩나서 그냥 public Transform playerTransform 인스턴스를 만들어서

플레이어 비행기 게임 오브젝트를 집어넣고 좌표를 받아왔다...

 

여튼 targetPos = playerTransfrome.Position은 플레이어의 위치이고

myPos는 총알이 발사될 위치이다. 이 게임에서 보스는 움직이지 않게 설정했으니

그냥 글로벌 좌표로 때려버렸다....

 

Vector3[] VectorToTarget은 발사된 위치로부터 플레이어까지 도달하는데 필요한 벡터 값이다.

*벡터 = 크기와 방향을 가진 물리량

 

이 경우에는 (0,1.4)로 부터 (X|X=마우스의 X좌표, - 3.0)으로 향하는 벡터값이 될 것이다.

마우스가 중앙에 있다면 (0,1.4)로부터 (0,-3)을 향한 방향이다.

 

5개를 동시에 나눠지게 발사하고 싶으니 Vector3[] vector = new Vector[n]를 사용해 배열을 5칸 만들어주었다.

 

처음엔 일일이 적다가

어라? 이거 그냥 반복문으로 만들어도 되잖아... 싶어서 for문으로 다시 만들었다.

 

끝부분에 있는 .normalized는 대각선으로 이동해도 속도를 비슷하게 만들어준다.

 

이게 뭔 소리냐면 X로 1칸, Y로 1칸을 움직이면 같은 시간에 대각선으로 √2 만큼 이동하게 된다.

 

이러면 이동속도가 X축이나 Y축을 따라 이동할 때는 1이지만

대각선으로 이동할 때는 1.414정도로 약 1.4배의 속도로 움직이게 된다.

이 값을 완화해 주는 기능이다.

 

GameObject Shot = Instantiate(bullet, myPos, Quaternion.identity)의 경우에는

bullet이라는 GameObject를 myPos의 위치에서 Quaternion.indentity방향으로 생성한다. 라는 뜻인데

 

그래서 Quat.. 뭐시기가 뭐죠?

 

쿼터니온은 사원수라는 단어인데

오일러값을 기준으로 방향을 정해준다.

 

출처 위키피디아

간단하게 설명하자면 각각 X, Y, Z축을 기준으로 얼마나 회전하였느냐를 기준으로 하는데

identity는 방향을 바꾸지 않음(게임오브젝트 설정 그대로)이라는 뜻이며

 

즉 회전하지 않은 상태로 생성한다. 2d화면에서 방향을 정하고 싶다면

Quaternion.Euler(0, 0, Z)에서 Z값을 수정하여 방향을 정해줄 수 있다.

 

X나 Y의 경우에는 2D화면에서는 돌려도 잘 티가 안나거나 90도가 된다면

2D를 정측면에서 바라보기 때문에 보이지 않게 된다.

 

어차피 동글동글한 탄환이고 방향 바꿔도 티가 안나기 때문에 이번에는 패스

 

Shot.GetComponent<Rigidbody2D>().AddForce(vectorToTarget[i] * 150f)

 

방금 Instantiate를 통해 생성한 Shot이라는 게임오브젝트의 Rigidbody 값을 참조해

벡터를 기준으로 150이라는 힘을 더해준다.

 

이를 통해 해당 벡터 값(발사지점으로 부터 플레이어를 향해)으로 이동하게 된다.

 

ShootCircle 함수는 뭐.. 똑같으니 넘어가자

 

마지막으로 MakeCircle()  함수이다.

 

진짜 이거 만든다고 너무 고생했다.

void MakeCircle()
{
    Vector3 targetPos = playerTransform.position;
    Vector3 myPos = new Vector3(0f, 1.4f, 0);
    Vector3 vectorToTarget = (targetPos - myPos).normalized;
    int n = 30;

    for(int i = 0; i < n; i ++)
    {
        GameObject MakeCircle = Instantiate(bullet, myPos, Quaternion.identity);
        Vector2 circleVec = new Vector2(Mathf.Cos((Mathf.PI) * i / n ), Mathf.Sin(-(Mathf.PI) * i / n ));
        MakeCircle.GetComponent<Rigidbody2D>().AddForce(circleVec * 30f);
    }
    
}

위의 벡터 부분은 위에서 다뤘으니 쿨하게 넘어가자.

 

원 모양으로 벡터를 설정하려면 어떻게 해야할까?

우선 반지름이 1인 원을 생각해보자. 원 위의 한 지점을 P라고 부르면

출처 나무위키

이런 그림이 될 것이다.

 

이제 모든 bullet에 해당 점에 대한 x,y의 값 만큼 벡터를 넣어주면 된다.

 

이게 뭔 소릴까 나도 모르겠다.

 

하여튼 내가 생각해낸 방법은 삼각함수였다.

출처 나무위키

sin함수는 180도 마다 양수와 음수로 바뀌며 최대 1에서 -1까지의 값을 갖는다.

출처 나무위키

cos함수도 마찬가지다. 이는 각각의 축에서 방향을 나타내는 벡터로 사용할 수 있다.

출처 나무위키

밑변과 높이는 각각 x와 y값이 될것이고 우리는 이를 통해 벡터를 구할 수 있다.

 

1 PI = 180degree 이므로 2 PI는 360도 = 한 바퀴이다.

출처 나무위키 그림을 내가 반갈죽함

이를 총알의 갯수만큼 나눠준다. (PI * 2 / n)

9발이라고 가정해보자.

출처 나무위키 그림을 내 손으로 훼손시킴

각각의 검은 선에 해당되는 값이 X좌표이다.

각각 약 0, 0.6, 1, 0.6, 0, -0.6, -1, -0.6, 0이 될 것이다. (대충 눈으로 봤을 때)

 

Y좌표는 Cos으로 90도씩 (2칸씩) 땡겨오면

대충 각각의 벡터는 {(0,1), (0.6, 0.6), (1,0), (0.6, -0.6), (0, -1), (-0.6, -0.6), (-1, 0), (-0.6, 0.6), (0, 1)}이 된다.

이를 다시 대충 4분면에 좌표를 표시해보자.

출처 내가 그린 그림판 그림

놀랍게도 원모양이 나온다.

 

이거 만들겠다고 진짜 개고생에 고생에 고생에... 흑흑....

 

아쉽게도 첫 발과 9번째 발이 위치가 겹치긴 하겠지만 뭐 어떤가 이쁘게 나오면 됐지...

 

그렇다면 반원을 만들고 싶다면? 2PI가 아닌 PI로 계산해주면 된다!

 

그래서 스크립트에는 PI로 되어있는 것을 볼 수 있다.

 

마찬가지로 90도 각도를 만들고 싶다면 PI를 2로 나누면 된다.

어쨌든 그렇게 원 모양 벡터를 만든 후 AddForce를 통해 힘을 더해주면? 완성이다.

 

오늘 TIL THE END

관련글 더보기