미궁기사 키우기 - 프로젝트 상세

2026-03-13 방치형 RPG Unity 2022.3 LTS, C#, Naninovel, DOTween

프로젝트 개요

Naninovel 비주얼 노벨 엔진과 방치형 RPG 시스템을 통합한 게임입니다. 플레이어는 기사를 조작해 무한 스테이지의 몬스터를 자동 전투로 처치하며 골드를 획득하고, 골드로 스탯을 강화해 더 높은 스테이지에 도전합니다. 3명의 동료 캐릭터(Ariel, Elena, Lynn)를 해금하고 호감도를 쌓으면 개별 스토리가 진행되는 구조입니다.

팀원 소개

이름 역할 담당 업무
김경학 프로그래머, 기획, 아트 프로젝트 전체 개발 (시스템 설계, 동료 시스템, 장비 제작 시스템, Naninovel 연동, Claude Skill 기반 시나리오 자동 생성, 일러스트 제작)

기술 스택

  • 엔진: Unity 2022.3 LTS
  • 언어: C#
  • 라이브러리: Naninovel (비주얼 노벨 스크립트 엔진), DOTween (애니메이션), DarkNaku.Number (대수 표기)
  • 시나리오 생성: Claude Code Skill (nani-scenario-generator)
  • 아키텍처: Singleton, Object Pool, Observer (이벤트), Interface 기반 전투 타겟팅
  • 데이터: ScriptableObject (캐릭터/적 스탯), CSV (외부 데이터), JSON (세이브)

Claude Skill 기반 Naninovel 시나리오 자동 생성

이 프로젝트에서는 Claude Code의 커스텀 스킬(nani-scenario-generator)을 개발해, 자연어 시나리오를 Naninovel 스크립트(.nani)로 자동 변환하는 워크플로우를 구축했습니다.

시나리오 생성 워크플로우

캐릭터 일러스트는 니지저니(Nijiourney)로 제작하고, 캐릭터 설정과 스토리 초안은 제미나이(Gemini)가 작성합니다. 작성된 시나리오를 Claude Skill에 전달하면 Naninovel 규칙에 맞춰 스크립트가 자동 완성됩니다.

[시나리오 생성 워크플로우]
1. 니지저니(Nijiourney) → 캐릭터 일러스트 제작
2. 제미나이(Gemini) → 캐릭터 설정, 스토리 초안 작성
3. Claude Skill(nani-scenario-generator) → Naninovel 스크립트 자동 변환

nani-scenario-generator 스킬 구조

스킬은 .claude/skills/nani-scenario-generator/SKILL.md에 정의되어 있으며, Naninovel의 전체 명령어 문법을 포함합니다. 자연어 입력에서 캐릭터, 배경, 선택지, 효과음, 카메라 연출 등을 추출해 .nani 파일로 변환합니다.

; --- 스킬이 지원하는 Naninovel 명령어 ---
@back BackgroundName time:1.0        ; 배경 전환
@char CharacterName.pose pose:center  ; 캐릭터 표정/위치
@hide CharacterName                   ; 캐릭터 숨기기
@hideAll time:1                       ; 전체 숨기기
@choice "선택지" goto:.LabelName     ; 분기 선택지
@goto .LabelName                      ; 라벨 이동
@bgm BGMName volume:0.3              ; 배경음악
@sfx SFXName                          ; 효과음
@camera zoom:0.3                      ; 카메라 줌
@shake MainBackground power:0.2       ; 화면 흔들기
@spawn SpawnName pos:55,45            ; 이펙트 생성
@showUI / @hideUI                     ; UI 표시/숨기기
@wait / @delay                        ; 대기
@end                                  ; 스크립트 종료

스킬 입력 예시

[입력] "Scene starts in a dark dungeon.
Elena is scared, cornered by slimes.
She screams for help.
The player can choose to help her immediately or watch first."

스킬 출력 결과

; Elena story 1
@back Dungeon0 time:1
@showUI time:1
@bgm Action2 volume:.3

