Gloomy

인조인간 '글루미'의 눈물로 키운 꽃을 마을에 심어 주민을 '정화'하는 방치형 육성 전략 시뮬레이션 게임

개요

종류
모바일 2D 게임
기간
2025.01 ~ 현재
인원
1인
내 역할
기획, 개발 전체
기술 스택
Unity 6C#

핵심 성과

구조 없이 기능을 붙이다 무너진 구버전을 다시 설계하면서, 안정적인 구조 위에서 기능을 쌓는 법을 배웠습니다. 그 구조 위에 직접 만든 기획 툴과 게임을 잇는 대사 시스템을 붙였습니다.

1. 엉킨 구조를 한 방향 구조로 다시 설계

  • 문제:
    • 설계 없이 기능을 계속 붙이다보니, 구버전은 매니저끼리 서로 직접 부르는 호출이 233회, 그중 특정 매니저 하나에만 70회였으며, 하나를 고치면 어디가 깨질지 알 수 없었습니다.
    • 초기화가 시작 씬에 몰려 있고 순서도 정해져 있지 않아서, 다른 씬에서 플레이하면 매번 null 에러가 났습니다. 그때마다 원인 대신 null 검사(?.)로 증상만 막았고, 이런 임시방편이 기술 부채로 쌓여 새 기능을 붙일수록 어디서 무엇이 빠졌는지 알기 어려워졌습니다.
  • 해결:
    • Scenes(보여주기) / Systems(상태) / Core/Rules(순수 계산) / Data(저장 값) 네 층으로 나누고 위에서 아래로만 부르게 했습니다. 아래에서 위로는 이벤트로만 알립니다.
    • 초기화를 시작 진입점 한 곳에서 순서대로 실행하고, 에디터에서는 어느 씬에서 플레이해도 같은 초기화를 거치게 했습니다.
    • 리팩토링 과정에서 구버전이 무너진 원인을 규칙 문서로 적어, AI가 짠 코드도 같은 기준으로 검토했습니다.
  • 성과:
    • 이후 기능은 “어느 층에 속하는가, 누가 부르는가”를 먼저 정하고 만듭니다. 대사 시스템도 이 기준으로 게임 코드와 분리된 모듈로 만들어, 기존 시스템을 수정하지 않고 붙였습니다.
  구버전 리부트
의존 방향 특정 매니저 직접 호출 70회 위에서 아래로만, 위로는 이벤트
전역 객체 6개 클래스가 각자 유지, 정적 인스턴스 25개 진입점 한 곳에서만 유지
초기화 시작 씬에 의존, 다른 씬에서 테스트 시작 시 null 에러 진입점에서 순서대로, 어느 씬에서든 플레이 가능
초기화 흐름
1. 저장 파일 불러오기
2. 게임 시계 복원 (오프라인 경과는 계산만 하고, 아직 알리지 않음)
3. 각 시스템 준비 (오프라인 경과 알림을 받을 준비)
4. 오프라인 경과 알리기 ← 반드시 모든 시스템이 준비된 뒤
5. 시작 씬 결정 후 이동

4번이 3번보다 먼저 오면, 아직 준비 안 된 시스템은 오프라인 동안 일어난 일을 통째로 놓칩니다. 에러도 나지 않아서 찾기 어려운 문제라, 순서를 Unity 실행 순서에 맡기지 않고 진입점에서 직접 정했습니다.

코드 비교: 직접 호출에서 이벤트로
  • 구버전 GloomyManager.cs: 스탯 하나 바꾸는 함수가 UI 갱신과 파일 저장까지 직접 함
public void SetGBlue(float setValue) {
    currentGloomy.blue += setValue;
    ...
    BaseUIManager.instance?.UpdateGloomyBlue(currentGloomy.blue);  // 데이터가 UI를 직접 부름
    SaveCurrentGloomy();  // 값이 바뀔 때마다 파일 쓰기
}
  • 리부트 SaveService.cs + GameClock.cs: 저장은 “곧 저장한다”는 이벤트만 보내고, 각자 자기 값을 기록
// SaveService — 무엇을 저장할지 모르고, 어떻게 저장할지만 안다 ---------
public event Action BeforeSave;
...
BeforeSave?.Invoke();  // 저장 직전에 알리기만 한다
string json = JsonConvert.SerializeObject(Data);

