제3회 UNIJAM 후기
게임잼을 준비하며
1월 10일부터 12일까지 2박 3일간 진행한 제3회 UNIJAM에 대한 후기를 남겨보려 합니다
작년 여름에 참가한 제1회 PoolC 게임잼에 이은 두 번째 게임잼 참가인데, 처음에는 긴장 반 두려움 반이었다면 이번에는 한 번 해봐서인지 긴장이 조금 덜됐던 것 같네요
PoolC 게임잼에서는 기획 포지션으로 참여했고, 작년 1학기에 진행했던 프로젝트도 기획 포지션으로 진행해서 프로그래밍으로 참가하는 건 이번이 처음이었습니다
그렇다고 해서 게임 프로그래밍을 완전 처음 하는 것이냐… 고 묻는다면 다행히 그건 아닙니다
재작년, 그러니까 2023년 겨울 즈음부터 udemy, 인프런 등에서 인강을 보면서 공부를 시작했고, 그 이후로 유튜브에서도 강의를 들으며 공부했습니다
다만 작년에 프로젝트 진행하면서 개발보다 기획에 조금 더 집중하는 바람에, 게임잼이 한달 정도 남았을 때 코딩 지식이 거의 초기화… 된 상태였습니다
그래서 남은 기간 유튜브 강의(특히 골드메탈님꺼 많이 들었네요) 다시 들으면서 복습했고, 기억을 조금이나마 되살린 상태로 행사장에 들어갔습니다
게임잼을 진행하며
게임잼 후기로 어떤 내용들을 남길까 고민했습니다
팀 선정 과정에서의 우여곡절, 행사 중간중간 진행한 이벤트들, 열악한 환경에서 최대한 살아남기 등 여러 뒷이야기가 있지만, 쓰다 보면 예상했던 분량보다 길어질 것 같고, 무엇보다 개발 과정에 대한 이야기를 더 담고 싶어서 일단 넘어가려고 해요
그러면 바로 시작하겠습니다 👍
주제: 점입가경
게임잼에서 가장 중요하다고 할 수 있는 게임의 주제로 점입가경이라는 키워드가 선정되었습니다
저희 팀은 ‘시간이 지날수록 점점 막장이 되어간다’라는 의미에 주목하여 기획하였고, 두 명의 플레이어가 끊임없이 분열하는 보석들을 이용해 전투하여 상대의 넥서스를 파괴하는 쪽이 승리한다는 방향으로 개발을 시작했어요
팀은 기획자 2, 개발자 2, 아트 1로 구성되었고, 다른 개발자분이 서버 개발, 저는 클라이언트 개발을 맡아 진행하게 됐습니다
개발은 플레이어와 넥서스 구현 - 유닛 충돌 구현 - 유닛 파괴 및 생성 구현 - 게임 플레이 화면 UI 구현 - 플레이어 공격 기능 구현 - 카드 시스템 구현 - 버그 수정 과정으로 진행했어요
전체적인 플로우는 잘 진행되었으나 딱 한 가지, 유닛이 처치되면 분열하는 함수를 제대로 만들지 못했습니다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public void Split(int splitTimes, Transform transform, EUnitGroup hitUnitGroup)
{
for (int i = 0; i < splitTimes; i++)
{
GameObject newObject = null;
if (newUnitGroup == EUnitGroup.Enemy)
{
newObject = GameManager.instance.pool.Get(GameManager.instance.computerCharacterIndex);
newObject.GetComponent<NeutralUnit>().unitGroup = EUnitGroup.Enemy;
}
else if (newUnitGroup == EUnitGroup.Allay)
{
newObject = GameManager.instance.pool.Get(GameManager.instance.playerCharacterIndex);
newObject.GetComponent<NeutralUnit>().unitGroup = EUnitGroup.Allay;
}
else
{
newObject = GameManager.instance.pool.Get(0);
}
newObject.transform.position = transform.position + Vector3.up * 0.2f * i;
newObject.transform.rotation = Quaternion.identity;
Collider2D newColl = newObject.GetComponent<Collider2D>();
Rigidbody2D newRb = newObject.GetComponent<Rigidbody2D>();
NeutralUnit neutralScript = newObject.GetComponent<NeutralUnit>();
newColl.isTrigger = false;
newRb.isKinematic = false;
newObject.layer = LayerMask.NameToLayer("Active Unit");
if (newUnitGroup == EUnitGroup.Allay)
{
GameManager.instance.playerUnits.Add(newObject);
}
else if (newUnitGroup == EUnitGroup.Enemy)
{
GameManager.instance.enemyUnits.Add(newObject);
}
foreach (Transform child in newObject.transform)
{
child.gameObject.tag = "Split Disable";
}
neutralScript.health = 8;
neutralScript.MoveToRandomDirection();
}
}
해당 함수의 코드 일부분인데, 실행해 보면 splitTimes = 1일 때 유닛이 움직이지 않고, 좁은 공간에선 기하급수적으로 유닛들이 분열하여 컴퓨터가 멈추는(…) 심각한 버그도 있었습니다
나중에 가서야 PoolManager에 유닛 최대 수 제한도 걸고, 변수 몇 개를 어거지로 추가해서 제한을 걸어두니까 다행히 컴퓨터가 터지진 않더라고요
그래도 지금 생각해 보면 조금 더 간결하게 구현할 수 있었을 것 같은데… 저 때는 왜 이렇게 구현했는지 잘 모르겠습니다 허허
결과적으로 이런 느낌으로 나왔네요
다음으로 유닛들의 충돌과 생성 이외에 업그레이드를 담당할 카드 기능도 구현했습니다
초기 기획 당시 카드를 등급별로 나누고(일반, 레어, 에픽) 시간의 흐름에 따라 등장하는 카드의 등급이 높아지도록 설계하려고 했으나, 시간 관계상 일반 등급의 카드만 구현하게 됐어요
요런 식으로 Scriptable Object를 만들어서 이름, 설명, 효과 등의 데이터를 담았고, 카드를 클릭하면 Card 스크립트에서 데이터를 가져와 수치들을 적용하는 간단한 기능입니다
원래는 2일 차 자정까지 예비 빌드본을 만들고 이후에는 버그 수정과 폴리싱을 할 계획이었지만, 개발이 생각보다 늘어지는 바람에 결국 최종 제출 1시간 전에서야 개발이 마무리되었습니다
그래서 막판에는 기획 한 분과 아트 한 분이 합세하여 4명이 개발을 진행했는데, 도와주신 덕분에 마무리할 수 있었던 것 같네요
그러나 엎친 데 덮친 격으로 최종 빌드본에서 버그가 발생해, 이후 시연을 진행할 때 플레이어 공격 기능과 카드 시스템이 제대로 작동하지 않는… 안타까운 일이 있었습니다
게임잼 회고
어찌저찌 게임잼이 마무리됐는데, 개발이 처음이었던 만큼 아쉬운 점들이 많은 것 같네요
우선 개발 초기에 미리 오브젝트들의 스크립트를 어떻게 설계할지 정하고 갔어야 했던 것 같습니다
그렇게 하지 않으니까 개발 막바지에 어떤 함수가 어떤 기능을 하는지, 변수가 어디서 이용되고 있는지 파악하기 너무 힘들어서 개발 진행에 어려움을 많이 겪었습니다
같은 맥락에서 오브젝트들을 구분할 때 태그와 레이어를 너무 남발했고, 둘을 혼용해서 쓰다 보니 나중에 개발하는 과정에서 조건문을 만들 때 헤맸던 것 같네요
또한 이건 기획의 영역과도 맞닿아있는데, 개발의 큰 틀을 명확하게 정하고 끝까지 갔어야 했던 것 같습니다
특히 서버 구축은 시간도 오래 걸리고, 클라이언트 개발보다 복잡해서 아예 처음부터 빼버리고, 싱글플레이 게임으로 밀고 갔으면 다른 부분에 시간을 많이 투자할 수 있었을 텐데 이 부분이 가장 아쉬운 것 같습니다
또한 멀티플레이인 것을 감안하고 개발하다가 도중에 싱글플레이로 바꾸다 보니, 몇몇 함수들이 고장 나 고치는데 많이 애먹었습니다
협업과 관련된 부분에선 막판에 개발하는 사람이 4명으로 늘어나니까 Git에서 브랜치 4개로 작업하고 씬도 따로따로 만들어서 작업했는데, 나중에 브랜치를 머지하는데 시간이 너무 많이 소요돼서 고생했습니다
마지막으로 제 역량 부족으로 어떻게 보면 게임의 가장 중요한 메카닉인 유닛의 생성과 분열을 제대로 구현하지 못한 것 같아서, 팀원들에게 미안하게 생각하고 있습니다
개발 초반에 간단하게 작동하는 프로토타입을 만들고 다른 기능들을 구현하기 시작했어야 하는데, 제대로 작동하지 않는 상태에서 버그를 안 고치고 미루다 보니 스노우볼이 너무 크게 구른 것 같네요
핵심적인 부분을 집중해서 개발하고 고치고, 나머지 부분들에 대한 우선순위를 미뤄야 하는데, 저는 정반대로 했던 것 같습니다
저의 전체적인 개발 속도도 많이 느렸던 것 같아요
이는 실력과 지식이 부족한 것도 있고, 처음에 제대로 설계하지 못한 채 개발을 진행해서 그렇게 된 부분도 있는 것 같습니다
그래도 아쉬운 점만 남은 것은 또 아닙니다
개발을 진행하며 이전에 공부했던 것들을 적용해서 구현할 때, 그래도 공부한 게 효과가 있구나 생각이 들면서 기분이 좋았습니다
특히 유닛 생성을 오브젝트 풀링을 이용해 구현할 때, 이전에 유튜브 강좌를 들으며 공부했던 내용이 도움이 많이 되었어요
개발하면서 부족한 부분이 많았던 만큼, 내가 앞으로 어떤 내용을 먼저 공부해야 하는지, 개발에 있어서 어떤 지식이 필요한지도 많이 배웠던 것 같습니다
우선 스크립트 하나에 코드를 다 때려넣는게 아니라, 체계적으로 분류해서 구현하는 방법부터 몸에 익혀야 할 것 같네요 🤔
무엇보다 게임 개발에 대한 열정을 다시 확인할 수 있었던 뜻깊은 시간이었던 것 같습니다
사실 게임잼이라는 게 육체적으로, 정신적으로 많이 힘든 상황에서 개발을 하는거라 중간중간 포기하고 싶은 생각도 들고, 내가 이걸 왜 신청했지라는 생각도 들었지만, 그런 상황에서 내가 개발한 게 잘 작동할 때의 기분은 이루 말할 수 없을 정도로 좋습니다
더 많이 공부해서, 다음 게임잼 때는 조금 더 수월하게 개발을 진행할 수만 있다면 좋겠네요
혹시 게임잼에 참여할지 말지 고민 중이신 분이 있다면, 주저하지 말고 꼭 참가해 보셨으면 좋겠습니다!
다음 프로젝트는 이번 게임잼을 복기하는 겸해서 비슷한 기능들을 가진 게임을 만들어볼 생각입니다
사실 이 글을 쓰는 시점에서 기초적인 기능 몇 가지를 구현해 놓은 상태입니다
근데 다시 만들어보니까 게임잼 때 삽질한 게… 참 많군요 😂
그래서 당분간 해당 프로젝트에 대한 포스팅 위주로 글을 올릴 것 같습니다
긴 후기 읽어주셔서 감사합니다 😘