공용 스크립트 제작기🎈11 - 치트 시스템 편

공용 스크립트 제작기🎈11 - 치트 시스템 편

Unity 프로젝트의 공용으로 사용할 수 있는 스크립트를 제작하면서 어떻게 만들었는지, 어떤 점에 신경썼는지 설명하는 글입니다.

게임 개발 중 테스트를 위해 치트 기능이 필요할 때가 많다. 매번 코드를 수정하고 테스트하기보다는 콘솔에서 명령어만 입력하면 바로 실행되는 시스템이 있는 것이 좋다. 그래서 에디터 전용 치트 콘솔 시스템을 만들어봤다.

CSV에 치트를 정의하고, ICheat 인터페이스를 구현한 클래스만 만들면 자동으로 인식되어 콘솔에서 실행할 수 있다.

💡예시

치트 시스템의 사용 예시를 먼저 살펴보자.

CSV에 치트 정의

Assets/Data/CSV/CheatData.csv 파일에 치트 정보를 작성한다.

ID,Description,Parameters
AddGold,골드를 추가합니다,amount:int
AddItem,아이템을 추가합니다,itemId:string|count:int
SetLevel,레벨을 설정합니다,level:int
GodMode,무적 모드를 토글합니다,
Teleport,지정 좌표로 이동합니다,x:float|y:float|z:float

ICheat 구현 클래스 작성

중요: 클래스명은 CSV의 ID와 정확히 일치해야 한다!

#if UNITY_EDITOR
using Core.Utilities;

namespace Core.Cheat.Commands
{
    /// <summary>
    /// 골드 추가 치트
    /// 사용법: AddGold [amount]
    /// </summary>
    public class AddGold : ICheat
    {
        public void Execute(string[] args)
        {
            if (args.Length < 1)
            {
                GameLogger.Log("[Cheat] 사용법: AddGold [amount]");
                return;
            }

            if (int.TryParse(args[0], out int amount))
            {
                // 골드 추가 로직
                GameContext.Instance.PlayerData.Gold += amount;
                GameLogger.Log($"[Cheat] 골드 {amount} 추가됨");
            }
            else
            {
                GameLogger.LogWarning($"[Cheat] 잘못된 수량: {args[0]}");
            }
        }
    }
}
#endif

치트 실행

게임 실행 중 ` (백틱) 키를 눌러 콘솔을 열고 명령어를 입력한다.

> AddGold 1000
> AddItem "Legendary Sword" 5
> SetLevel 99
> GodMode
> Teleport 10 0 20

코드에서 치트 실행

#if UNITY_EDITOR
// 프로그래밍 방식으로 치트 실행
CheatManager.Instance.ExecuteCheat("AddGold 1000");
CheatManager.Instance.ExecuteCheat("AddItem \"Legendary Sword\" 5");
#endif

📊 클래스 다이어그램

CSV 데이터와 리플렉션 기반 자동 탐색을 결합한 구조다.

CheatManager (LazySingleton)
├── cheatTypes: Dictionary<string, Type>  // ID → ICheat 구현 타입
├── cheatDataMap: Dictionary<string, CheatData>  // ID → CSV 데이터
└── allCheatData: List<CheatData>  // 전체 목록

EditorCheatConsole (MonoBehaviour)
├── 입력 UI 렌더링
├── 자동완성 처리
├── 히스토리 관리
└── 키보드 입력 처리

CheatInputParser (static)
└── 입력 문자열 파싱

CheatExtensions (static)
├── GetUsage(): 사용법 문자열 생성
├── GetParameterCount(): 매개변수 개수
└── GetParameterInfoList(): 매개변수 정보 목록

ICheat (interface)
└── Execute(string[] args): 치트 실행

CheatData (CSV 자동 생성)
├── ID: 치트 명령어
├── Description: 설명
└── Parameters: 매개변수 정의

📁 시스템 아키텍처

핵심 컴포넌트

CheatManager (LazySingleton)
  • 역할: 치트 시스템의 중앙 관리자
  • 주요 기능:
    • Assembly에서 ICheat 구현 클래스 자동 탐색 (리플렉션)
    • CSVManager에서 CheatData 로드
    • 치트 실행 (ExecuteCheat)
    • 치트 검색 (GetMatchingCheats, GetExactMatch)
    • 치트 존재 여부 확인 (HasCheatType, HasCheatData)
  • 초기화 순서:
    1. FindAllCheatTypes(): 모든 ICheat 구현체 탐색
    2. LoadCheatData(): CSV 데이터 로드
EditorCheatConsole (MonoBehaviour)
  • 역할: 치트 입력 UI 및 사용자 인터랙션 처리
  • 주요 기능:
    • ` 키로 콘솔 열기 / ESC로 닫기
    • 자동완성 목록 표시 및 선택
    • 파라미터 가이드 표시
    • 명령어 히스토리 (Ctrl+↑/↓ 키)
    • Tab 자동완성
  • 키보드 바인딩:
    • ` (백틱): 콘솔 열기
    • ESC: 콘솔 닫기
    • Tab: 자동완성
    • ↑/↓: 자동완성 목록 탐색
    • Ctrl+↑/↓: 히스토리 탐색
    • Enter: 명령어 실행
CheatInputParser (static)
  • 역할: 입력 문자열을 치트 ID와 매개변수 배열로 파싱
  • 주요 기능:
    • TryParse(): 문자열 → (ID, args[]) 변환
    • Tokenize(): 큰따옴표 처리 (공백 포함 문자열 지원)
  • 동작 예시:
    • AddItem "Legendary Sword" 5 → ID: “AddItem”, args: [“Legendary Sword”, “5”]
CheatExtensions (static)
  • 역할: CheatData에 대한 확장 메서드 제공
  • 주요 기능:
    • GetUsage(): 사용법 문자열 생성 (예: “AddItem [itemId] [count]”)
    • GetParameterCount(): 매개변수 개수 반환
    • GetParameterInfoList(): 매개변수 정보 목록 (이름, 타입)
  • Parameters 형식: name:type|name:type|...
ICheat (interface)
  • 역할: 모든 치트 명령어의 공통 인터페이스
  • 메서드: void Execute(string[] args)
  • 규칙: 클래스명이 CSV의 ID와 정확히 일치해야 함
CheatData (CSV 자동 생성)
  • 역할: CSV에서 자동 생성된 치트 정의 데이터
  • 필드:
    • ID: 치트 명령어 (= ICheat 구현 클래스명)
    • Description: 치트 설명
    • Parameters: 매개변수 정의 (name:type|name:type|...)

🔄 데이터 흐름

초기화 단계
  1. 게임 시작 → CSVManager.Initialize() 호출
  2. CheatManager.Instance 최초 접근 시 Initialize() 호출
  3. [FindAllCheatTypes] 리플렉션으로 ICheat 구현체 탐색
    • Dictionary<string, Type>에 (클래스명 → 타입) 매핑
  4. [LoadCheatData] CSV 데이터 로드
    • CSVManager.GetTable() 호출
    • Dictionary<string, CheatData>에 (ID → 데이터) 매핑
  5. [완료] CheatManager 사용 가능
콘솔 열기/닫기 프로세스
  1. [사용자 입력] ` (백틱) 키 입력
  2. [Update] InputManager.IsOpenCheatPressed() 감지
  3. [Show] 콘솔 표시
  4. [사용자 입력] ESC 키 입력
  5. [Hide] 콘솔 숨김