// GameClock — 자기 시각은 자기가 기록한다 ---------
saveService.BeforeSave += WriteToState;

2. 시간을 한 곳에 모아 테스트 가능하게

  • 문제:
    • 현재 시각을 읽는 코드가 9개 파일에 22번, 프레임 시간이 14개 파일에 흩어져 있어서, 배속이나 시간 이동을 넣을 곳이 없었습니다.
    • 시간에 따라 변하는 스탯 등을 확인하려면 직접 기다리거나 매번 직접 수치를 변경하여야 했습니다.
  • 해결:
    • 게임의 모든 시각을 게임 시계 하나에서만 읽게 하기로 직접 정했습니다. 1분 단위로 시간이 흐르고, 배속(1~3600배)과 시간 점프를 지원합니다.
    • 배속을 높이면 한 프레임에 계산이 수천 번 몰려 화면이 멈출 수 있습니다. 그래서 프레임당 처리량에 상한을 두고, 남은 시간은 버리지 않고 다음 프레임으로 넘겼습니다.
    • 앱을 껐다 켰을 때와 백그라운드에서 돌아왔을 때를 같은 코드로 처리합니다. 오래 접속하지 않았다면 게임 날짜는 현실과 똑같이 맞추고, 그동안의 성장 계산만 최대 7일치로 제한했습니다.
  • 성과:
    • 시간을 읽는 곳이 9개 파일에서 1곳으로 줄었습니다.
    • 하루가 지나야 확인할 수 있던 테스트를 버튼 한 번으로 할 수 있게 됐습니다.
    • 배속을 높이거나 오래 접속하지 않았다가 복귀해도 화면이 멈추지 않고, 날짜도 어긋나지 않습니다.

3. 기획 도구와 게임을 잇는 대사 시스템

  • 문제: 구버전은 대사 파일 형식이 3종이라 읽는 코드가 4곳에 따로 있었고, 타이핑 연출도 3벌이었습니다. 형식이 서로 맞지 않아 같은 기능 여러 버전으로 구현해야했으며, 기능 하나를 고치려면 여러 곳을 모두 고쳐야 했습니다.
  • 해결:
    • 직접 만든 기획 도구 ‘Scriber’의 내보내기 형식과 Unity 대사 시스템을 함께 설계했습니다.
    • 대사를 고칠 때: Scriber에서 수정하고 내보내기만 하면 게임에 반영됩니다.
    • 새 연출을 추가할 때: 게임 쪽에 이름과 처리 동작을 한 줄 등록하면 연결됩니다.
    • 빠진 연결이 있으면 재생 전에 목록으로 알려줍니다.
  • 성과:
    • 대사, 분기, 화자, 효과 수치, 연출 문구를 고칠 때 코드 수정 0
    • 타이핑 3벌과 대사 형식 3종을 각각 하나로 통합
    • 빠진 연결을 플레이 도중이 아니라 재생 전에 대사 시스템의 알림으로 발견
    • 진행 로직을 단위 테스트 11개로 검증

AI와 협업 방식

구버전은 혼자 공부하며 만들었고, AI에는 로직 질문만 했습니다. 리부트에서는 저의 계획을 함께 고민하고 AI가 코드를 쓰되, 제가 정한 규칙 안에서만 쓰고 있습니다.

상세
  • 역할 분담
    • 기획, 문제 발견, 씬 배치, 테스트, 커밋은 제가 합니다.
    • 구현 계획과 데이터 흐름은 AI와 상의해서 정하고, 최종 결정은 제가 합니다.
  • 규칙 문서
    • 구버전이 무너진 원인(직접 호출, 중복 구현, 수치 하드코딩, 흩어진 시간 처리)을 금지 규칙으로 문서에 적었습니다.
    • AI가 작성한 코드도 이 규칙을 기준으로 검토하고, 위반하면 커밋하지 않습니다.
    • 요청하지 않은 기능이나 지금 쓰이지 않는 코드를 만들지 않도록, 구현 계획에 “이번에 만들 것”과 “안 만들 것”을 나눠 적게 했습니다.
  • 검증 방식
    • 게임 본체: 모든 코드를 직접 열어 확인합니다. 핵심 로직은 이해하고 검증한 다음 커밋합니다.
    • 개발용 툴: 기능 단위 명세를 먼저 쓰고, 구현 계획 점검과 기능 테스트로 검증합니다.