프로젝트 개요
스테이지를 반복(루프)하며 정상/이상현상을 판별하는 1인칭 공포 게임입니다. 핵심 로직은 C++ 100%로 구현하고, Blueprint는 데이터 할당·연출 바인딩에만 사용합니다. 각 섹션의 클래스 다이어그램에서 파일 배지를 클릭하면 실제 C++ 코드를 팝업으로 확인할 수 있습니다.
모듈러 아키텍처
WorldSubsystem기반 시스템 분리 — 스테이지/이상현상/개발도구를 독립 서브시스템으로 격리GameMode는 판정만,PlayerController는 입력/연출만 — 단일 책임 원칙GameInstance를 데이터 허브로 활용해 레벨 전환 간 상태 유지
데이터 드리븐 설계
- 8개 DataTable로 밸런싱 외부화 —
DT_Anomalies,DT_StageConfig,DT_InterferenceRules등 - Google Sheets → CSV → UE DataTable 임포트 파이프라인으로 기획자 협업
FTableRowBase기반 Row Struct로 타입 안전한 데이터 접근
이상현상 엔진
EAnomalyType17종 — Transform, Material, Visibility, Gimmick 등 다형성 처리FGameplayTag타겟팅으로 월드의UAnomalyTargetComponent탐색/적용- 경험 기록(정답/오답)으로 중복 제거 + 오답 우선 재등장 밸런싱
2단계 판정 구조
- 문(Exit)으로 “정상” 판정, 거울로 “이상현상” 판정 — 진입점 분리
- 판정 후 연출 완료 대기 → 완료 콜백 → 결과 처리로 연출-로직 분리
- 델리게이트 브로드캐스트로 HUD/PC에 결과를 느슨하게 전파
GameplayTag 활용
- 이상현상 타겟, 스폰 포인트, 복도 문 역할 전환 대상을 일관된 태그로 식별
- 레벨 하드코딩 의존도를 제거하고 데이터 기반 유연한 매핑
상호작용 · 연출
- 카메라 Ray Trace 기반
IInteractable감지 + CustomDepth 하이라이트 - LevelSequence로 상호작용 연출, 완료 콜백으로 판정 흐름 연결
핵심 시스템
각 시스템의 핵심 설계와 담당 클래스를 요약합니다. 항목을 클릭하면 해당 섹션으로 이동합니다.
클래스 다이어그램 · 코드
ATOIPlayerController ATOICharacter UTOIDeveloperSubsystem
전체 아키텍처를 계층별(Framework/Subsystem/Component/Interaction) 다이어그램으로 시각화합니다. 클래스 배지를 클릭하면 C++ 코드 팝업을 확인할 수 있습니다.
이상현상 시스템
UAnomalyManager UAnomalyTargetComponent
DataTable 기반 이상현상 풀에서 스테이지 조건·경험 기록으로 선정하고, GameplayTag로 월드 타겟을 찾아 17종 타입별 적용/복구를 수행합니다.
스테이지 관리 · 판정
UStageManager ATOIGameMode
스테이지 로드/리셋/맵 전환은 StageManager가 실행하고, 판정/결과 분기는 GameMode가 심판으로서 담당합니다. 2단계 판정(연출 대기 → 결과)으로 연출과 로직을 분리합니다.
데이터 파이프라인
UTOIGameInstance TOIDataTypes.h
8개 DataTable을 GameInstance에서 중앙 보관하고, Google Sheets DB → CSV 임포트 → UE DataTable 워크플로우로 기획자와 협업합니다.
자아 간섭 시스템
FInterferenceRuleData EInterferenceCondition
루프 반복·정체 시간·상호작용 취소 등 조건으로 간섭 게이지를 누적하고, 메타/회피/강박 자아가 우선순위에 따라 개입합니다. (데이터 스키마 완료, 서브시스템 구현 예정)
클래스 다이어그램
클래스를 클릭하여 상세 코드를 확인하세요.
Door/Mirror 상태 초기화
Entrance/Exit 전환
선택·적용 위임
Stage/Map 저장
스테이지 시작 통보
OnJudgementDirectionComplete()
→ OnStageClear()
→ LoadStage(nextStage)
OnJudgementDirectionComplete()
→ OnStageFail()
→ LoadStage(0) 루프 리셋
이상현상 시스템
DataTable + EAnomalyType enum 디스패치로 다양한 이상현상을 관리하고,
UAnomalyTargetComponent 계층을 통해 월드 액터에 적용·복구하는 핵심 시스템입니다.
DataTable 기반 이상현상 풀
DT_Anomalies DataTable에 FAnomalyData Row로 전체 이상현상 풀을 관리합니다.
EAnomalyType enum으로 적용 방식을 분기하고, FGameplayTag로 타겟 액터를 식별합니다.
AnomalyTargetComponent 계층구조
각 이상현상은 UAnomalyTargetComponent 기반의 컴포넌트로 개발됩니다.
범용적인 설정(위치·머티리얼·스케일·사운드 등)은 부모 컴포넌트가 일괄 처리하고,
디테일한 동작이 필요한 이상현상은 자식 클래스로 상속받아 BlueprintNativeEvent를 오버라이드하여 고유 로직을 구현합니다.
컴포넌트 기반 확장 구조
각 이상현상은 액터에 컴포넌트로 부착하는 방식으로 개발됩니다.
부모 컴포넌트(UAnomalyTargetComponent)는 EAnomalyType에 따른 범용 Apply/Rollback을 이미 처리하므로,
대부분의 이상현상은 DataTable 파라미터 조정만으로 동작합니다.
천장 하강·점진적 스케일 변형처럼 Tick 기반 로직이나 전용 델리게이트가 필요한 경우에만
자식 클래스를 생성해 OnApplyGimmickAnomaly / OnRollbackGimmick을 오버라이드합니다.
에디터 화면 · 디버그 도구
이상현상 데이터는 DataTable(DT_Anomalies)과 DataAsset으로 에디터에서 관리됩니다.
디버깅 시에는 TOI 디버그 설정에서 특정 이상현상을 테이블에서 조회하여 강제 적용할 수 있어,
반복 테스트 없이도 원하는 이상현상을 즉시 확인할 수 있습니다.
디버그를 통한 이상현상 강제 적용 흐름
에디터의 "프로젝트 설정 → TOI 디버그" 메뉴에서 아래 과정으로 특정 이상현상을 강제할 수 있습니다. 이를 통해 반복 플레이 없이 특정 이상현상의 동작을 빠르게 검증할 수 있습니다.
"특정 이상현상 강제"로 설정
DT_Anomalies 테이블의 Row를 선택 (예: AN_039_Locker_Shrink)
UAnomalyManager가 확률 계산을 건너뛰고, 선택된 이상현상을 즉시 적용
스테이지 관리 시스템
UStageManager WorldSubsystem이 스테이지 로드·초기화·맵 전환을 책임지고, 판정 로직은
ATOIGameMode가 분리하여 담당합니다.
UStageManager (WorldSubsystem)
스테이지 로드/리셋, 문 역할 전환, 맵 전환을 "실행"하는 서브시스템입니다.
FStageConfigData DataTable에서 스테이지별 이상현상 확률·연속 정상 제한 등을 읽고,
이상현상 선택·적용은 UAnomalyManager에 위임합니다.
FStageConfigData (DataTable Row)
DT_StageConfig DataTable의 Row 구조체로 스테이지별 동작 규칙을 정의합니다.
Stage 0은 항상 정상(비교 기준), Stage 1~7은 이상/정상 확률·경비원 기믹 등이 적용됩니다.
ATOIGameMode (심판 역할)
GameMode는 "판단/분기"만 수행하고, 상태 변경(스테이지·맵 전환)은 StageManager에 위임합니다.
판정 결과는 델리게이트로 브로드캐스트되어 PlayerController/HUD가 연출에 반응합니다.
역할 분리 설계
GameMode(심판)는 판단만 하고, StageManager(실행자)가 환경 구성·상태 갱신을 수행합니다.
PlayerController(연출)는 델리게이트를 바인딩해 페이드·사운드·텔레포트를 처리합니다.
스테이지 라이프사이클
LoadStage() 호출 시 5단계의 순차 처리가 이루어집니다.
각 단계는 별도 시스템에 위임되어 단일 책임 원칙을 준수합니다.
Door/Mirror 리셋
Exit ↔ Entrance
선택·적용 위임
MapID 업데이트
시작 알림
이벤트별 분기 테이블
GameMode가 판단한 결과에 따라 StageManager가 실행하는 흐름입니다. Map 1의 Stage 7 클리어 시 Map 2로 전환되며, Map 2의 Stage 7 클리어 시 엔딩에 진입합니다.
| 이벤트 | GameMode (판단) | StageManager (실행) |
|---|---|---|
| 게임 시작 | StartPlay() |
LoadStage(0, 0, MapID, false) |
| 정답 (Stage 0~6) | OnStageClear() → OnStageClearEvent |
LoadStage(Loop, NextStage, MapID) |
| 정답 (Stage 7, Map 1) | OnStageClear() → OnMapTransition |
TransitionToNextMap(2) |
| 정답 (Stage 7, Map 2) | OnStageClear() → OnGameComplete |
엔딩 진입 |
| 오답 | OnStageFail(false) → OnStageFailEvent |
LoadStage(NewLoop, 0, MapID) |
| 즉사 | HandleInstantDeath() → OnStageFail(true) |
LoadStage(NewLoop, 0, MapID) |
문 역할 전환 (ConfigureDoorRoles)
복도 문은 GameplayTag(CorridorTag)로 식별되며,
맵 ID에 따라 Exit(판별용)/Entrance(일반) 역할이 런타임에 전환됩니다.
에디터 화면 · 디버그 도구
스테이지 설정 데이터는 DT_StageConfig DataTable과 Google Sheets DB로 관리됩니다.
디버깅 시에는 TOI 디버그 설정에서 강제 맵 ID를 지정해 특정 맵의 스테이지를 즉시 테스트할 수 있습니다.
스테이지 디버그 워크플로우
에디터의 "프로젝트 설정 → TOI 디버그" 메뉴의 스테이지 디버그 카테고리에서 맵 전환 없이 특정 맵의 스테이지를 즉시 테스트할 수 있습니다.
1 또는 2로 설정 (0 = 사용 안 함)
StageManager가 DT_StageConfig에서 해당 맵의 스테이지 설정을 로드
데이터 파이프라인
UTOIGameInstance가 보유한 8개 DataTable에서 Row Struct를 읽어
WorldSubsystem → ActorComponent로 전달되는 흐름입니다.
Google Sheets에서 기획 DB를 관리하고 CSV 임포트로 에디터에 반영합니다.
데이터 흐름 5단계
UTOIGameInstance가 레벨 전환에도 유지되는 DataTable 참조 허브 역할을 하며,
WorldSubsystem이 GetGameInstance()로 접근해 Row를 조회·필터링하고, ActorComponent에 적용합니다.
결과는 Delegate로 브로드캐스트되어 연출 시스템이 느슨하게 반응합니다.
에디터에서 편집
경험 기록 보관
Filter · Apply
Apply · Rollback
이벤트 발행
UTOIGameInstance (데이터 허브)
레벨 전환에도 유지되는 GameInstance에서 8개 DataTable 참조와
루프/스테이지 상태, 이상현상 경험 기록을 중앙 보관합니다.
DataTable 에셋은 C++에서 로드하지 않고 BP_TOIGameInstance에서 할당합니다.
Subsystem의 데이터 로딩
UAnomalyManager는 Initialize 시점에 GameInstance에서 DataTable 참조를 가져와
내부 포인터에 캐싱합니다. 이후 SelectAnomalyForStage() 호출 시 캐싱된 테이블에서 Row를 조회합니다.
Google Sheets → UE 워크플로우
기획 팀이 Google Sheets에서 DB를 편집하고, CSV로 내보내 UE 에디터에서 DataTable Reimport로 반영합니다.
Row Struct의 필드명과 CSV 열 헤더가 1:1 대응하도록 설계되어 있습니다.
밸런싱 조정
헤더 = 필드명
CSV 임포트
에셋 참조
Row 조회
8개 DataTable 총괄
TOIDataTypes.h에 Row Struct가 정의되어 있으며, 각 DataTable은 UTOIGameInstance에서
EditDefaultsOnly 프로퍼티로 참조됩니다. 핵심 시스템(이상현상·스테이지·간섭)은 런타임에 실시간 조회되고,
연출/환경 DataTable은 시스템 구현에 맞춰 순차적으로 연동됩니다.
핵심 시스템 DataTable
DT_Anomalies
이상현상 풀 정의 — 타입(EAnomalyType 17종), 맵 ID, FGameplayTag 타겟, 발생 확률, 스케일/시간/강도 파라미터, 즉사 여부, 캠코더 전용 플래그
DT_StageConfig
스테이지별 규칙 — 이상현상 확률(80%), 연속 정상 제한(2회), 경비원 스폰 확률/딜레이, 자아 간섭 허용 여부
DT_InterferenceRules
자아 간섭 규칙 — RuleCategory(GaugeIncrease/EgoActivation), 우선순위, 조건 지표(EInterferenceCondition 12종), 게이지 변동량, 맵 간 지속성
DT_Interactables
상호작용 오브젝트 정의 — 이름, 타입(EInteractType: Open/Inspect/Pickup/Sit), UI 텍스트 키(로컬라이제이션 연동)
연출 · 환경 · 공통 DataTable
DT_Dialog
대사 데이터 — 원본 문자열, 화자 ID(EDialogSpeaker: System/Ego_Avoid/Ego_Meta/Doctor/NPC 등 8종), 표시 지속 시간
DT_SoundList
사운드 에셋 관리 — 타입(ESoundType: BGM/SFX/Ambience/Voice/UI), FSoftObjectPath 경로, 볼륨, 루프 여부, 동시 재생 그룹
DT_Environment
환경 연출 설정 — 안개 밀도, 조명 강도 배율, Post Process 그레인, 환경 사운드 키
DT_Localization
UI 텍스트 로컬라이제이션 — 원본 문자열, 카테고리(ELocalizationCategory: Common/MainMenu/HUD/Interaction 등 8종), 주석
TOIDataTypes.h — Row Struct 정의
모든 DataTable Row Struct는 TOIDataTypes.h 단일 파일에 정의됩니다.
FAnomalyData와 FStageConfigData는 각각 이상현상/스테이지 섹션에서 상세히 다루었으므로,
여기서는 나머지 핵심 구조체를 소개합니다.
FInterferenceRuleData
자아 간섭 규칙 Row — GaugeIncrease(수치 증가)와 EgoActivation(자아 개입 유도) 두 카테고리로 나뉘며,
조건 지표 12종과 우선순위로 어떤 자아가 개입할지 결정합니다.
FInteractableData
상호작용 오브젝트 Row — 문/거울/아이템 등 상호작용 가능 오브젝트의 이름, 타입, UI 텍스트 키를 정의합니다.
InteractTextKey는 DT_Localization과 연동되어 다국어 지원이 가능합니다.
FDialogData · FSoundListData · FEnvironmentData
연출/환경 관련 Row Struct입니다. 대사는 화자별 자아 구분, 사운드는 FSoftObjectPath 비동기 로딩,
환경은 스테이지별 분위기 조절을 지원합니다.
에디터 화면 · Google Sheets DB
각 DataTable은 UE 에디터에서 직접 편집하거나, Google Sheets DB에서 관리 후 CSV 임포트로 반영합니다. 아래는 리소스가 남아 있는 DataTable의 에디터 화면과 Google Sheets DB 화면입니다.
DT_Anomalies — 이상현상 데이터
DT_StageConfig — 스테이지 설정
DT_InterferenceRules — 자아 간섭 규칙
DT_Interactables — 상호작용 오브젝트
리소스 미첨부 DataTable (2026.03 개발 종료 기준)
아래 DataTable들은 Row Struct 정의(TOIDataTypes.h)와 GameInstance 참조까지 끝냈습니다.
다만 해당 시스템을 구현하기 전에 개발이 종료되어 에디터 화면과 Google Sheets DB 리소스는 남기지 못했습니다.
FDialogData) — 자아 간섭 대사 · NPC 대화 시스템과 함께 채울 계획이었던 테이블
FSoundListData) — 동적 BGM/SFX 재생 시스템과 함께 사운드 에셋을 등록할 예정이었던 테이블
FEnvironmentData) — 스테이지별 환경 연출(안개 · 조명 · Post Process) 시스템과 함께 채울 계획이었던 테이블
FLocalizationData) — UI 시스템을 마친 뒤 다국어 텍스트를 넣을 계획이었던 테이블
자아 간섭 시스템
루프가 반복될수록 플레이어의 플레이를 방해하는 간섭 시스템입니다.
메타·회피·강박 세 유형의 자아가 플레이 패턴을 추적하여 개입하며,
데이터 스키마(FInterferenceRuleData, EInterferenceCondition 12종)와
DT_InterferenceRules 테이블 구조가 설계 완료된 상태입니다.
간섭 게이지 시스템
플레이어의 루프 횟수, 스테이지 이동, 특정 오브젝트 상호작용, 정체 시간 등을 누적하여 간섭 게이지가 100 이상 도달하면 다음 스테이지에서 간섭이 발동합니다.
데이터 스키마 (구현 완료)
TOIDataTypes.h에 Row Struct와 Enum이 정의되어 있으며,
UTOIGameInstance에서 InterferenceRuleDataTable을 참조합니다.
FStageConfigData의 bAllowInterference 플래그로 스테이지별 허용 여부를 제어하고,
TOIPlayerController에 InvertedMappingContext 및 SetInputContext()가 준비되어
강박 간섭(조작 반전)의 입력 연동 훅이 마련된 상태입니다.
메타 간섭 Priority 1
ESC 강탈 연출(강제 ESC → 깨진 UI), 자막 UI 깨짐, 스팀 업적으로 말 걸기, Windows 사용자 이름/스팀 닉네임 호출, 가짜 OBS·메모장 실행, 실제 PC 시간 읊기
회피 간섭 Priority 2
판단 직전 혼란 자막 3종 ("이거 맞는 판단일까?", "이상한 건 딱히 없어 보이는데", "아까 그거 다르지 않았나?"), NPC '안경녀' 근처 통과 시 자아 발언
강박 간섭 Priority 3
가짜 종료 팝업 + 자아 반응, 이동 조작 반전(2초, SetInputContext 연동), 캠코더 강제 사용 + 노이즈/프레임 드랍
2026.03 개발 종료 시점 정리
스테이지 시스템, 이상현상 프레임워크, 상호작용, Map 전환, UI 구조, 플레이어 기본 로직 같은 핵심 시스템은 구현을 마쳤습니다. 2026년 3월에 프로젝트를 종료하면서 손대지 못한 범위는 아래와 같고, 프로그래밍 구현과 에디터 콘텐츠 작업으로 나뉩니다. 특히 개별 이상현상 세팅, 비주얼 연출, 플레이어 모션처럼 에디터에서 직접 만들어야 하는 콘텐츠가 많이 남은 상태였습니다.
프로그래밍 구현
자아 간섭 서브시스템
데이터 스키마(FInterferenceRuleData, EInterferenceCondition 12종) 완료 →
WorldSubsystem으로 게이지 누적·임계치 판정·자아 우선순위 평가·발동 로직 구현,
메타·회피·강박 각 자아별 연출 트리거 연결
캠코더 시스템
Map 2 전용 이중 시각 레이어(육안/캠코더), F키 토글, Stage당 횟수·시간 제한,
bCamcorder 이상현상 연동, 캠코더 전용 텍스처·텍스트 렌더링
NPC 시스템
학생 NPC 클래스(표정·자세 이상현상 프리셋 연동), 경비원 NPC AI(20% 확률 스폰, 복도 순찰, 감지→손전등 OFF 판정→즉사 기믹)
대사 · 다이얼로그 시스템
FDialogData/EDialogSpeaker 스키마 완료 → 자아 간섭 대사, NPC 대화,
플레이어 독백 UI 재생 로직 및 자막 위젯 구현
환경 연출 매니저
FEnvironmentData 스키마(FogDensity, LightIntensityMult, PostProcess_Grain, AmbienceSound) 완료 →
런타임 안개·조명·Post Process 동적 제어 및 DT_SoundList 기반 BGM/SFX 매니저
에디터 · 콘텐츠 작업
C++ 프레임워크는 올라갔지만, 에디터(블루프린트·레벨·데이터 테이블)에서 만들어야 하는 콘텐츠는 종료 시점까지 많이 남아 있었습니다.
개별 이상현상 구현 (71종 프리셋)
C++ 이상현상 프레임워크(AnomalyManager·AnomalyTargetComponent)는 완성 — 오브젝트 Transform/Scale/Material/Visibility/Sound/Spawn 등 11종 적용 타입 지원.
종료 시점 기준 C++ 기믹은 CeilingLowTargetComponent, GradualScaleTargetComponent 2종까지 구현했고,
71종 프리셋을 에디터에서 하나씩 세팅하는 작업은 남아 있었습니다.
나머지는 블루프린트 기믹이나 DT_Anomalies 데이터로 개별 세팅해야 하는 몫이었습니다.
플레이어 모션 · 애니메이션
기본 이동(Idle/Walk/Run), 카메라 셰이크, 사망 몽타주는 구현 완료. 캠코더 들기/내리기 모션, 손전등 조작 모션, 거울 인터랙션 연출 애니메이션, 복도 리스폰 시퀀스 애니메이션, 즉사 리액션 같은 상황별 모션은 만들지 못한 채 종료했습니다.
레벨 디자인 · 오브젝트 배치
Map 1·2 교실 내부에 ATOIDoor, ATOIMirror, ATOISpawnPoint 배치,
AnomalyTargetComponent를 이상현상 대상 오브젝트에 부착,
NPC 배치 위치·책상 규격·창문·안내판 등 상세 오브젝트 레벨 세팅,
복도 구조 및 계단실 투명벽 설정
시퀀스 · 비주얼 연출
문/거울 판정 결과 레벨 시퀀스, 맵 전환 시퀀스, 경비원 NPC 등장 연출, 자아 간섭 개별 연출(ESC 강탈 UI, 가짜 메모장/OBS 창, 화면 왜곡), 캠코더 시각 필터(노이즈·그레인), 사물함·마지막 좌석 공포 연출
데이터 테이블 · 블루프린트
DT_Anomalies(71종 프리셋 데이터 입력), DT_StageConfig(스테이지별 규칙 수치), DT_InterferenceRules(자아 간섭 규칙), DT_Dialogs(대사), DT_SoundList(사운드), DT_Environment(환경) 실제 데이터 채우기 및 WBP_ 위젯 블루프린트 디자인·바인딩
사운드 · 오디오 리소스
이벤트 기반 SFX(판정 성공/실패, 맵 전환, 손전등)는 훅 완료. 스테이지별 동적 BGM, 경비원 속삭임 사운드, 이상현상별 개별 효과음, 자아 간섭 연출 사운드, 환경 앰비언스 오디오는 리소스 제작과 연결 전에 종료했습니다.
팀 협업 · 형상관리
8인 팀에서 PM 역할을 겸했습니다. Azure DevOps로 형상관리 환경을 세팅하고, 아트 파트 팀원들이 쓸 온보딩 가이드를 직접 작성해서 배포했습니다.
형상관리 — Azure DevOps
형상관리는 Azure DevOps Repos로 운영했습니다. 팀원 전원이 main 브랜치에 커밋했고, 프로그래밍·아트·기획 파트가 같은 이력을 봤습니다.
PM 역할 · 온보딩 가이드
형상관리가 처음인 아트 파트 팀원들이 막히지 않게 온보딩 가이드를 직접 써서 배포했습니다. Azure DevOps 사용법, 에셋 워크플로우, 아트 작업 가이드까지 세 종류를 만들어 질문이 반복되는 구간을 문서로 대체했습니다.