쉽게 배우는 모바일 게임 4주차 - 르탄이 카드 뒤집기 게임 1
.unitypackage 파일 import
학습자료로 다운 받았던 FindRtan.unitypackage 파일을 import 하는 방법은 매우 간단하다.
파일을 끌어다가 Asset folder에 두면 어떤 파일들을 import할지 체크하고 import 버튼을 누르면 끝.
Pixel per Unit
PPU 개념은 스프라이트 이미지의 몇 픽셀이 유니티의 1유닛에 해당하는지를 정하는 값이다.
예를 들어 PPU가 100이면 100픽셀이 1유닛으로 계산된다.
즉, PPU 값이 클수록 이미지가 작게 보이고, 작을 수록 크게 보인다.
카드배치(자동화)전략
카드 16장을 일일히 배치하는 건 무식하고, 이게 16장으로 고정되리란 법도 없다.
따라서 코딩을 통해 자동으로 카드가 배치되는 로직을 짜야한다.
카드 사이즈는 1.3x1.3 이고, 각 카드 간격을 0.1로 잡았을 때,
현재 존재하는 카드의 위치가 (0, 0)이라면 오른쪽에 위치한 카드는 (1.4, 0)이 된다.
상하는 y 값을 1.4씩 조절하여 4 x 4 배열로 두면 된다.
아파트로 비교해봤을 때 y값은 층수라 생각하고 x값은 호수라고 생각하면 쉽다.
13(3,0) 14(3,1) 15(3,2) 16(3,3)
09(2,0) 10(2,1) 11(2,2) 12(2,3)
05(1,0) 06(1,1) 07(1,2) 08(1,3)
01(0,0) 02(0,1) 03(0,2) 04(0,3)
1번째 카드부터 16번째 카드까지의 배치가 위와 같이 된다.
각 층마다 4장의 카드만 들어가니까 x값은 4로 나눴을 때 나머지 값이 적절하고,
4장의 카드가 생길때마다 층수가 올라가니까 y값은 4로 나눴을 때 몫 값이 적절하다.
//Board.cs 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);
}
배열 랜덤 셔플
//Board.cs Start()
arr.OrderBy(x => Random.Range(0f, 7f)).ToArray();
배열을 랜덤으로 뒤섞기 위해 강사님이 위와 같은 코드를 작성하셨다.
int가 아니라 0f, 7f와 같이 float 값을 이용한 이유가 궁금해서 검색해보니 정수 랜덤은 중복될 확률이 높아서라고...?
그치만 여전히 이해가 안간다. 내일 튜터님한테 물어보려고함.(답변 여기에 정리해두기!)
진행하다가 문제가 생겼다.
셔플이 전혀 되지 않는다.
튜터님 퇴근하셨으므로 내일 물어보려고 하다가, ChatGPT에게 물어보니 Fisher-Yates 셔플 로직을 찾아서 붙여보니 된다.
간단히 분석해보자면, 한 줄로 서 있는 녀석들 중 제일 뒤에 서있는 녀석과 앞에 있는 녀석들 중 하나와 자리를 바꾸고, 그 다음에 뒤에서 2번째 녀석은 자신보다 앞에 있는 녀석들과 자리를 바꾸는 것을 반복한다.
왜 하필 뒤에서부터 섞는 거지 하고 찾아봤다.
일단 이미 대부분의 튜토리얼이나 라이브러리에서는 이와 같은 방식을 채택해서 사용하기 때문에, 팀 작업 시 통일감 있게 작업하려면 대세를 따르는 게 맞는 것 같다.
그리고 구현 중 실수를 방지하기 위해서 뒤에서부터 섞는 방법을 채택하게 된 듯.
Random.Rande(0, i+1)는 헷갈릴 여지가 적지만 Random.Range(i, arr.Length)는 헷갈릴 수도 있다고.
암튼 지금 새로운 셔플 로직을 적용하니 섞인다. 그래도 튜터님한테 물어보긴 할 것임.
for (int i = arr.Length - 1; i > 0; i--)
{
int j = Random.Range(0, i + 1);
(arr[i], arr[j]) = (arr[j], arr[i]);
}
다른 cs에서 함수 불러오기
//Board.cs Start()
go.GetComponent<Card>().Setting(arr[i]);
go는 GameObject로 card prefab이다.
즉, 해당 GameObject에 Component로 붙어있는 Card.cs를 얻고(Get),
그 안에 있는 Setting() 메소드를 실행한다.
public이 아닌 값 Unity에서 확인하기
Inspector를 우클릭하면 Normal이 아닌 Debug를 선택할 수 있다.
게임 진행 중 오브젝트를 선택해서 해당 오브젝트를 더 디테일하게 살펴볼 수 있다.
Resource 불러오기
//Card.cs Setting()
front.sprite = Resources.Load<Sprite>($"rtan{idx}");
각 카드에 부여된 idx 넘버를 통해 rtan{idx} 라는 이름의 Sprite이미지 파일을 불러왔다.
해당하는 파일들은 Assets - Resources 폴더 안에 위치해야한다.
Invoke로 딜레이 만들기
public void DestroyCard()
{
Invoke("DestroyCardInvoke", 1.0f);
}
void DestroyCardInvoke()
{
Destroy(gameObject);
}
public void CloseCard()
{
Invoke("CloseCardInvoke", 1.0f);
}
void CloseCardInvoke()
{
animator.SetBool("isOpen", false);
front.SetActive(false);
back.SetActive(true);
}
카드를 두 장 나 오픈한 뒤에는 결과를 보여주는 딜레이를 주기 위해 Invoke()를 이용한다.
느낀점
오늘 끝내려고 했는데 끝내지 못했다!
ADHD 특성상... 한 번 멈추면 집중이 잘 되지 않으므로 끝나고 질문을 몰아서 하려고 했는데 그러면 안되겠다.
집중하다보면 시간이 금방 가서... 튜터님이 퇴근하고 안계신다.
내일 학습 할 것은 무엇인지
내일 4주차 끝내고 5주차까지 끝내려고 한다.
역기획서
참고로 오늘은 팀원들끼리 모여서 역기획서 작성도 했다.
노션에 다 같이 작성한 걸 아래에 복붙(실명만 제거했음)
# **[ 어몽어스 ( Among US)]**
**Innersloth**에서 2018년에 배급한 **소셜 추리 게임**이다.
4~15명의 플레이어가 우주선을 배경으로 게임을 진행한다.
플레이어는 크루원과 임포스터(사기꾼)로 나뉜다.
**크루원** - 주어진 임무를 완수해야 한다.
**임포스터** - 크루원을 몰래 죽이거나 방해하여 승리해야 한다.
## **1 ) 이 게임의 매력 포인트는 무엇인가요?**
● 캐쥬얼하고 귀여운 캐릭터로 친구들과 가볍게 즐기기 좋다.
● 수많은 변수(임무, 사보타주 등)로 인한 반복 플레이가 가능하다.
● 죽어도 유령 상태로 플레이가 가능한 재미 요소가 존재한다.
● 주어진 역할을 기반으로 협동을 진행하고 심리전을 펼치는 긴장감이 있다.
## **2 ) 이 게임을 좋아하는 이유는 무엇인가요?**
● - 단순한 조작과 룰을 기반으로 소셜 상호작용을 통해 심리전을 펼치는 재미
● - 친구들과 다같이 즐기기 좋고 임포스터를 찾아냈을 때 정말 짜릿하다.
● - 배정받은 역할에 몰입해 계획대로 플레이 되었을 때 느껴지는 성취감
● - 캐릭터가 귀엽고, 멀티 플레이가 가능해서 좋다!
● - 5명이상의 여러 사람과도 즐길수 있음.
● - 플레이가 어렵지 않아 남녀노소 즐길 수 있다.
## **3 ) 게임 개요**
**게임 이름 : 어몽어스**(Among us)
**장르 :** 파티 게임/마피아 심리 게임
**플랫폼 :** PC, 모바일, 콘솔 멀티 플랫폼 지원
**개발사 :** InnerSloth
**출시 년도 :** 2018년 6월 15일
**엔진 : Unity**
**플레이 방식 :** 실시간 협동 멀티 플레이
## **4 ) 시스템 개요**
**● 임무 시스템**
크루원 역할의 플레이어들이 할당 받는, 승리를 위해 수행해야 하는 핵심 시스템.
임무는 맵 곳곳에 흩어져 있는 시설을 통해 할 수 있는 미니 게임 형태로 모든 임무가 완료되면 크루원 측이 게임에서 자동으로 승리하게 된다.
임무 진행 시 행동 애니메이션은 없지만 완료 시 전체 임무 진행도가 증가한다.
**● 역할**
크루원의 목표 : 게임의 목적성 (크루원 승리) 부여
크루원 분산 유도 : 임무를 위해 크루원들이 맵 곳곳으로 이동하게 되어 임포스터에게 노출되도록 유도
게임 진행 제한 : 임무 완료 시 게임 자동 종료되므로 일종의 타이머 역할을 하게 됨
심리전 유도 : 임무를 위한 가짜 행동과 진짜 행동을 분석하여 추리하게 됨
**● 종류**
**짧은 임무 : 짧은 시간 안에 끝낼 수 있는 간단한 임무**
- **항해실**: 경로 계획하기
- **상부엔진**: 엔진 출력 전환하기
- **하부엔진**: 엔진 출력 전환하기
- **원자로**: 원자로 매니폴드 잠금 해제하기
- **전기실**: 배선 수리하기
**긴 임무 : 시간이 오래 걸리는 임무**
- **관리실**: 관리실 카드 긁기
- **무기고**: 무기고 데이터 다운로드
- **산소 공급실**: 산소 필터 청소하기
- **항해실**: 항해 데이터 다운로드
- **보관함**: 연료 채우기
- **전기실**: 전기실 보정하기
- **의무실**: 샘플 검사하기
**가짜 임무 : 임포스터가 받는 특별한 임무이다. 크루원처럼 보이기 위해 주어지는 임무이며, 실제로는 아무런 변화가 일어나지 않는다.**
- **관리실**: 관리실 카드 긁기
- **전기실**: 배선 수리하기
**● 유사 시스템**
**락다운프로토콜 :** 어몽어스와 달리 시체 발견 후 즉시 투표를 진행하지 않고, 별도의 회의 시간이 주어지지 않는 것이 특징이 있다.
**구스구스덕 :** 플레이 방식은 어몽어스와 동일하나, 다양한 능력을 가진 역할들이 존재한다.
**프로젝트윈터** : 생존 게임의 요소를 강화하여 자연 재해, 자원 관리, 협력 및 배신을 강조한다.
**Feign :** 시민, 임포스터, 중립 진영에 소속되어 본인의 진영이 승리할 때 까지 턴을 돌아가며 플레이 한다.
서로의 행동이 오가는 턴이 짧으며 정신병자라는 변수가 있다.
추가됨
과제: 30초가 되면 게임 끝내기
void Update()
{
time += Time.deltaTime;
timeTxt.text = time.ToString("N2");
if (time >= 30.0f)
{
Time.timeScale = 0.0f;
endTxt.SetActive(true);
}
}
내가 처음 짠 코드는 위와 달리 time == 30.0f를 비교하는 방식이었다.
전혀 멈추질 않길래 혹시나 싶어서 time >= 30.0f로 비교문을 변경해주었더니 바로 멈추었다.
Time.deltaTime을 계속 더하는 방식인데 정확히 30.0f를 맞추기 힘들어서 그런 듯.
'부트캠프 > 사전캠프' 카테고리의 다른 글
[내일배움 사전캠프_2025JUN27]내가 만들고 싶은 게임 찾아보기 (0) | 2025.06.27 |
---|---|
[내일배움 사전캠프_2025JUN26]쉽게 배우는 모바일 게임 5주차 - 게임 완성하기 1 (마무리) (0) | 2025.06.26 |
[내일배움 사전캠프_2025JUN24]쉽게 배우는 모바일 게임 3주차 - 고양이 밥주기 게임 2 (마무리) (0) | 2025.06.24 |
[내일배움 사전캠프_2025JUN20]쉽게 배우는 모바일 게임 3주차 - 고양이 밥주기 게임 1 (0) | 2025.06.20 |
[내일배움 사전캠프_2025JUN19]쉽게 배우는 모바일 게임 2주차 - 풍선을 지켜라 2 (마무리) (2) | 2025.06.19 |