[르탄이 카드 뒤집기] 게임 기본 씬 구성하기(4-1)
게임 기본 씬 구성하기
- 이번 강의에서는 기존에 배웠던 키워드들의 총 복습.
- 카드 뒤집기 게임을 만들면서 보드게임 만들기의 기초를 배워보자.
- 유니티 Main Camera 세팅과 시간을 표시해 줄 Text를 만든다. (UI -> Legacy -> Text)
- TimeTxt 세부 설정을 하고, Unitypackage도 Assets 폴더에 Import 해줌
씬 추가 구성 - 카드 한 장과 시간 시스템(4-2)
Card Prefab 만들기
- 이제 16장의 카드를 깔아주기 위한 Card Prefab 하나를 만들어 보자.
- Board 오브젝트를 생성하고, 앞으로 깔게 될 카드를 이 Board 밑에 깔아주자.
- Board 밑에 Card 오브젝트 생성
- 카드 한쪽에는 카드의 정보가 담겨 있고 다른 한쪽에는 어떤 정보가 있는지 모르게 모두 똑같은 이미지들이 담겨 있다. 비슷하게 만들어 보자.
- 앞면은 각 카드들의 정보, 즉 rtan 이미지들을 넣고 뒷면은 물음표 모양으로 하자.
- Card 밑에 2D Object -> Square 생성 -> 'Front' -> rtan0 이미지 추가
- Card 밑에 2D Object -> Square 생성 -> 'Back' -> rtan0 이미지 추가
- 혹시 이미지가 너무 크다면, Pixels per Unit을 줄여주자.
- Pixels per Unit: Scene창의 네모칸 하나 = Unit. 하나의 Unit을 몇 픽셀로 할 거냐?
- 이미지 크기가 500x500이면 현재 Unit 하나가 100이니까 5개의 Unit칸을 가로x세로로 차지하게 됨
- 르탄이 이미지를 Unit 하나에다가 넣고 싶다면 Unit 하나의 크기를 500으로 맞춰주면 됨
- 다시 돌아와서, Back에다가 물음표를 만들어줘야 한다. UI -> Legacy -> Text
- 만들어진 Canvas는 게임 월드 상에다가 표시해 줄 거기 때문에 Canvas의 Render Mode를 World Space로 변경
- Text도 Canvas의 사이즈와 똑같게 유지해 주기 위해 center를 맨 우측 하단으로 맞춤
- 현재 Canvas를 더블클릭해 보면 매우 크다. Scale을 0.01로 줄여주자.
- Card의 Scale, Text등 세부 세팅을 끝내면 Card 한 장의 Prefab 완성.
- Prefab 폴더에 넣어주자.
타이머 기능 만들기
- 이번에는 시간이 흐르게 하는 기능을 만들어 보자.
- GameManager 오브젝트, 스크립트 생성
- 시간을 더해주는 값을 저장해 줄 time 변수를 만들고, 이 time 변수를 텍스트에 표시해 줄 text 변수를 만들자.
public Text timeTxt;
float time = 0.0f;
void Start()
{
}
void Update()
{
time += Time.deltaTime;
timeTxt.text = time.ToString("N2");
}
- 변수 두 개와 시간을 더해주는 로직, 시간을 입력해주는 로직 작성
- GameManager 오브젝트에 스크립트와 변수를 추가하고 플레이 해보면 시간이 정상적으로 흐르는 걸 볼 수 있다.
카드 만들기 - 배치하기(4-3)
반복적으로 카드 생성하기
- 이제 카드를 배치하는 로직을 짜보자.
- 이번에는 Card Prefab을 복붙해서 미리 배치하는 게 아니라, 코드를 통해서 배치해 보자.
- 왜냐하면 만약 레벨 시스템이 있어서 카드가 16장보다 점점 늘어난다면 하나하나 직접 깔아주기 힘들기 때문. 반복적인 작업은 코드를 통해 하자.
- 자동적으로 코드를 통해서 손쉽게 반복적인 로직을 구현해보자.
- 먼저 카드를 반복적으로 생성해 줘야 한다. 생성은 Instantiate.
- 근데 16장뿐만 아니라 무한으로 계속해서 반복적으로 생성되게 하려면?
- 이 기능을 구현하기 위해 새로운 C# 문법을 배워보자. (이번에는 한정된 숫자만)
- Board 스크립트를 생성하고 Board 오브젝트에 붙여준다.
public class Board : MonoBehaviour
{
void Start()
{
for()
}
- 정해진 횟수만큼 반복적으로 수행하게 하는 기능을 위해 새로운 문법인 반복문을 써보자.
- 키워드는 for
void Start()
{
for(int i = 0; i < 16; i++)
{
}
}
- 반복문은 이러한 모습을 하고 있다.
- for 키워드를 쓰고, 괄호 안에는 3개의 코드가 들어감
- int i = 0;은 초기화 값
- 이 초깃값을 몇 번 반복할 건지 i < 16을 통해 조건을 정해준 것
- i가 16보다 작으면 계속해서 중괄호 안에 있는 로직을 실행하게 도와주는 것
- 중괄호 안에 있는 로직을 한번 실행하면 i++ 즉 i의 값을 하나씩 증가시켜 주게 되는 것이다.
void Start()
{
for(int i = 0; i < 16; i++)
{
Debug.Log("Hello");
}
}
- 예를 들어 중괄호에 이렇게 디버그 로그를 적어준다면, 이 for문이 맨 처음 실행될 때 int i는 0으로 세팅이 됐잖슴. int i는 0이니까 16보다 작음. 그러면 이 내부에 있는 코드가 실행이 되는 것. 내부 코드가 실행이 되면 Hello라는 디버그 로그가 찍히는 것
- 중괄호 안의 로직이 모두 실행이 되면 그 다음 문장인 i++로 넘어감
- i값이 하나 증가하게 됨. 그리고 반복문이니까 다시 처음에 i값을 살펴보게 되는 것
- 그럼 이제 i값은 1임. 역시 조건문을 보면 i는 1이고 16보다 작음. 그럼 또 중괄호 로직 시작.. 이렇게 계
~속 반복적으로 실행되는 것
- 그러면 i가 15일 때는? 마찬가지로 디버그 로그가 실행이 되고 i++가 되면 16. 그리고 반복문이니 다시 돌아오긴 함. 16이 되고 다시 조건문을 봤는데 i < 16이래. 그럼 참이냐 거짓이냐, 거짓이로다. 그래서 중괄호의 로직은 실행되지 않고 반복문이 끝나게 된다.
- 만약 i <= 16이라면 작거나 같다, 즉 16도 포함이니 한번 더 i가 찍히고 끝남. <와는 한번 더 실행되냐 안 되냐의 차이
- 만약 i를 2씩 올려주고 싶다면 i+=2라고 바꿔주면 됨
- 디버그 로그를 "Hello" 대신 i로 수정하고 플레이 해보면, 0부터 15까지 로그가 찍힌 걸 볼 수 있다.
- 그럼 이제 여기에 디버그 로그 대신 Instantiate를 써주면 되겠다.
public GameObject card;
void Start()
{
for(int i = 0; i < 16; i++)
{
Instantiate(card);
}
}
- Instantiate를 사용해야 하니 생성해 줄 Prefab이 필요하다. card 변수 추가
- card가 생성되도록 중괄호 작성. 이러면 카드 16개가 생성되겠다.
- 유니티로 가서 Board 스크립트에 Card Prefab을 넣어주고 플레이 해보면, Hierarchy창에 Card 클론들이 모두 16개가 생성된 걸 볼 수 있다.
- 이 카드들을 Board 안에 넣어주고, 위치값도 수정해 줘야 될 것 같다.
void Start()
{
for(int i = 0; i < 16; i++)
{
Instantiate(card, this.transform);
}
}
- 생성된 게임 오브젝트의 위치를 정해주는 건 Instantiate애서 변수 하나만 더 추가해 주면 된다.
- Board 안에 들어가도록 this 변수 사용
- 플레이 해보면 Board 밑으로 잘 생성된다.
반복적으로 카드 배치하기
- 그럼 이제 이 카드들을 원하는 모양인 4x4 배열로 배치되는 로직을 만들어 보자.

- 카드의 사이즈는 1.3x1.3으로 하고, 0.1씩 띄워서 배치한다. 그러니까 1.4의 간격으로 배치. 그렇게 되면 두 번째 카드는 x1.4의 위치에 배치되고, 세 번째 카드는 x2.8 위치에 배치되겠다. 이걸 마찬가지로 y에도 적용.
- 그러면 각각 x, y에 적절한 값들을 넣어줘야 한다.

- 카드를 배치하고 싶은 모습은 다음과 같다. 4x4의 모습. 거기에 숫자를 적어놓은 건데, 맨 아랫줄이 0, 1, 2, 3으로 시작해 한 줄씩 올라가는 구조.
- x가 0, 1, 2, 3이고 y는 0, 4, 8, 12라고 생각해 보자. 임의적으로 좌표를 만들어 볼 것
- x는 0, 1, 2, 3. 오른쪽으로 갈수록 숫자가 하나씩 늘어날 거고 y는 세로로 올라갈수록 숫자가 하나씩 늘어나겠다.
- 첫 번째 줄을 보면, y는 같으니까 x만 계속 늘어날 것. 0카드는 0, 0, 1카드는 1, 0, 2카드는 2, 0, 3카드는 3, 0
- 두 번째 줄은 y가 하나 올라감. 4카드는 0, 1, 5카드는 1, 1, 6카드는 2, 1, 7카드는 3, 1

- 이 임의적인 좌표를 나열해 보면 이렇게 된다.
- 일정한 규칙이 있다. 오른쪽으로 갈 수록 x값이 하나씩 늘어나고 위로 갈 수록 y값이 늘어난다. 맨 끝은 이전에 계산했던 카드의 간격. 결국 각각의 숫자에다가 1.4를 곱해주면 될 것이다.
- 그러면 (0, 0), (1, 0), (2, 0), (3, 0)들의 좌표를 구해야 한다. 구해 놓은 좌표에다가 1.4씩 곱해줘야 하니까.
- 그걸 어떻게 구함..?

- 현재 0, 1, 2, 3, 4 이렇게 올라가고 있는데, 이걸 나눠준다고 생각해 보자.
- 나눴을 때 몫과 나머지가 나오게끔 연상을 해보자.
- 만약 첫 번째 줄을 (한 줄에 4개니까) 4로 나눠준다고 했을 때, 각각의 몫은 0, 1, 2, 3이 된다.
- 결국 딱 Index의 몫만큼 y값이 세팅이 되는 걸 확인할 수 있다.
- 그럼 x는? 예를 들어 5를 보자. 5는 4로 나눴을 때 몫이 1이 된다. 그리고 몫에서 나머지를 구하면 1이니까 5 - 4가 된다. 5 - 1(몫) x 4라면 남는 수는 1. 나머지는 1.
- 6의 경우 몫은 1, 나머지는 2. 7의 경우 몫은 1, 나머지는 3.

- 위 이미지를 보면 알 수 있듯 5는 (1, 1). 몫 1, 나머지 1. 6(2, 1)은 몫 1, 나머지 2.
- 결국에는 반복문을 수행할 때 숫자값을 각각의 카드에다가 넣어주면 될 것이다. 그럼 각각 자신의 숫자를 갖고 있고 그 숫자의 몫과 나머지 즉 몫은 y값, 나머지는 x값에서 넣어줘서 각각 1.4씩을 곱해주면 될 것이다.
- 복잡하지만 몫과 나머지(y, x)를 구해서 거기에 1.4를 곱해준다고 생각하면 된다.
- 이제 이 부분을 스크립트에서 구현해 보자.
void Start()
{
for(int i = 0; i < 16; i++)
{
GameObject go = Instantiate(card, this.transform);
}
}
- 생성된 게임 오브젝트의 위치를 잡아줘야 하니 잠시 GameObject 변수로 들고 있자. (go는 GameObject의 약자)
void Start()
{
for(int i = 0; i < 16; i++)
{
GameObject go = Instantiate(card, this.transform);
go.transform.position = new Vector2();
}
}
- 이 go의 position값을 Vector2로 다시 잡아 주자. 이 Vector2의 x, y값에 우리가 원하는 값을 넣어주면 되겠다.
- 아까 카드 배치에서, x값에다가는 현재 for문을 통해서 증가하고 있는 i값(숫자값)을 4로 나눴을 때 나머지 값을 x에다가 넣어주기로 했다. 몫 값은 y에다가.
void Start()
{
for(int i = 0; i < 16; i++)
{
GameObject go = Instantiate(card, this.transform);
int x = i % 4;
int y = i / 4;
go.transform.position = new Vector2();
}
}
- x에는 나머지 값이 들어가고 나머지 값을 구하는 연산자는 %
- 한 줄에 4개였으니까 i % 4를 적어 4로 나눠준다.
- y, 즉 몫값은 i / 4로 적어주면 된다.
- float 같은 실수형 자료형을 쓸 때는 소수점 값을 구해줬었다. 그런데 정수형 값을 나누기(/) 연산자로 계산해 주면 몫이 도출되게 된다.
- 이제 x에는 나머지 값, y에는 몫 값이 들어갔다.
void Start()
{
for(int i = 0; i < 16; i++)
{
GameObject go = Instantiate(card, this.transform);
int x = i % 4;
int y = i / 4;
go.transform.position = new Vector2(x, y);
}
}
- 적절한 위치에 넣어주기 위해 Vector2에 x, y 입력
- 그리고 몫과 나머지만 넣어주면 안된다. 1.4를 곱해줘야지요
void Start()
{
for(int i = 0; i < 16; i++)
{
GameObject go = Instantiate(card, this.transform);
float x = (i % 4) * 1.4f;
float y = (i / 4) * 1.4f;
go.transform.position = new Vector2(x, y);
}
}
- 나누는 값들을 괄호에 묶어주고 1.4를 곱해준다. f를 사용하니 int도 float로 변경
- 유니티로 돌아와서 플레이 해보면, 우리가 원하는 4x4 모양으로 카드가 배치된 걸 볼 수 있다. 위치만 잡아주면 될 것 같다.
- Shift 눌러서 카드 전체 선택하고 옮겨서 위치 확인. x는 -2.1, y는 3.0정도 옮기자.
void Start()
{
for(int i = 0; i < 16; i++)
{
GameObject go = Instantiate(card, this.transform);
float x = (i % 4) * 1.4f - 2.1f;
float y = (i / 4) * 1.4f - 3.0f;
go.transform.position = new Vector2(x, y);
}
}
- 스크립트로 돌아가 위치 수정
- 다시 플레이 해보면, 화면에 카드들이 모두 보이게 4x4 모양으로 배치된 걸 볼 수 있다.