인터랙티브 서사Interactive Narrative

선택 → 결과 → 분기 → 엔딩

무엇인가

당신의 선택이 이야기가 어디로 가고 어떻게 끝나는지를 진정으로 바꿉니다. 인터랙티브 픽션, Life is Strange, Orange Light(Chengguang) 게임이 이 위에 만들어졌습니다.

어떻게 플레이하는가

모든 선택지가 교차로에서 서명하는 기분입니다. 엔딩에서 '다른 길을 갔다면 어땠을까...' 하고 궁금해하고, 실제로 돌아가 그 길을 고릅니다.

왜 배워야 하는가

서사와 게임플레이의 경계입니다. '선택 → 결과 → 분기' 구조가 이야기에 처음으로 '나'의 감각을 줍니다.

코어 루프
선택 → 결과 → 분기 → 엔딩
핵심 추상화
장면 노드 · 분기 선택 · 상태 변수 · 지연된 결과 · 멀티 엔딩

바로 플레이 가능한 데모

인터랙티브 서사 Demo
게임 불러오는 중…
규칙 힌트
서사 제시 → 플레이어 선택 → 상태 변수 갱신 → 다음 분기 노드로 진입 → … → 엔딩 도달 → 리플레이 가능
개념

인터랙티브 서사 패턴은 플레이어의 선택이 이야기의 방향을 진정으로 바꾸는 루프를 중심으로 합니다. 핵심 분기점에서 플레이어는 결정을 내리고, 각 결정은 관찰 가능하거나 추적 가능한 결과를 만들어 서사를 서로 다른 경로와 엔딩으로 분기시킵니다. 본질적으로 이것은 분기 상태 트리입니다. 노드는 장면, 엣지는 선택, 잎은 엔딩입니다. 구현에서 분기 서사는 상태 추적과 조건 시스템에 의존하며, 선택의 모든 결과는 읽을 수 있고 재현 가능해야 합니다.

역할

인터랙티브 서사의 핵심 목적은 플레이어의 결정을 이야기 궤적의 변화에 곧장 매핑해, 게임을 수동적 독서에서 능동적 참여로 바꾸는 것입니다. 상태 추적과 분기 트리로 이를 달성해 플레이어에게 인과감과 통제감을 주고, 몰입과 리플레이 가치를 높입니다.

의의

인터랙티브 서사를 마스터하면 디자이너는 제한된 자원으로 감정적 몰입이 높은 경험을 만들 수 있습니다. 글쓰기와 시스템 설계를 섞고, 각 선택의 설명 가능성과 비용을 강조해 이야기와 게임플레이의 깊은 시너지를 이끌어내며, 현대 서사 게임의 핵심 경쟁력입니다.

핵심 게임플레이 시스템 분석

코어 루프 흐름도
코어 루프 흐름도
3.1 해결하는 문제
  • 선택에 진짜 결과를 보장해 '뭘 골라도 똑같은' 가짜 분기를 피합니다
  • 상태 변수로 플레이어가 한 일을 기억해 결과가 지연될 수 있게 합니다
  • 엔딩으로 플레이어가 다시 플레이할 이유를 줍니다
3.2 학습 목표
  • 분기 트리의 최소 구조(노드/엣지/잎)를 이해한다
  • 상태 변수 설계를 마스터한다: 무엇을 기억하고 언제 실현할지
  • 3~5개의 구분되는 엔딩이 있는 최소 플레이 가능 서사 루프를 설계할 수 있다
3.3 최소 규칙 집합
  • 선택: 각 노드는 구분되는 선택지 2~4개를 제공합니다
  • 결과: 선택은 반드시 상태를 바꿉니다(즉시 또는 지연되어 보이게)
  • 엔딩: 상태는 구분 가능한 최종 결과로 수렴합니다(최소 2개)

흔한 조합과 변형

서사 + 전략
전투 결과가 서사 분기에 영향을 줍니다(턴제 전술 서사)
서사 + 경영
경영 성과가 서로 다른 서사 경로를 발동합니다
서사 + 퍼즐
퍼즐 성공이 숨겨진 이야기를 해금합니다
고급 설계와 알고리즘 (접힘)클릭하여 펼치기
설계 시 주의사항
  • 가짜 분기(기록 없이 수렴하는 다른 경로)는 플레이어 신뢰를 즉시 침식합니다
  • 분기 폭발은 스크립트를 유지보수 불가능하게 합니다. 수렴 지점으로 억제하세요
  • 모든 결과가 즉각적이면 서스펜스가 없고, 모두 지연되면 피드백이 없습니다. 섞으세요
알고리즘 예시
  • 상태 머신 / 방향 그래프: 서사 노드와 분기 조건
  • 변수 시스템: 플래그, 호감도, 단서 수집
  • 엔딩 판정: 다중 조건 임계값과 우선순위
핵심 코드(State Flags → Ending Resolution)
ts· 7줄
const ENDINGS: [string, (f: Flags) => boolean][] = [
  ["Bonfire Night Talk", (f) => f.kind && f.courage],  // ordered by priority, highest first
  ["A Bowl of Hot Soup", (f) => !!f.kind],
  ["Shadow Under the Lamp", (f) => f.courage && !f.warm],
  ["Safe and Sound", (f) => !!f.cautious],
];
const ending = ENDINGS.find(([, cond]) => cond(flags))?.[0] ?? "Rain Lets Up";