C# 에서 하드코딩으로 의사 결정 트리를 구성해보았습니다.
이 구조는 복잡한 if-else 대신, 조건과 결과를 분기 노드(DecisionNode) 형태로 깔끔하게 구성할 수 있어 가독성과 유지보수 측면에서 유리합니다.
퀘스트 트리
플레이어의 상태에 따라 다음과 같은 분기를 수행하는 퀘스트 결정 트리를 만듭니다
[레벨 ≥ 15?]
├─ 예 → [고대 열쇠 + 왕의 인장 있음?]
│ ├─ 예 → [튜토리얼 + 사이드미션1 완료?]
│ │ ├─ 예 → 🎁 특별 퀘스트 지급: 왕의 시험
│ │ └─ 아니오 → 📌 보조 퀘스트들을 먼저 완료하세요.
│ └─ 아니오 → 🔑 필요한 아이템이 없습니다.
└─ 아니오 → [VIP 토큰 있음?]
├─ 예 → 🎟️ 임시 퀘스트 개방
└─ 아니오 → [숨겨진 스크롤 있음?]
├─ 예 → 🌀 보너스 퀘스트 오픈
└─ 아니오 → ⛔ 레벨이 부족합니다.
코드 구성 요약
• DecisionNode : 조건 분기 및 결과 실행을 담는 노드
• Player : 레벨, 인벤토리, 완료 퀘스트 목록 등 상태 정보 보유
• QuestSystem : 트리 구성과 평가 수행
• Main : 테스트용 예시 플레이어 상태 설정
예시 코드
// QuestDecisionNode.cs
using System;
using System.Collections.Generic;
class DecisionNode
{
public Func<bool> Condition = null;
public Action Result = null;
public DecisionNode YesNode = null;
public DecisionNode NoNode = null;
}
class Player
{
public int Level;
public List<string> Inventory = new List<string>();
public List<string> CompletedQuests = new List<string>();
public bool HasItem(string item) => Inventory.Contains(item);
public bool HasItems(params string[] items)
{
foreach (var item in items)
if (!Inventory.Contains(item)) return false;
return true;
}
public bool HasCompleted(string quest) => CompletedQuests.Contains(quest);
}
class QuestSystem
{
private Player player;
public QuestSystem(Player player)
{
this.player = player;
}
public DecisionNode BuildQuestDecisionTree()
{
return new DecisionNode
{
Condition = () => player.Level >= 15,
YesNode = new DecisionNode
{
Condition = () => player.HasItems("Ancient Key", "Royal Seal"),
YesNode = new DecisionNode
{
Condition = () => player.HasCompleted("Tutorial")
&& player.HasCompleted("SideMission1"),
YesNode = new DecisionNode
{
Condition = null,
Result = () => Console.WriteLine
("🎁특별 퀘스트 지급: 왕의 시험")
},
NoNode = new DecisionNode
{
Condition = null,
Result = () => Console.WriteLine
("📌보조 퀘스트들을 먼저 완료하세요.")
}
},
NoNode = new DecisionNode
{
Condition = null,
Result = () => Console.WriteLine
("🔑 필요한 아이템이 없습니다.")
}
},
NoNode = new DecisionNode
{
Condition = () => player.HasItem("VIP Token"),
YesNode = new DecisionNode
{
Condition = null,
Result = () => Console.WriteLine
("🎟️ VIP 토큰 보유자: 임시 퀘스트 개방")
},
NoNode = new DecisionNode
{
Condition = () => player.HasItem("Hidden Scroll"),
YesNode = new DecisionNode
{
Condition = null,
Result = () => Console.WriteLine
("🌀 숨겨진 스크롤로 보너스 퀘스트 오픈")
},
NoNode = new DecisionNode
{
Condition = null,
Result = () => Console.WriteLine
("⛔ 레벨이 부족합니다.")
}
}
}
};
}
public void EvaluateTree(DecisionNode node)
{
if (node == null) return;
if (node.Condition != null)
{
if (node.Condition())
EvaluateTree(node.YesNode);
else
EvaluateTree(node.NoNode);
}
else
{
node.Result?.Invoke();
}
}
}
class Program
{
static void Main()
{
var player = new Player
{
Level = 12,
Inventory = new List<string> { "Ancient Key", "Hidden Scroll" },
CompletedQuests = new List<string> { "Tutorial" }
};
var questSystem = new QuestSystem(player);
var root = questSystem.BuildQuestDecisionTree();
questSystem.EvaluateTree(root);
}
}
하드코딩이지만 구조화된 이점
• if 조건문 없이, 조건 분기를 읽기 좋은 트리 구조로 표현 가능
• 각 조건에 대한 Condition = () => ... 람다식이 직관적
• Condition = null 을 명시하여 조건이 없는 마지막 노드임을 강조 (사실 생략 가능하지만 가독성을 위해 남겨둠)
• 트리 흐름이 깔끔하게 보이고, 테스트하기도 쉬움
마무리
JSON이나 ScriptableObject로 확장하지 않고도, 하드코딩 상태에서도 의사결정 로직을 트리로 구성하는 방식은 기획 또는 로직이 일정 수준 복잡한 게임에도 충분히 실용적으로 사용할 수 있습니다.
언젠가 이 코드를 기반으로 JSON 자동 구성까지 확장해도 좋겠지만, 지금 단계에서는 이 방식만으로도 꽤 멋지고, 실제 구현 가능한 느낌이 납니다.
'C#' 카테고리의 다른 글
정렬 알고리즘의 기초 1편 : 버블 정렬 (Bubble Sort) (0) | 2025.05.04 |
---|---|
C# 에서 temp 없이 값 교환하는 법, 이렇게 간단해도 될까요? (0) | 2025.05.04 |
Dictionary 를 배열처럼 접근할 때 주의사항 (0) | 2025.05.02 |
Parsing과 TryParse() 계열 함수들 (0) | 2025.04.30 |
이벤트 관리를 위한 설계 제안 (0) | 2025.04.30 |