Loading the catalog…
Loading the catalog…
Unity 2D 모바일 게임 FindClover 개발 중간점검 클로버를 찾아 제한 시간 안에 최대한 높은 점수를 획득하는 2D 모바일 게임 Unity / Android / Touch Input / JSON Save / 광고 / 성장 시스템 / 어빌리티 시스템 현재 제작하고 있는 FindClover 의 개발 과정을 중간점검해보려고 한다. 처음에는 단순히 화면에서 클로버를 찾아 터치하는 게임으로 시작했지만, 개발을 진행하면서 게임의 기본 루프뿐만 아니라 재화, 성장, 광고, 능력, 상점, 뽑기 시스템까지 점점 확장되었다. 이번 글에서는 지금까지 어떤 시스템을 구현했고, 개발 과정에서 어떤 문제를 해결했는지 정리한다. 1. FindClover는 어떤 게임인가? FindClover는 모바일 환경을 기준으로 제작하고 있는 2D 캐주얼 게임이다. 기본적인 게임 플레이는 간단하다. 게임 시작 화면에 여러 클로버가 생성된다. 제한 시간 안에 클로버를 찾아 터치한다. 세잎클로버와 네잎클로버를 구분하여 획득한다. 제한 시간이 종료되면 결과를 계산한다. 획득한 보상을 이용해 성장한다. 성장한 능력을 이용해 다시 게임을 플레이한다. 즉, 클로버 찾기 → 보상 획득 → 성장 → 다시 클로버 찾기 라는 반복 구조를 목표로 했다. 처음에는 게임의 핵심 플레이만 구현하는 것이 목적이었지만, 이후 반복 플레이에 대한 동기를 만들기 위해 성장 시스템과 재화 시스템을 추가했다. 2. 프로젝트 초기 구성 프로젝트 초기에 먼저 게임의 기본적인 구조를 만들었다. 초기 커밋에서는 다음과 같은 시스템을 순서대로 구현했다. Title / Option UI UI Manager Game Manager Clover Manager Clover Spawn Manager Touch / Drag 시스템 카메라 이동 Sound Manager Haptic Manager InGame UI Pause UI Game End UI Scene Load Manager 처음부터 모든 시스템을 완성하기보다는 게임의 기본적인 실행 흐름을 먼저 만드는 방향으로 진행했다. 특히 게임의 중심이 되는 부분은 GameManager → SpawnManager → CloverManager → TouchController 의 흐름이다. 3. 클로버 생성 시스템 게임의 핵심 오브젝트는 클로버이다. 클로버의 종류를 구분하고 이를 관리하기 위해 Clover 와 CloverManager 를 분리했다. Clover └─ 개별 클로버의 상태 및 동작 CloverManager └─ 클로버 전체 관리 SpawnManager └─ 게임 시작 시 클로버 생성 세잎클로버와 네잎클로버를 각각 관리하고, 스테이지 데이터에 따라 생성 개수와 크기를 다르게 설정할 수 있도록 구성했다. 이를 통해 스테이지마다 세잎클로버 개수 네잎클로버 개수 클로버 크기 제한 시간 보상 등을 다르게 설정할 수 있는 기반을 만들었다. 4. 모바일 터치 입력 구현 FindClover는 PC 게임이 아니라 모바일 게임을 목표로 했기 때문에 터치 입력이 매우 중요했다. 초기에는 단순 터치뿐만 아니라 화면을 드래그하여 맵을 확인하는 방식도 구현했다. Touch ├─ 클로버 선택 └─ Drag └─ 화면 이동 이 과정에서 Unity Input System을 이용해 터치 입력을 처리했다. 이후 실제 모바일 환경을 고려하면서 터치 UX를 계속 수정했다. 특히 중요한 문제가 화면 좌표와 월드 좌표의 차이 였다. 5. UI 좌표계와 월드 좌표계 문제 개발 중 클로버가 생성되는 영역을 조정하면서 좌표계를 크게 수정했다. 초기에는 월드 좌표를 기준으로 클로버의 생성 범위를 설정했다. X : -5.5 ~ 5.5 Y : -7.65 ~ 7.35 하지만 실제 모바일 UI를 기준으로 게임 화면을 구성하면서 UI 좌표를 기준으로 관리하는 것이 더 적합하다고 판단했다. 그래서 이후에는 Canvas 기준으로 다음과 같은 범위를 사용하도록 변경했다. X : -290 ~ 290 Y : -500 ~ 500 이 과정에서 단순히 좌표 값만 변경하는 것이 아니라, 터치 위치 → UI 좌표 → 게임 오브젝트 위치 의 관계를 다시 정리했다. 모바일 게임에서는 화면 해상도와 Canvas 설정에 따라 입력 좌표가 달라질 수 있기 때문에 이 부분이 생각보다 중요했다. 6. 클로버 찾기 판정 방식 개선 클로버를 찾는 과정에서도 UX 문제가 있었다. 처음에는 작은 영역을 기준으로 터치를 판정했지만, 모바일에서는 손가락으로 정확한 위치를 누르기 어려웠다. 그래서 일정 범위 안을 터치해도 클로버를 선택할 수 있도록 판정 영역을 조정했다. 초기에는 월드 좌표 기준으로 약 0.8 정도의 탐지 반경을 사용했고, 이후 UI 기반 구조에서는 약 80px 수준의 재선택 영역을 사용하도록 변경했다. 결국 중요한 것은 실제 스프라이트의 크기보다 사용자가 편하게 누를 수 있는 영역을 제공하는 것 이었다. 모바일에서는 정확한 판정보다 적절한 입력 관용성이 더 중요하다는 것을 확인할 수 있었다. 7. Depth Sorting 2D 게임이지만 클로버가 화면에 여러 개 겹쳐 보일 수 있기 때문에 깊이 표현도 필요했다. 이를 위해 Y값에 따라 Sort Order 를 변경하는 시스템을 구현했다. Y 위치 ↓ Depth 계산 ↓ Sorting Order 변경 이를 통해 화면에서 위쪽에 있는 오브젝트와 아래쪽에 있는 오브젝트가 자연스럽게 겹쳐 보이도록 만들었다. 현재는 BaseDepthSorter DynamicDepthSorter StaticDepthSorter 등으로 역할을 나누어 관리하고 있다. 8. GameManager와 게임 상태 게임 전체의 흐름은 GameManager 에서 관리한다. 현재 게임 상태는 크게 다음과 같이 구분했다. public enum GameState { Ready, Playing, GameOver } 게임이 시작되면 Playing 상태가 되고 제한 시간이 감소한다. 제한 시간이 0이 되면 게임을 종료하고 결과를 계산한다. 또한 스테이지 데이터와 현재 스테이지 인덱스를 연결하여 스테이지별 게임 설정을 사용할 수 있도록 했다. 이 구조를 통해 게임 플레이와 UI를 어느 정도 분리할 수 있었다. 9. 스테이지와 성장 시스템 게임의 반복 플레이를 만들기 위해 단순히 클로버를 찾는 것에서 끝내지 않고 성장 시스템을 추가했다. 현재 프로젝트에는 GrowthData 와 GrowthManager 를 중심으로 성장 시스템이 구성되어 있다. 성장을 통해 게임에서 얻을 수 있는 보상이나 플레이 효율을 높일 수 있도록 설계했다. 예를 들어 골드 획득량 증가와 같은 효과를 게임 플레이 결과에 적용할 수 있도록 했다. 게임 플레이 ↓ 스테이지 결과 ↓ 보상 계산 ↓ 성장 효과 적용 ↓ 최종 보상 이 구조를 만들면서 단순히 값을 증가시키는 것보다 보상 계산 과정에서 성장 효과를 연결하는 방식 을 고민하게 되었다. 10. 재화 시스템 게임 내 재화도 각각 별도의 Manager로 분리했다. 현재 주요 재화 시스템은 다음과 같다. Gold Gem Star HourGlass 각 재화는 별도의 Manager를 가지고 있으며 JSON을 이용해 저장한다. 예를 들어 Gold는 다음과 같은 데이터를 저장한다. [Serializable] public class GoldSaveData { public int totalGold; public string lastDailyRewardDate; public string lastAdResetDate; public int basicGoldAdCount; } 게임을 종료했다가 다시 실행해도 플레이어의 재화가 유지될 수 있도록 했다. 11. PlayerPrefs 대신 JSON 저장을 사용한 이유 FindClover에서는 주요 게임 데이터를 PlayerPrefs 보다는 JSON으로 저장하는 방향을 선택했다. 예를 들어 골드는 gold.json 능력 데이터는 ability.json 과 같은 방식으로 저장한다. JSON 저장을 사용하면 데이터 구조를 직접 정의할 수 있다는 장점이 있다. 특히 어빌리티 시스템처럼 Ability ID Count Upgrade Level 등 여러 데이터를 함께 저장해야 하는 경우 JSON 구조가 더 적합하다고 판단했다. 12. HourGlass 시스템 게임의 플레이 횟수 또는 플레이 자원 역할을 하는 HourGlass 시스템도 구현했다. 현재 기본 설정은 최대 보유량 : 5 회복 시간 : 8분 이다. 시간이 지나면 자동으로 회복되며, 게임을 실행하지 않은 동안에도 시간이 흐른 만큼 회복량을 계산한다. 마지막 회복 시간 ↓ 현재 시간과 비교 ↓ 경과 시간 계산 ↓ 회복 횟수 계산 ↓ HourGlass 증가 또한 광고 보상으로 HourGlass를 획득할 수 있고, 일정 시간 동안 무제한으로 사용할 수 있는 기능도 고려하여 구현했다. 13. 광고 시스템 모바일 게임이기 때문에 보상형 광고 시스템도 구현했다. Unity LevelPlay를 이용하여 광고 시스템을 구성했고, AdsManager 에서 광고 종류를 관리한다. 현재 코드에서는 다음과 같은 보상 종류를 관리하고 있다. FindClover EliminateObjects TimeExtend Revive GemAndGold BasicAbility RareAbility Gold Gem HourGlass 광고를 하나의 Manager에서 관리하고 광고 종류를 enum으로 구분하도록 구성했다. public enum AdRewardType { FindClover, EliminateObjects, TimeExtend, Revive, GemAndGold, BasicAbility, RareAbility, Gold, Gem, HourGlass } 개발 과정에서는 에디터에서 실제 광고를 기다리지 않고 테스트할 수 있도록 Mock Reward 방식도 사용했다. #if UNITY_EDITOR StartCoroutine(EditorMockReward(onRewarded)); return; #endif 덕분에 Unity Editor에서 광고 시청 과정을 빠르게 테스트할 수 있었다. 하지만 현재 완벽히 구현되진 않았다. 아직 테스팅 중이다. 14. 무료 보상과 일일 광고 제한 광고 시스템을 단순히 광고를 보여주는 기능으로 끝내지 않고 일일 제한과 무료 보상 시스템까지 연결했다. 예를 들어 골드의 경우 무료 일일 보상 ↓ 무료 보상 소진 ↓ 광고 보상 ↓ 일일 광고 제한 의 형태로 구성했다. 이 과정에서 날짜를 저장하고 다음 날이 되면 광고 횟수를 초기화하도록 구현했다. HourGlass와 Ability 시스템에도 비슷한 방식의 일일 광고 제한 구조를 적용했다. 15. Ability 시스템 현재 FindClover에서 가장 크게 확장하고 있는 부분이 Ability 시스템 이다. 초기 게임은 단순히 클로버를 찾는 구조였지만, 플레이를 반복할 이유를 만들기 위해 능력 시스템을 추가했다. 현재 AbilityManager 에서는 다음과 같은 데이터를 관리한다. Ability ID Ability Count Upgrade Level Ability Slot 그리고 플레이어의 어빌리티 데이터를 JSON으로 저장한다. 16. Ability 강화 구조 어빌리티는 동일한 능력을 여러 번 획득하여 강화하는 방식이다. 예를 들어 하나의 Ability가 여러 개 중복 획득되면 Count가 증가하고, 일정 개수 이상 모이면 Upgrade Level을 올릴 수 있다. 구조는 대략 다음과 같다. Ability 획득 ↓ Count 증가 ↓ 필요한 개수 충족 ↓ Upgrade ↓ Ability 효과 강화 최대 강화 레벨도 설정하여 무한히 성장하지 않도록 했다. 또한 Ability 효과는 Gold 획득량 증가와 같은 실제 게임 시스템과 연결했다. 17. Ability Box / Gacha 시스템 최근에는 Ability를 획득하는 방식으로 상자와 뽑기 시스템을 제작하고 있었다. 현재 구조에는 Basic Rare Epic Legendary 등의 등급이 존재한다. 확률표는 DrawProbabilityTable 이라는 ScriptableObject로 분리했다. [CreateAssetMenu(menuName = "Gacha/Draw Probability Table")] public class DrawProbabilityTable : ScriptableObject { public List<DrawLevelProbability> levels; } 이렇게 하면 코드에 확률을 직접 작성하는 대신 Unity Inspector에서 확률 데이터를 관리할 수 있다. 18. 뽑기 확률과 성장 단순한 고정 확률이 아니라 뽑기 레벨과 경험치를 함께 관리하도록 구현했다. 현재 AbilityManager에는 Rare Draw Level Legendary Draw Level Rare Draw EXP Legendary Draw EXP Rare Guarantee Legendary Guarantee 등의 데이터가 존재한다. 또한 일정 횟수마다 특정 등급 이상을 보장하는 천장 시스템도 구현하고 있다. 이를 통해 장기적으로 플레이할수록 뽑기 시스템 자체가 성장하도록 설계했다. 19. Ability Shop 제작 현재 개발의 마지막 단계에서는 Ability Shop을 제작하고 있다. ShopUI에서 구매 버튼을 누르면 구매 확인 팝업을 띄우고, 실제 구매가 진행되면 AbilityManager에서 뽑기를 실행하는 구조이다. 대략적인 흐름은 다음과 같다. ShopUI ↓ 구매 버튼 ↓ 구매 확인 Popup ↓ 재화 확인 ↓ AbilityManager ↓ Ability 추첨 ↓ 중복 처리 ↓ Ability 추가 ↓ JSON 저장 Basic / Rare / Legendary 등의 상자와 단일 구매 및 10회 구매를 연결하고 있다. 20. 개발 중 발견한 NullReferenceException Ability Shop을 구현하면서 중요한 문제도 발견했다. 구매 확정 버튼을 누르면 다음과 같은 오류가 발생했다. NullReferenceException: Object reference not set to an instance of an object AbilityManager.HandleDuplicate() AbilityManager.DrawBasicAbility() ShopUI.OnAbilityPurchaseButtonClicked() 처음에는 chestRarity 와 같은 값이 초기화되지 않은 문제를 의심했다. 하지만 실제 코드를 확인하면서 더 직접적인 원인을 찾을 수 있었다. GetRandomAbility() 는 해당 등급의 Ability가 하나도 없으면 null 을 반환한다. if (pool.Count == 0) return null; 그런데 이후 바로 HandleDuplicate(ability); 를 호출하고, HandleDuplicate() 내부에서 ability.abilityID 에 접근하고 있었다. 즉, GetRandomAbility() ↓ 해당 등급 Ability 없음 ↓ null 반환 ↓ HandleDuplicate(null) ↓ ability.abilityID 접근 ↓ NullReferenceException 이라는 흐름이었다. 이 문제를 통해 단순히 != null 체크를 어디에 추가할 것인가보다 null이 발생할 수 있는 데이터 흐름 자체를 먼저 확인해야 한다 는 것을 다시 확인했다. 21. 또 하나 확인한 구매 구조의 문제 Ability 구매 과정에서는 중복으로 Ability를 추가할 가능성도 확인했다. 현재 구매 과정에서 DrawBasicAbility() ├─ HandleDuplicate() └─ AddAbility() 가 실행되는 동시에 Shop의 다른 구매 처리에서도 AddAbility() 가 호출될 가능성이 있었다. 이 경우 한 번 구매했는데 Ability Count가 두 번 증가하는 문제가 발생할 수 있다. 따라서 현재는 단순히 오류 하나를 고치는 것뿐만 아니라, Ability를 실제로 획득하는 책임을 어디에서 담당할 것인지 를 다시 정리할 필요가 있는 상태이다. 22. 현재 프로젝트 구조 현재 Assets/Scripts 기준으로 보면 프로젝트가 상당히 여러 영역으로 분리되어 있다. Scripts ├─ Ability │ └─ AbilityData │ ├─ Clover │ └─ Clover │ ├─ Controll │ ├─ TouchController │ ├─ CameraJoystickMove │ └─ CameraMultiTouchMove │ ├─ Global │ ├─ BaseDepthSorter │ ├─ DynamicDepthSorter │ └─ StaticDepthSorter │ ├─ Growth │ └─ GrowthData │ ├─ Level │ └─ LevelData │ ├─ Manager │ ├─ GameManager │ ├─ CloverManager │ ├─ SpawnManager │ ├─ TouchManager │ ├─ AbilityManager │ ├─ GrowthManager │ ├─ GoldManager │ ├─ GemManager │ ├─ StarManager │ ├─ HourGlassManager │ ├─ AdsManager │ ├─ SoundManager │ ├─ HapticManager │ └─ UIManager │ ├─ Shop │ └─ DrawProbabilityTable │ └─ UI ├─ InGameUI ├─ GameEndUI ├─ ShopUI ├─ AbilityUI ├─ GrowthUI ├─ InventoryUI └─ ... 처음에는 하나의 게임을 만드는 것이 목적이었지만, 현재는 게임의 각 기능을 별도의 Manager와 Data 구조로 분리하는 형태로 발전했다. 23. 현재까지 개발하면서 배운 점 이번 프로젝트에서 가장 크게 느낀 점은 게임의 기능을 구현하는 것과 게임 시스템을 설계하는 것은 다른 문제 라는 것이다. 처음에는 클로버를 생성하고 터치하면 된다. 라고 생각했다. 하지만 실제로 개발해보니 다음과 같은 문제가 계속 추가되었다. 클로버 생성 ↓ 터치 판정 ↓ 모바일 UX ↓ 좌표계 ↓ 스테이지 ↓ 보상 ↓ 재화 ↓ 저장 ↓ 성장 ↓ 광고 ↓ Ability ↓ Shop ↓ Gacha 하나의 기능이 추가될 때마다 기존 시스템과 연결해야 했다. 특히 Manager가 많아지면서 어떤 시스템이 어떤 시스템의 데이터를 변경해야 하는지 가 중요해졌다. 24. 현재 FindClover의 상태 현재까지 구현된 시스템을 정리하면 다음과 같다. 게임 기본 시스템 클로버 생성 세잎 / 네잎클로버 구분 터치 입력 드래그 Depth Sorting 스테이지 제한 시간 게임 시작 / 종료 Pause Game End UI Title Option InGame UI Pause UI Game End UI Inventory Growth Shop Ability UI 재화 Gold Gem Star HourGlass 성장 Growth System Ability System Ability Upgrade Ability Slot 저장 JSON Save / Load 일일 보상 날짜 저장 광고 횟수 저장 Ability 데이터 저장 HourGlass 회복 시간 저장 광고 LevelPlay 보상형 광고 무료 보상 일일 광고 제한 Ability 광고 구매 Gold / Gem / HourGlass 광고 보상 상점 재화 구매 Ability Box 단일 구매 10회 구매 등급별 확률 뽑기 레벨 경험치 천장 시스템 까지 구현한 상태이다. 25. 앞으로 해결해야 할 부분 현재 가장 먼저 해결해야 하는 것은 Ability Shop의 구매 흐름이다. 특히 1. null Ability 처리 2. Ability 중복 획득 처리 3. 구매 → 추첨 → 저장 흐름 정리 4. 1회 / 10회 구매 로직 통합 5. 광고 구매와 일반 구매 로직 통합 을 우선적으로 정리할 예정이다. 그 이후에는 실제 모바일 환경에서의 테스트를 중심으로 진행할 필요가 있다. 특히 터치 판정과 UI 크기, 광고 보상, 저장 데이터, 앱 종료 후 데이터 복구 등을 실제 Android 환경에서 계속 확인할 예정이다. 마무리 FindClover는 처음에는 단순한 2D 클로버 찾기 게임 으로 시작했다. 하지만 지금은 게임 플레이 → 보상 → 재화 → 성장 → 능력 → 상점 → 뽑기 로 이어지는 하나의 게임 시스템을 갖추는 단계까지 발전했다. 이번 프로젝트에서 가장 의미 있었던 부분은 단순히 기능을 많이 구현했다는 것이 아니다. 처음에는 기능 하
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
유니티 개발일지_20_FindClover 중간 점검. Unity 2D 모바일 게임 FindClover 개발 중간점검 클로버를 찾아 제한 시간 안에 최대한 높은 점수를 획득하는 2D 모바일 게임 Unity / Android / Touch Input / JSON Save / 광고 / 성장 시스템 / 어빌리티 시스템 현재 제작하고 있는 FindClover 의 개발 과정을 중간점검해보려고 한다. 처음에는 단순히 화면에서 클로버를 찾아 터치하는 게임으로 시작했지만, 개발을 진행하면서 게임의 기본 루프뿐만 아니라 재화, 성장, 광고, 능력, 상점, 뽑기 시스템까지 점점 확장되었다. 이번 글에서는 지금까지 어떤 시스템을 구현했고, 개발 과정에서 어떤 문제를 해결했는지 정리한다. 1. FindClover는 어떤…
Open source