시작
저번 글에 이어 자동차를 조종하는 것을 마저 만들어볼 것이다.
이전 글 내용이 기억나지 않는다면 아래의 링크로 들어가면 볼 수 있다.▼
C# 스크립트 생성
키보드로 자동차를 조종하기 전에 우선은 입력없이 자동차 스스로 움직이게 해보자.
그러기 위해서는 자동차 오브젝트의 위치나 각도를 바꿔줄 코드가 필요한데, 그 코드를 Unity에서는 스크립트(Script)라고 부른다.
기본적으로 스크립트는 C# 문법을 따른다.
우선은 C# 스크립트를 생성하자.
Assets 폴더의 빈 공간을 우클릭해서 C# Script를 만들 수 있다.▼
스크립트를 만들면 이렇게 이름을 설정할 수 있는데, 바로 바꾸는 편이 좋다.
스크립트를 생성하면 완전 빈 파일이 생성되는 것이 아니라 내부에 클래스가 작성된 채로 생성이 되는데 클래스 이름이 파일 이름으로 설정된다.
만약 지금 상태에서 그냥 다른 데를 클릭하면 클래스 이름이 NewBehaviour로 설정되게 된다.
클래스 이름이 다르게 되면 코드상에서 문제가 발생할 수 있으니 두 번 이름 바꾸지 않게 바로 설정하는게 좋다.
물론 지금 당장 적합한 이름이 생각나지 않으면 어쩔 수 없다.▼
필자는 Controller라고 이름을 지었다.
스크립트를 가볍게 한 번 클릭하면 오른쪽의 Inspector에서 코드를 간단하게 볼 수 있다.
물론 여기서 코드를 수정할 수 없으므로 미리 설치 했던 VSCode를 통해 수정할 것이다.▼
그런데 VSCode 세팅하는 글에서도 말했지만 Mac에서는 그냥 더블 클릭해서 스크립트를 킬 경우 자동완성이 되지 않는 문제가 있다.
그럴 때는 VSCode를 완전히 끄고 Assets - Open C# Project를 눌러서 VSCode를 키면 자동완성이 잘 작동한다.▼
윈도우 유저이거나 그냥 켜도 자동완성이 잘 되면 굳이 이렇게 안 키고 간편하게 스크립트를 더블 클릭해주면 된다.
Start()와 Update()
VSCode를 통해 스크립트를 열게 되면 우리가 이름 지었던 그대로 `Controller`라는 클래스가 있고 그 안에 `Start()`와 `Update()`라는 함수가 있다.▼
"이 함수들의 역할은 무엇일까?"
아주 간단하게 축약된 게임 시스템
두 함수들의 역할을 명확히 이해하기 위해서는 게임 시스템에 대한 간단한 이해가 있어야 한다.
게임은 영화의 특성과 비슷하다.
요즘 영화는 48fps, 120fps, 60fps와 같이 다양한 fps로 제작이 되는데, 예전에는 24fps가 정석이자 표준이었다.
fps는 frame per second의 약자로 1초에 보여주는 프레임을 말한다.
즉, 1초에 24장의 사진을 보여줘야 부드럽게 움직이는 것과 같이 인식한다는 것이다.
이 개념은 게임에도 똑같이 적용된다.
게임도 따지고 보면, 여러 장면을 연속적으로 보여줌으로써 게임 오브젝트나 배경 사물들의 움직임을 표현한다.
다만 이 여러 장면이란게 영화처럼 고정된 것이 아니라 플레이어가 조작할 때마다 달라진다는 것이 다른 점이다.
만약 지금까지 알고리즘 위주로 공부했거나 이론적인 부분만 공부했다면, command line tool을 많이 이용했을 것이다.
알고리즘을 돌리면 결과가 나오면서 프로그램이 종료되는 것이 일반적인 상황이었을 것이고, 무한 루프가 난다는 것은 프로그램이 잘못 짜여졌다고 판단이 된다.
하지만 게임 시스템에서는 연속적인 화면을 보여주기 위해 의도적으로 무한 루프를 만든다.
만약 게임이 우리가 짰던 command line tool을 이용한 알고리즘과 같다면, 프로그램은 화면을 보여주고 종료할 것이다.
"그러면 어쨌든 화면이 보이는 거 아닌가?"
전혀 아니다.
화면을 볼 틈새도 없이 창이 닫힐 것이고, 창이 닫히는 속도를 우리 눈으로 따라오기는 힘들 것이다. ▼
하지만 무한 루프를 통해 화면을 계속 그려주면 우리는 그제서야 화면을 볼 수 있다.▼
그리고 무한 루프의 한 루프가 프레임이 된다.
즉, 이런 순서로 게임이 돌아가게 된다는 것이다.▼
Start()
위의 시스템을 이해했다면 이제 `Start()`함수가 어떤 일을 하는 지를 이해할 수 있게 된다.
게임은 위의 시스템처럼 무한 루프로 돌아가지만, 어쨌거나 초기화는 필수다.
초기화 없이 무언가가 돌아가게 된다면 내부 변수가 어떻게 꼬일지 전혀 모르는 문제가 발생한다.
그렇기에 무한 루프가 돌아가기 전에 변수나 시스템에 대해서 초기화를 해야하는데, 그 역할을 맡은게 `Start()`라고 보면 된다.
그 말은 즉슨 `Start()`는 무한 루프에 포함되어있지 않으므로 한 번만 실행되고 그 후에는 더 실행되지 않는다는 것이다.
주석으로 적힌 설명 "Start is called before the first frame update"은 결국 가장 첫번째 프레임이 시작되기 전에 실행된다는 말은 무한 루프 이전에 실행된다는 의미이다.▼
Update()
위의 시스템 설명을 한 이유는 사실 `Update()` 때문이다.
Update는 프레임에 포함되어 무한 루프로 돌아간다.
이말은 즉 무한 루프가 돌아갈 때 실행되는 각 프레임마다 `Update()`가 들어가서 루프가 돌아갈 때마다 계속 실행이 된다.
그렇기에 연속적인 탐색과 이동 작업이 가능해진다.
주석으로 적힌 설명 "Update is called once per frame"이라는 말은 해석 그대로 매 프레임마다 실행된다는 의미이다.▼
요약
결론적으로 `Start()`는 초기화에 이용되며 단 한 번만 실행 되는 함수이고,
`Update()`는 매 프레임마다 실행되며 게임 로직에 맞춰 오브젝트의 상태를 바꿀 수 있는 함수다.
우리는 이 두 함수를 이용해서 간단하게 자동차의 상태를 바꿔볼 것이다.
스크립팅
그럼 이제 본격적으로 자동차의 상태를 변경할 스크립트를 짜보자.
Rotate()
우선 차체를 회전시켜보자.
자동차 오브젝트의 상태를 바꾸려면 자동차 오브젝트의 상태에 접근할 수 있는 코드나 자동차 오브젝트 자체가 필요하다.
이 때 해당 오브젝트의 상태에 접근할 수 있게 도와주는 것이 transform 컴포넌트 객체다.▼
"어? 저 transform이 어떤 오브젝트의 transform일 줄 알고 막 쓰는거지?"
스크립트를 작성하고 나면 해당 스크립트 자체를 오브젝트의 컴포넌트로 넣어준다.
해당 과정을 해주면 transform은 자기가 적용된 오브젝트의 transform으로 인식되므로 어떤 오브젝트의 transform인지는 명확하게 결정된다.
그러면 그 다음엔 transform의 `Rotate()` 메소드에 접근한다.
`Rotate()` 메소드의 경우 x, y, z 세 매개변수를 받아 해당 값 만큼 회전시켜준다.
그런데 2D의 경우 회전에 관여하는 것은 z값 하나다.▼
x값이나 y값은 오브젝트의 위치를 표현하고 z값은 오브젝트의 회전 정도를 표현한다.
따라서 아래의 코드 처럼 z값에 45를 넣게 되면 반 시계 방향으로 45도 회전하게 된다.▼
transform.Rotate(0, 0, 45);
코드에서 오류가 감지 안됐다면 문법에 맞게 잘 작성한 것이니 Command(⌘) + S 로 저장하고 유니티로 돌아가자.
일단 한 번 실행 해 볼 것이다.
일단 유니티로 돌아가면 Reload Script Assemblies이라는 말과 함께 로딩이 될 것인데, 이상이 생긴게 아니라 우리가 짠 C# 스크립트를 로딩하는 과정이니 정상이다.▼
컴파일이 완료되면 스크립트를 Car 오브젝트에 드래그 앤 드랍하여 컴포넌트로 넣어주자.
이 과정으로 이제 car는 게임이 실행되면 우리가 짠 명령어를 수행하게 된다.▼
스크립트가 오른쪽 처럼 잘 들어갔는 지 확인하고 실행해보자.
자동차는 어떤 모습을 할까?▼
차체는 이 모습으로 정지하게 된다.
이유는 회전하는 코드를 `Start()`함수에 넣었기 때문이다.
딱 한번 회전하고 더 이상 회전하지 않게 된다.▼
계속해서 회전 시키려면 `Update()`함수에 넣으면 되는데, 아마 위에 사용했던 코드 그대로 사용하게 되면 엄청난 속도로 회전할 것이다.▼
부드럽게 회전하는 것을 보고 싶다면 아래의 코드를 넣자.▼
transform.Rotate(0, 0, 0.1f);
Translate()
우리가 진짜 원하는 것은 자동차가 회전하는 것이 아닌, 시원하게 앞으로 나가는 것이다.
시원하게 앞으로 나간다는 의미는 매 프레임마다 좌표값이 한 방향으로 계속해서 바뀐다는 의미다.
그렇다면 우리에게 필요한 함수는 위치 값을 계속해서 더해가며 바꿔주는 메소드다.
이미 눈치챘겠지만 그 메소드는 transform의 `Translate()`메소드 이다.
매개변수는 똑같이 x, y, z인데 2D 오브젝트의 경우 x, y 평면에서면 움직이므로 z값에 값을 넣어도 크게 변하는 것을 보지 못할 것이다.
일단 위쪽으로 향해 가도록 y에 1을 넣어보자.
transform.Translate(0, 1, 0);
이번에는 Start()에 코드를 넣지 않고 `Update()`에 바로 넣어보자.▼
아까 `Rotate()` 부분을 따라왔다면 car 오브젝트에 스크립트가 이미 적용되어있을 것이므로 컴파일이 끝나면 바로 실행해보면 아주 잘 달리는 것을 볼 수 있다.▼
너무 빠른 것 같다면 `Rotate()`와 같이 시간을 조절하면 된다.
정리
- 게임 시스템
- 크게 input_event, game_logic, scene_rendering 의 반복으로 이루어져있다.
- `Start()`
- 게임이 시작하고 처음 한 번만 실행되며 주로 초기화에 이용된다.
- `Update()`
- 게임이 시작되고 매 프레임마다 실행되며 오브젝트의 상태나 게임 로직의 변화를 줄 때 이용된다.
- `transform.Rotate()`오브젝트의 회전 상태를 바꿔준다.
- `transform` 컴포넌트의 메소드이다.
- `transform.Translate()` 오브젝트의 좌표 상태를 바꿔준다.
- `transform` 컴포넌트의 메소드이다.
'Develop > Unity' 카테고리의 다른 글
[Unity2D] Input 시스템 (0) | 2024.03.03 |
---|---|
[Unity2D] Object와 Sprite (0) | 2024.02.22 |
[Unity2D] 게임 디자인 (0) | 2024.02.22 |
[Unity2D] unity와 C#, VSCode 설정(macOS, M1) (0) | 2024.02.22 |
[Unity2D] 기초에 앞서 (0) | 2024.02.22 |