치트 실행 프로세스
  1. [사용자 입력] 콘솔에 명령어 입력 (예: AddGold 1000)
  2. [Enter 키] ExecuteCommand() 호출
  3. [CheatManager.ExecuteCheat] 치트 실행 시작
    • CheatInputParser.TryParse()로 파싱
    • ID: “AddGold”, args: “1000”
  4. [치트 타입 찾기] cheatTypes “AddGold”에서 Type 조회
  5. [인스턴스 생성] Activator.CreateInstance(cheatType)
  6. [Execute] ICheat.Execute(args) 호출
  7. [완료] 로그 출력 및 결과 표시

🎯 신경 쓴 부분

CSV 기반 치트 정의

치트의 메타데이터(설명, 파라미터)를 CSV로 분리하여 코드 수정 없이 관리할 수 있다.

ID,Description,Parameters
AddGold,골드를 추가합니다,amount:int
AddItem,아이템을 추가합니다,itemId:string|count:int
  • 기획자도 치트 정의 가능 (코드 수정 불필요)
  • 파라미터 형식: name:type|name:type|...
  • 지원 타입: int, float, string
큰따옴표로 공백 포함 문자열 지원

CheatInputParser가 큰따옴표 내부를 하나의 토큰으로 처리한다.

private static List<string> Tokenize(string input)
{
    bool inQuotes = false;
    for (int i = 0; i < input.Length; i++)
    {
        char c = input[i];
        if (c == '"')
        {
            inQuotes = !inQuotes;
        }
        else if (c == ' ' && !inQuotes)
        {
            // 토큰 구분
        }
    }
}
  • AddItem "Legendary Sword" 5 → “AddItem”, “Legendary Sword”, “5”
  • 아이템명, 적 이름 등 공백이 포함된 문자열 처리 가능
실시간 파라미터 가이드

파라미터 입력 시 현재 어떤 파라미터를 입력 중인지 시각적으로 표시한다.

AddItem [itemId] [count]
아이템을 추가합니다

필요한 파라미터:
  → itemId (string)  ← 현재 입력 중
    count (int)
  • 현재 입력 중인 파라미터 하이라이트 (→)
  • 입력 완료된 파라미터 체크 표시 (✓)
  • 타입 정보 함께 표시
명령어 히스토리

이전에 실행한 명령어를 Ctrl+↑/↓ 키로 다시 불러올 수 있다.

private List<string> commandHistory = new List<string>();
private int historyIndex = -1;

private void NavigateHistory(int direction)
{
    historyIndex += direction;
    inputText = commandHistory[commandHistory.Count - 1 - historyIndex];
}
  • 세션 동안 명령어 히스토리 유지
  • 반복 테스트 시 편리

결론

에디터 전용 치트 콘솔 시스템을 만들어봤다. 자동완성, 파라미터 가이드, 히스토리 등 편의 기능도 추가해서 테스트할 때 꽤 유용하게 쓸 수 있을 것 같다. 좀 더 추가하고 싶은 기능들이 있지만 일단은 여기까지.


© 2022. All rights reserved.