1. 총알 생성의 위치 변경
총알은 화면 밖에서 생성되어 player의 위치로 발사 되어야 한다. 2편에서 Instantiate를 이용해 총알프리팹을 복제하여 새로운 총알로 생성할 때 생성되는 위치를 Vector3값으로 전달하는데, 이때 Vector3의 값을 변경하여 게임화면 밖 빨간영역에서 생성되도록 조정하려한다.
여기에서는 2D공간에서의 위치를 만들 것이기 때문에 z축은 0으로 고정하고, x와 y축 값만 만들어 내려한다.
player가 있는 위치를 기준으로 빨간 영역의 가로 범위가 -13에서 13까지, 세로 범위가 -22에서 22까지이다. 이를 쉽게 확인해보려면, 빈 게임오브젝트를 하나 만들어 빨간영역으로 이동시켜놓고 Transform의 position값을 확인해본다.
그런데 총알이 화면안쪽에서 생성되어서는 안되기 때문에, x좌표가 -13이나 13으로 고정했을 때는 y좌표가 -22에서 22까지, y좌표가 -22나 22로 고정했을 때는 x좌표가 -13에서 13까지. 이 두가지 조건으로 x와 y값을 만들고 Vector3에 넣어 Instantiate에 전달해줄 것이다.
GameController 스크립트의 Start( )안에 float형 switchValue 변수를 선언하고 Random.value를 써서 무작위 숫자를 넣어준다. float은 실수로, 부동소수점이 들어간 숫자들을 담을 수 있는 형식이다. Random.value는 0.0에서 1.0사이의 수를 돌려준다. 정리하면, 첫 째 줄에서 선언한 float형 변수 switchValue에는 Random.value의 값인 0.0에서 1.0사이의 무작위 숫자를 넣어준다.
두세번째에서는 x, y 좌표값을 넣어줄 xValue, yValue 변수를 선언하고 각각 일정 범위안의 무작위 숫자를 넣어준다. Random.Range(a, b ) 함수는 a와 b사이의 숫자를 무작위로 만들어준다. 이때 전달하는 숫자의 뒤에 알파벳 f를 달아주는데, f를 달아줌으로써 이 숫자가 float형임을 의미한다. 예를 들어 f가 없는 1은 소수점이하가 없는 정수 1을 의미하고, f가 있는 1f는 소수점이하가 있는 1.0을 의미한다. 즉 xValue에는 -13.0에서 13.0까지 소수점이하가 있는 숫자중에 하나를 무작위로 들어가고, yValue에는 -22.0에서 22.0까지의 소수점이하가 있는 숫자가 무작위로 들어간다. 만일 여기에서 f가 없는 숫자를 Random.Range( )에 넣어주게되면, 범위내의 소수점 이하가 없는 무작위 정수가 결과로 나오게 된다.
선언하고 값을 넣어준 switchValue 변수는 어느쪽 좌표를 고정할지를 구분할 용도로 사용하고, xValue와 yValue는 고정되지 않은 나머지 좌표의 좌표값을 무작위로 넣어주는데 사용할 것이다.
변수를 선언하고 무작위 숫자를 넣어주는 코드 다음에 각 변수에 어떤값이 들어가는지 Console에 출력을 해보자. Debug.Log( )에는 문자열을 전달해주어야 하는데, 매우 편리하게도 문자열 + 숫자를 하면 둘을 합친 문자열로 만들어준다. 문자열 "a"에 숫자 1을 더하면 문자열 "a1"이 된다. 위에서 Debug.Log( )에 넣어준 문자열 + 숫자 + 문자열 + 숫자 + 문자열 + 숫자가 합쳐져서 문자열하나로 전달되는 셈.
지금까지 이 글에서 나온 자료형을 정리하면 정수는 int, 실수는 float이었고, 문자열은 string이다. 이들은 앞으로 가장 빈번하게 사용하는 자료형이므로 알아두자.
플레이를 하여 Console창에 나타난 값이 예상했던 범위 안의 숫자들인지 확인해보자. 중지하고 플레이하기를 여러차례 반복해보면 각 변수에 들어 있는 값이 계속 변하는 것을 알 수 있다. 이는 각 변수에 무작위로 생성되는 실행할 때마다 넣어주고 있기 때문이다.
이제 전체 돌아가는 내용을 다시 한번 정리하면, 유니티를 실행했을 때 scene에 등록되어 있는 게임오브젝트에 첨부되어 있는 각 콤포넌트의 내용중에 Start( )가 즉시 호출되고, CPU는 Start( )안의 내용을 위에서부터 한 줄을 실행하고 다음 줄을 실행한다. 워낙 처리속도가 빠르니 눈치채기는 어렵지만 코드의 중간 중간 Debug.Log( )로 문자열을 찍어보면 순서대로 나오는 것을 알 수 있다.
다시 돌아와서 작동 순서를 보면 GameObject형 bullet변수를 선언하고, 그 다음 float형 switchValue변수를 선언하고 Random.value의 무작위 값을 넣어주고 나서 float형 xValue변수를 선언하고 무작위 숫자를 넣어주고, float형 yValue변수를 선언하고 무작위 숫자를 넣어준다. 이를 순서대로 처리하고 나서 Debug.Log( )로 변수의 값을 출력한 뒤에 bullet변수에 bulletPrefab을 Vector3위치에 회전없이 복제한 게임오브젝트를 넣어준다.
작동되는 순서를 위와 같이 짚어보고, 모든 코드는 순서대로 한줄, 한줄 실행된다는 점을 알아두자.
이제 x축, y축을 구분하여 값을 넣어주기 위해 조건문을 사용해보자.
if (조건) { 조건이 만족되었을 때 작동할 내용 } 으로 조건문을 만들 수 있다. 바로 뒤에 세미콜론이 없이 else가 등장하는데 바로 앞의 if문 조건이 충족되지 않았을 때는 else에 붙은 중괄호 안의 내용이 작동된다. if의 내용이 실행이되면 else의 내용은 실행되지 않고, if의 내용이 실행되지 않으면 else의 내용이 실행된다. if의 소괄호안 조건이 맞거나 안맞거나에 따라 나뉘지만 if나 else의 내용 둘 중 하나는 반드시 실행이 된다.
조건은 같다, 크다와 같은 형식으로 어렵지는 않다.
a > b. a가 b보다 크다.
a >= b. a가 b보다 같거나 크다.
a == b. a와 b가 같다.
a < b. a가 b보다 작다.
a <= b. a가 b와 같거나 작다.
위의 다섯가지로 조건을 만들 수 있으며, 비교가 되는 a와 b는 같은 자료형이어야 한다.
총알 복제하는 스크립트를 조건문 안으로 가져왔다. switchValue의 값이 0.5보다 클 경우에는 x좌표는 0, y좌표는 yValue의 값을 대입하여 총알을 복제하고, 0.5보다 작을 때는 그 반대로 y좌표를 0, x좌표를 xValue의 값을 대입하여 복제한다. 본래 Vector3에서 다루는 숫자는 float형이며, 세 값을 모두 정수로 쓰는 경우를 제외하고는 모두 실수의 값을 넣어주도록 한다. 따라서 2편에서 Vector3에 대입했던 0을 0f로 변경한다.
여러차례 실행해보면 위의 빨간라인 상의 좌표에 총알이 랜덤하게 생성되는 것을 볼 수 있다. 위에서 고정된 축의 좌표값을 0f로 고정했기 때문이며, 이를 좌우나 상하 양 끝의 값으로 변경하면 처음 원했던 화면 밖 테두리 위치에 총알이 생성될 수 있다.
조건문안에 조건문을 하나 더 넣었다. 천천히 살펴보며 어떻게 작동될지 상상해보자.
Random.value의 값을 조건문의 소괄호안에서 0.5보다 큰지 직접비교하고, 그 결과에 따라 x축 고정인 경우에는 -13또는 13을, y축 고정인 경우에는 -22또는 22를 대입해준다.
여기까지 작성하고 여러차례 실행을 해보면 처음 원했던 화면 밖 테두리 영역에서만 총알이 랜덤하게 등장하는 것을 확인할 수 있다.
2. 총알 날아가기 계획
Bullet이라는 새로운 스크립트를 만들고 bullet프리팹에 콤포넌트로 넣어준다.
게임이 시작되고 GameController스크립트에서 bullet 게임오브젝트를 생성할때 생성된 bullet 게임오브젝트는 scene에 자동 등록되며 scene에 등록되는 순간 지금까지 해왔던 대로 bullet에 있는 Bullet스크립트의 Start( )가 호출될 것이다. 그런데 이 때 Start()처럼 Update( )도 호출이 된다. 새로 만든 스크립트 파일마다 Start( )와 함께 항상 들어있던 것이 Update( )인데, Update( )는 Start( )가 호출 되고 나서 그 다음으로 호출이 되며, 앱이 작동하는 동안 매 프레임마다 호출이 된다.
게임화면은 CPU가 화면을 액정에 그리는 횟수가 경우에 따라 다르다. 우리가 접하는 움직이는 영상매체들이 모두 그러하듯, 조금씩 다른 정지화면을 여러 차례 바꿔치기하며 애니메이션을 만들듯이, 컴퓨터도 마찬가지 방식으로 화면을 그리며, 여기에서 한 장의 그림에 해당하는것이 프레임이 된다. Update( )는 매 프레임마다 한 번 씩 호출된다. 따라서 프로그램이 엄청 복잡하여 CPU가 할일을 더디게 하면 화면에 그리는 횟수가 줄어들기도 하고, 할일이 별로 없으면 늘어나기도 하는데, 그만큼 Update( )도 매 프레임마다 호출이 되기 때문에 느려지면 느려지는 만큼 덜 호출되고 빨라지면 더 많이 호출되기도 한다.
아무튼 매 프레임마다 호출되는 Update( )를 이용하여 총알의 위치를 바꾸어주면 매 프레임마다 총알이 이동하게 된다.
3. 목표 위치 정하기
Bullet 스크립트 파일의 Bullet 클래스 안에 Vector3형 targetPos변수를 선언한다.
이 위치에서 선언한 변수는 이 변수와 동일한 깊이에 있는 Start( )와 Update( )에서 모두 접근하여 사용할 수 있다. targetPos에는 이 총알이 생성되었을 때 player의 위치 정보를 담아둘 것이다.
대문자로 시작하는 GameObject는 Transform처럼 유니티에서 이미 정의하고 있는 GameObject클래스를 의미하고, 그 안에 있는 Find( )함수에 우리의 목표가 될 player 게임오브젝트의 이름을 문자열로 전달하고 있다. 처음 1편의 Debug.Log( )에서도 사용했지만 여기에서 사용하는 GameObject.Find( )의 경우에도 문장 사이에 점이 있다. 이 점을 이용하면 마치 폴더안에 폴더안에 있는 파일 경로를 나타내듯이 클래스안에 있는 함수 또는 변수를 경로처럼 접근할 수 있게 된다.
GameObject.Find( 문자열 )은 씬에 등록된 모든 게임오브젝트 중에서 전달한 문자열과 같은 이름의 게임오브젝트를 반환해준다. 그렇게 얻어낸 GameObject에 들어 있는 Transform 콤포넌트의 클래스에 점을 사용하여 접근하고 다시 또 점을 사용하여 Transform클래스에 있는 position 변수에 접근하여 얻어낸 position 정보를 targetPos에 대입한다.
GameObject.Find("player").transform.position은 Inspector에 보이는 Transform항목의 position이며, position은 x, y, z축의 세 값을 가진 Vector3형의 값이다.
4. 총알의 위치 정하기
마찬가지 방법으로 myPos 변수를 생성하고 이 총알이 생성된 위치를 대입한다. 윗 줄에서 position에 접근한 방법과 똑같은 방법이지만 앞의 GameObject.Find( )는 없이 바로 transform으로 시작하는데, 이는 이 스크립트가 콤포넌트로 사용되고 있는 게임오브젝트의 Transform클래스를 의미하므로 transform.position은 복제되어 생성된 나(총알)의 위치를 의미한다.
5. 이동 간격 구하기
얼만큼 이동할지 그 양을 가질 Vector3형의 변수 newPos를 선언하고, 목표가 되는 player의 위치에서 나의 위치를 뺀 거리의 100분의 1만큼의 값을 넣어주었다.
Vector3의 값을 가진 targetPos에서 Vector3의 값을 가진 myPos를 빼면, targetPos의 x값에서 myPos의 x값을 빼고, targetPos의 y값에서 myPos의 y값을 빼고, 마찬가지로 z도 계산하게 되며, 계산된 x, y, z값에 각각 0.01을 곱하여 100분의 1만큼 나누어진 값을 만든다. 이렇게 만든 newPos 매 프레임마다 현재 위치에 더해주어 총알을 이동시킬 것이다.
정리하면 이동간격으로 사용할 거리값은 처음 총알이 생성되는 시점에 목표의 위치와 나의 위치 사이의 거리의 1% 만큼이 된다.
6. 목표로 이동하기
매프레임 호출되는 Update( )에서는 나(총알)의 위치를 바꾸어준다.
소문자로 시작하는 transform은 이 스크립트가 콤포넌트로 속해있는 게임오브젝트의 Transform콤포넌트를 의미한다. 따라서 transform.position은 나(총알)의 Transform에 있는 position변수이고 여기에 새로운 위치값을 넣어주면 위치를 이동시킬 수 있다.
Update( )가 호출되는 때에 나(총알)의 위치인 transform.position과 이동 거리가 담긴 newPos를 더하여 만든 새로운 위치값을 현재 나(총알)의 위치인 transform.position에 대입해준다. 유니티가 화면을 한장 그릴 때마다 newPos만큼 총알을 이동시켜 그리게 되므로 총알은 player를 향해 다가가게 된다.
플레이를 누르면 총알이 생성되자마자 player를 향해 날아가는 것을 확인할 수 있다. 여러 차례 실행하여 매번 다른 위치에서 생성된 총알이 항상 player를 향해 날아가는 것을 확인해보자.
설명이 다소 길었지만 막상 작성한 스크립트는 몇 줄 되지 않지만 혹시 플레이 버튼을 눌러도 작동이 되지 않는다면 Console창을 살펴보고, 빨간색 아이콘과 메세지가 발견된다면 어딘가 코드에 오류가 있을 수 있는데, 그 때는 Console창의 오류메세지를 더블클릭하면 스크립트의 오류난 부분으로 에디터에 포커싱 해주므로 오타가 있는지 확인할 수 있다.
혹시나 문제가 있다면 지금까지 작성된 Bullet과 GameController 스크립트파일을 위에서 다운로드하여 확인해보자.
이 시리즈가 도움이 되셨다면 구독과 공감 부탁드립니다.
이 시리즈가 계속 될 수 있는 힘이 됩니다.
Vector3( ) : docs.unity3d.com/ScriptReference/Vector3.html
Update( ) : docs.unity3d.com/kr/530/ScriptReference/MonoBehaviour.Update.html
Transform : docs.unity3d.com/kr/530/ScriptReference/Transform.html
Transform.position : docs.unity3d.com/kr/530/ScriptReference/Transform-position.html
GameObject : docs.unity3d.com/kr/530/ScriptReference/GameObject.html
GameObject.Find( ) : docs.unity3d.com/kr/530/ScriptReference/GameObject.Find.html
'tips > 유니티 게임만들기' 카테고리의 다른 글
유니티 강좌 6 - 총알 피하기 6/9. 콜리더와 충돌 감지 (0) | 2021.03.22 |
---|---|
유니티 강좌 5 - 총알 피하기 5/9. 버튼 이벤트와 Canvas (7) | 2021.03.17 |
유니티 강좌 4 - 총알 피하기 4/9. 함수 반복 호출 및 게임오브젝트 파괴 (2) | 2021.03.16 |
유니티 강좌 2 - 총알 피하기 2/9. 프리팹 복제 및 생성 (0) | 2021.03.08 |
유니티 강좌 1 - 총알 피하기 1/9. 준비 (0) | 2021.03.06 |
댓글