@char Elena.scared pose:center
Unknown: 으악! 안 돼! 이건 졸업 기념으로 맞춘 특제 로브란 말이야!

@char Slime pose:left
Unknown: 저리 가! 이렇게 붙으면 마법을 못쓴다고!

@choice "구해준다." goto:.Rescue
@choice "잠깐 지켜본다." goto:.Watch
@stop

# Rescue
나는 검을 뽑아 들고 슬라임 사이로 뛰어들었다.
@hide Elena
@char Slime pose:center
@camera zoom:.3
@delay .5
    @spawn SwordSlashThinBlue pos:55,45
    @sfx Skill_Knife_Throw_B
@delay 1.2
    @hide Slime lazy:true
    @sfx Weapon_Impact_Blood

이 워크플로우를 통해 3개 캐릭터 × 5개 에피소드, 총 16개의 시나리오 스크립트를 효율적으로 생성했습니다.

시나리오 통계

항목수치
시나리오 스크립트16개 (.nani 파일)
총 선택지(Choice) 지점39개
분기 라벨(Label)60개 이상
사용 배경(Background)12종 (던전, 숲, 동굴, 비, 모닥불, 레이저 복도 등)
캐릭터 표정(Pose)14종 (default, happy, angry, scared, nervous, love, sad, shy, embarrassed, tired, disgusted, thinking, worried, determined)
효과음/배경음20종 이상 (전투, 마법, 환경음, 타격음)
카메라 연출줌, 흔들기, 페이드

3개 캐릭터 스토리 라인

각 캐릭터는 프롤로그 1개 + 에피소드 5개로 구성되며, 선택지에 따라 대사가 분기합니다.

Elena - 수석 졸업생 마법사

에피소드 1에서 던전에서 슬라임에게 쫓기는 모습으로 첫 등장합니다. 플레이어가 구해주거나 지켜보는 선택지로 시작해, 마법 실전 강의(에피소드 2), 수정 동굴 사고와 스킨십(에피소드 3), 모닥불 야간 장면(에피소드 4), 최종 전투와 평생 계약(에피소드 5)으로 이어집니다.

선택지는 전투 방식(직접 구출 vs 관전), 대화 태도(위로 vs 놀림), 관계 발전(수락 vs 거절) 등 상황마다 2~3개의 분기를 제공합니다. 에피소드 5에서는 최종 선택지로 "평생 함께하자"와 "보수는 너로 충분해" 두 가지 결말이 있습니다.

Lynn - 용병 기사

에피소드 1에서 던전에서 몬스터와 교전 중인 용병으로 만납니다. 왕립 기사냐는 질문, 협력 제안, 전투 돕기 등의 선택지로 관계가 시작됩니다. 레이저 트랩에서 함께 넘어지는 해프닝(에피소드 2), 불침번 교대(에피소드 3), 전투 전 손잡기(에피소드 4), 최종 전투 후 새로운 계약(에피소드 5)으로 진행됩니다.

Lynn의 스토리는 전투와 전문성이 핵심 테마입니다. 선택지는 작전 수락, 걱정 표현, 로맨스 모멘트 등으로 구성되고, 최종적으로 플레이어와 "새로운 계약"을 맺으며 용병 생활을 마감합니다.

Ariel - 숲의 수호자

에피소드 1에서 숲 던전에서 활을 겨누며 경계하는 엘프로 등장합니다. 길을 잃었다고 설명하거나 항복하는 선택지로 시작합니다. 흔들리는 다리 공포(에피소드 2), 비 피하며 동굴에서 대화(에피소드 3), 괴물 습격 시 아리엘의 희생(에피소드 4), 모닥불 앞 맹세(에피오드 5)로 이어집니다.

에피소드 3의 비 피하기 씬에서는 일러스트 CG(ArielCG0)가 등장하고, 에피소드 4에서는 아리엘이 플레이어를 구하기 위해 몸을 던지는 연출로 감정적 몰입을 높입니다. 최종 선택지에서 숲을 함께 떠나거나 영원히 곁에 있겠다는 약속으로 마무리됩니다.

동료 시스템과 스토리 연동

3명의 동료는 각각 고유한 CompanionData ScriptableObject로 스탯, 애니메이션, 해금 비용을 관리합니다. 한 번에 1명만 활성화할 수 있으며, CompanionSystem 싱글톤이 해금, 호감도, 리스폰을 관리합니다.

호감도는 활성화된 동료와 함께 전투하는 동안 20초마다 1씩 상승하며 최대 100까지 쌓입니다. 스토리 에피소드는 호감도 기준(예: 에피소드 1은 호감도 10, 에피소드 5는 호감도 80)으로 해금되어, 전투와 스토리가 자연스럽게 연결되는 루프를 구성합니다.

동료의 레벨은 플레이어 레벨에 동기화됩니다. 플레이어가 레벨업하면 OnLevelUp 이벤트를 통해 동료 스탯도 갱신됩니다. 동료 AI는 플레이어가 공격 중인 적에게 2.5배 거리 페널티를 적용해 적을 분산시키는 전략을 사용합니다.

Naninovel 커스텀 명령어

한국어 조사 처리를 위해 @jong 커스텀 명령어를 구현했습니다. 문자열의 마지막 글자 받침 여부를 판별해 올바른 조사(은/는, 이/가 등)를 선택합니다.

[CommandAlias("jong")]
public class JongsungCheck : Command
{
    [ParameterAlias("text")]   public StringParameter SourceText;
    [ParameterAlias("with")]   public StringParameter WithBatchim;    // 받침 있을 때
    [ParameterAlias("without")] public StringParameter WithoutBatchim; // 받침 없을 때
    [ParameterAlias("set")]    public StringParameter TargetVariable;
    [ParameterAlias("append")] public BooleanParameter AppendToSource;

    private static bool HasFinalConsonant(string text)
    {
        var lastChar = text[text.Length - 1];
        const int hangulBase = 0xAC00;
        const int hangulLast = 0xD7A3;
        if (lastChar < hangulBase || lastChar > hangulLast) return false;
        var code = lastChar - hangulBase;
        var jong = code % 28;
        return jong != 0;
    }
}

시나리오에서의 사용 예시:

; 플레이어 이름의 받침 여부에 따라 조사 선택
@jong text:{PlayerName} with:이 without:"" append:true set:name
@choice "난 {name}야. 잘 부탁해." goto:.Accept

스토리 종료 시 @end 커스텀 명령어가 메인 게임 씬으로 전환합니다.

[CommandAlias("end")]
public class SwitchToAdventureMode : Command
{
    public override async UniTask Execute(AsyncToken asyncToken)
    {
        var opMain = SceneManager.LoadSceneAsync("main");
        await UniTask.WaitUntil(() => opMain.isDone);
    }
}

DialogueManager는 스토리 재생 시 SceneFadeManager로 페이드 효과와 함께 스토리 씬으로 전환하고, Naninovel 엔진 초기화 후 플레이어 이름을 커스텀 변수로 설정합니다.

지수 성장 스탯 시스템

캐릭터와 적의 스탯은 지수 함수 기반으로 성장합니다. CharacterStatsData ScriptableObject에 기본값과 성장 계수를 정의합니다.

Stat(n) = baseStat × growthRate^(n-1)

// 예: HP의 경우 baseMaxHealth=150, growthHealth=1.12
// 레벨 10 → 150 × 1.12^9 ≈ 415

private int CalculateStat(int baseValue, float growth, int level)
{
    if (level <= 1) return baseValue;
    double result = baseValue * System.Math.Pow(growth, level - 1);
    if (result > int.MaxValue) return int.MaxValue; // 오버플로우 방지
    return (int)result;
}

플레이어는 업그레이드 레벨을 골드로 올려 스탯을 성장시키고, 장비 보너스가 추가로 더해집니다. 골드는 DarkNaku.Number 타입으로 대수 표기(1.5K, 2.3M 등)를 지원합니다.

ICombatTarget 인터페이스 기반 전투

적이 플레이어와 동료 모두를 공격할 수 있도록 ICombatTarget 인터페이스를 도입했습니다. PlayerStats와 CompanionController 모두 이 인터페이스를 구현합니다.

public interface ICombatTarget
{
    Transform GetTransform();
    bool IsAlive();
    void TakeDamage(int damage);
    int GetCurrentHealth();
    int GetMaxHealth();
    string GetName();
}

적은 플레이어와 모든 활성 동료 중 가장 가까운 대상을 타겟으로 삼습니다. 타겟 유효성 검사에서 null 체크, GameObject 파괴 여부, IsAlive까지 확인하고 MissingReferenceException도 캐치합니다.

장비 제작 시스템

EquipmentCraftingSystem은 포인트 기반으로 랜덤 장비를 생성합니다. 최대 5포인트를 보유하고 60초마다 1포인트가 회복됩니다. 앱을 종료했다가 돌아오면 오프라인 시간만큼 포인트를 자동 회복합니다.

생성된 장비는 희귀도(일반 50%, 고급 75%, 희귀 90%, 영웅 98%, 전설 2%)에 따라 1~4개의 랜덤 스탯이 부여되고, 희귀도 배율이 스탯에 곱해집니다.

세이브 시스템

GameSaveSystem은 JSON 파일로 버전 관리(saveVersion = 5)하며 저장합니다. 업그레이드 레벨만 저장하고 기본 스탯과 성장 계수는 ScriptableObject에서 불러오는 방식으로, 밸런스 수정 시 기존 세이브에 영향을 주지 않습니다.

30초 간격 자동 저장, 앱 일시정지/종료 시에도 저장을 수행합니다. 장비 제작 포인트는 오프라인 시간을 계산해 복귀 시 자동 회복합니다.

주요 성과

  • Claude Skill 기반 시나리오 자동 생성: nani-scenario-generator 스킬로 자연어 → Naninovel 스크립트 변환 워크플로우 구축
  • 16개 시나리오 스크립트: 3개 캐릭터 × 5개 에피소드 + 프롤로그, 총 39개 선택지 분기
  • Naninovel 커스텀 명령어: 한국어 조사 처리(jong)와 스토리-게임플레이 전환(end) 구현
  • 동료 호감도-스토리 연동: 전투로 호감도를 올리고, 호감도 기준으로 에피소드가 해금되는 루프
  • 지수 성장 스탯 시스템: 방치형 RPG의 장기 진행 밸런스 설계
  • ICombatTarget 인터페이스: 플레이어와 동료가 동시에 공격받는 전투 구조
  • 랜덤 장비 생성: 5단계 희귀도, 포인트 기반 제작, 오프라인 회복
  • 버전 관리 세이브: 밸런스 패치 시 데이터 호환성 유지

결론/학습 내용

프로젝트의 의의

비주얼 노벨 엔진과 방치형 RPG를 통합해 스토리와 전투가 연결되는 게임을 만들었습니다. Claude Code 커스텀 스킬을 개발해 시나리오 작업을 자동화하고, 니지저니·제미나이와 결합한 AI 워크플로우로 콘텐츠 제작 효율을 높였습니다. 동료 호감도 시스템이 스토리 진행의 핵심 동기가 되고, 전투로 얻은 자원으로 성장하는 루프를 구성했습니다.

학습 내용

  • Claude Code Skill 개발: 자연어 → 도메인 특화 스크립트 변환 스킬 설계
  • Naninovel 엔진의 커스텀 명령어와 씬 전환 구조 이해
  • AI 도구 협업 워크플로우: 니지저니(일러스트) + 제미나이(스토리) + Claude Skill(스크립트 변환)
  • Interface 기반 다중 타겟 전투 시스템 설계
  • 지수 함수 스탯 성장 시스템과 오버플로우 방지
  • Observer 패턴(이벤트)을 활용한 시스템 간 느슨한 결합
  • JSON 기반 버전 관리 세이브 시스템 설계