세 명의 작업자 코드가 merge된 이후, 현재 Playground에 구현된 모든 시스템을 빠짐없이 정리한 최종 기술 보고서입니다.


1. 프로젝트 개요

Playground는 브라우저에서 동작하는 아이소메트릭 오픈 월드 시뮬레이션 게임입니다. 9명의 NPC가 각자의 일과를 살아가는 마을에서 플레이어가 산책하고, 대화하고, 퀘스트를 수행합니다.

기술 스택

분류 기술
프론트엔드 HTML5 Canvas 2D, Vanilla JS (4,986줄 단일 IIFE), Jekyll
호스팅 GitHub Pages
LLM 대화 Google Gemini 프록시 (Cloud Run), SSE 스트리밍
멀티플레이어 Firebase Realtime Database
보안 Cloudflare Turnstile, CORS, Rate Limiting
저장 localStorage (클라이언트)

파일 구조

├── assets/
│   ├── css/playground.css         # UI 스타일
│   └── js/playground-world.js     # 게임 엔진 (4,986줄)
├── playground/index.md            # HTML 구조 (Jekyll front matter)
└── server/
    ├── llm-proxy.mjs              # LLM 프록시 서버
    └── firebase-rules.json        # DB 보안 규칙

2. 월드 구조

2.1 맵

  • 크기: 34x34 타일 아이소메트릭 그리드
  • 지형 3종: 잔디(3종 변형 텍스처), 도로(x=13 세로 + y=17 가로, 폭 1.2타일), 강(좌측, 사인파 곡선)
  • 건물 3개: 카페(22,7 / 3x2), 사무실(25,9 / 4x2), 시장(19,23 / 4x3)
  • 장소 8개: 광장, 카페, 사무실, 공원, 시장, 집A/B/C
  • 소품 29개: 나무 9, 가로등 4, 덤불 4, 꽃 5, 울타리 6
  • 핫스팟 4개: 출구(13, 32.5), 카페입구, 시장게시판, 공원기념비

2.2 충돌

  • canStand(x, y): 맵 경계(1타일 안쪽), 건물 내부, 강 진입 불가
  • X축/Y축 개별 판정으로 벽 슬라이딩 지원

2.3 시간

  • 시작: 오전 8시 (480분)
  • 속도: 실시간 1초 = 게임 14분 (하루 약 103초)
  • 영향: 하늘 색상, NPC 스케줄, 퀘스트 조건, 이벤트 트리거, 가로등 점등

3. 플레이어

3.1 이동

항목
기본 속도 3.7 타일/초
달리기 배율 Shift 또는 모바일 홀드 시 x1.75
폭풍 감속 x0.8
눈 감속 x0.88
카드 속도 보너스 첫 일출 카드 보유 시 +5%
자동산책 속도 x0.5 (느긋한 산책)

3.2 입력 방식

플랫폼 이동 상호작용 카메라
데스크톱 WASD/방향키 E키 마우스 드래그/스크롤
모바일 가상 조이스틱 대화 버튼 터치 드래그/핀치
공통 클릭-투-무브 NPC 클릭 Space 초기화

3.3 자동산책

  • T키 또는 버튼으로 토글, localStorage 저장
  • 목적지 선택: NPC 근처 42% / 랜덤 장소 32% / 완전 랜덤 26%
  • 수동 입력 시 즉시 해제
  • 자동산책 중 NPC 근처(1.75타일) 접근 시 LLM 자동 대화 발생 (13~25초 간격)

3.4 이름

  • 최초 접속 시 프롬프트, localStorage 저장
  • "이름 변경" 버튼으로 변경 가능, 18자 제한

4. NPC 시스템

4.1 기본 NPC 9명

이름 색상 직장 취미장소 성격 키워드
허승준 빨강 집A 사무실 공원 차분, 책임감
김민수 파랑 집B 시장 광장 친절, 실용적
최민영 초록 집C 카페 공원 관찰력, 꼼꼼
정욱진 보라 집A 카페 시장 활발, 사교적
서창근 주황 집B 사무실 광장 분석적, 직설적
이진원 청록 집C 시장 광장 따뜻, 협력적
박지호 갈색 집A 사무실 공원 승부욕, 자신감
장동우 하늘 집B 카페 시장 신중, 인내심
유효곤 파랑 집C 사무실 광장 차분, 끈기

각 NPC는 npcPersonas에 나이(20대), 성별(남성), 성격 문자열이 정의되어 LLM 대화 시 시스템 프롬프트에 포함됩니다.

4.2 AI 행동 패턴

시간대 행동
~08시 집에서 대기
08~17시 직장으로 이동, 주변 배회
17~21시 취미장소로 이동, 주변 배회
21시~ 귀가
  • 로밍: 목적지 근처 랜덤 반경 내 배회, 도착 후 0.6~2.8초 대기
  • 장거리 여행: 4~12 게임시간마다 랜덤 장소 원정
  • 폭풍/강한 비: 카페, 사무실, 시장으로 대피
  • 대화 중: 이동 정지 ("chatting" 상태)
  • NPC간 사교: 22~56 게임분마다 근처 NPC 2명이 자발적 대화 (말풍선 + 로그)
  • 기분 표시: 머리 위 이모지 (😊/😢/😐)
  • 호감 하트: 호감 레벨만큼 ♥ 표시 (최대 4개)
  • 요청 마커: 활성 요청 시 ❗ 표시

4.3 커스텀 NPC

  • 생성: 이름 + 성격(선택) 입력, 최대 48명, 플레이어 근처 스폰
  • 공유 서버: WORLD_NPC_API_URL에 POST로 등록, 18초마다 동기화
  • 제거: 드롭다운 선택 또는 채팅 명령어 (제거 이름), 관련 이벤트/퀘스트/호감요청 자동 정리

5. 대화 시스템

5.1 대화 타겟 결정 (우선순위)

  1. 클릭으로 고정된 NPC (conversationFocusNpcId), 2x 거리 이내
  2. 클릭 선택된 NPC (focusedNpcId)
  3. 가장 가까운 NPC (4.6타일 이내)

5.2 LLM 기반 대화

모드 엔드포인트 타임아웃 특징
스트리밍 LLM_STREAM_API_URL (SSE) 20초 점진적 텍스트 표시
일반 LLM_API_URL (POST) 12초 스트리밍 실패 시 폴백
로컬 - - LLM 전체 실패 시 토픽 감지 기반 응답

컨텍스트 전송 내용: NPC 페르소나(나이/성별/성격), 게임 시간, 퀘스트 목표, 근처 NPC 이름, 관계도 수치, 최근 6개 대화 내역

5.3 특수 채팅 명령어

명령어 동작
선물 / gift / / give NPC에게 인벤토리 아이템 선물
인벤 / 인벤토리 / inventory / 가방 보유 아이템 목록 표시
제거 / 삭제 / remove + 이름 NPC 제거

5.4 토픽 감지 (로컬 폴백)

정규식 기반으로 5개 토픽 분류: quest, people, world, positive, general. 각각 맞춤 응답 생성.

5.5 앰비언트 대사

  • NPC 혼잣말: 6.8~17.8초 간격, 종족별 대사 풀
  • 자동산책 중 플레이어 혼잣말: 12~26초 간격, LLM 생성
  • 자동 대화: NPC 1.75타일 이내 접근 시 LLM 대화 교환

5.6 말풍선 렌더링

  • 캔버스 위 둥근 사각형 + 꼬리 포인터
  • 14자마다 줄바꿈, truncation 없이 전체 표시
  • 마지막 0.45초 페이드아웃, 최대 14개 동시 표시
  • 줌에 따라 폰트 크기 스케일 (16~20px)

6. 퀘스트 시스템

6.1 메인 퀘스트: "이웃의 실타래" (5단계)

단계 목표 보상
0→1 광장에서 허승준에게 말 걸기 관계도 +6
1→2 김민수에게 메시지 전달 관계도 +8, 허-김 +10
2→3 카페에서 최민영 만나기 관계도 +6
3→4 20시 이후 공원 기념비 조사 -
4→5 허승준에게 결과 보고 관계도 +10, 동적 퀘스트 해금

6.2 동적 퀘스트 (10종 템플릿, 3티어)

티어 1 (questCount 0~5)

유형 단계 핵심 메커닉
deliver 3 NPC A → NPC B 메시지 전달 → 보고
explore 3 장소 방문 → 보고
social 3 같은 NPC와 3회 대화
observe 3 야간(20~23시) 장소 방문 → 보고
fetch 2 특정 아이템 구해 NPC에게 전달

티어 2 (questCount 6~15)

유형 단계 핵심 메커닉
chain 5 3명 NPC 순서대로 메시지 릴레이 → 보고
investigate 4 단서 획득 → 장소 방문 → 미스터리 NPC 찾기 → 보고
gift_quest 3 아이템 수집 → 다른 NPC에게 선물 전달

티어 3 (questCount 16+)

유형 단계 핵심 메커닉
nightwatch 4 20시 이후 2곳 야간 순찰 → 보고
urgent 3 긴급 배달, 60초 내 완료 시 보너스 (관계도 +5, 호감 +10)

6.3 퀘스트 생성 규칙

  • 중복 방지: 최근 3회 같은 유형 금지, 최근 2회 같은 주요 NPC 회피
  • LLM 대사 보강: enrichQuestDialogue()로 NPC 성격에 맞는 대사 비동기 교체 시도
  • 제거된 NPC 자동 처리: 퀘스트 대상 NPC 제거 시 해당 단계 자동 스킵

6.4 완료 보상

  • 관계도 +5 (추가 단계당 +2)
  • 호감 포인트 15 (카드 배율 적용)
  • 50% 확률 랜덤 아이템
  • 카드 드롭 기회 (25%)
  • urgent 60초 내 완료: 추가 관계도 +5, 호감 +10

7. 호감도(Favor) 시스템

7.1 호감 레벨

레벨 이름 레벨업 필요 포인트
0 낯선 사이 -
1 아는 사이 100
2 친구 100
3 절친 100
4 소울메이트 100 (최대)

7.2 호감 요청 (Favor Request)

최소 레벨 유형 보상
0 아이템 가져오기 (bring_item) +20 호감, +8 관계도
1 다른 NPC에게 전달 (deliver_to) +25 호감, +10 관계도, 간식
2 장소 방문 (visit_place) +30 호감, +12 관계도, 보석
  • 발생 조건: 0.8% 확률/틱, 플레이어 20타일 이내, 120초 쿨다운
  • 표시: NPC 머리 위 ❗ 마커

7.3 관계도 (Relations)

4개 관계 추적: playerToHeo(52), playerToKim(47), heoToKim(38), playerToChoi(50). 범위 0~100, 카드 효과 배율 적용.


8. 아이템 시스템

8.1 아이템 종류 (6개)

이모지 이름 NPC 선물 관계도 비고
🌹 빨간 꽃 +5 -
🌼 노란 꽃 +5 -
커피 원두 +5 -
🍪 간식 +5 요리사의 비밀 카드로 2배 획득
💌 편지 +8 퀘스트 재료
💎 보석 +12 보석 사냥꾼 카드로 리스폰 단축

8.2 메커니즘

  • 12개 스폰 포인트 맵 곳곳에 고정 배치
  • 리스폰: 180초 (보석은 카드 효과로 단축)
  • 줍기: 1.5타일 이내, E키 또는 모바일 버튼
  • 선물: 채팅에서 선물 명령어로 NPC에게 증정
  • 카드 드롭: 줍기/선물 시 확률적으로 발생

9. 카드 수집 시스템

9.1 카드 목록 (8장)

카드명 등급 효과
첫 일출 Rare 이동속도 +5%
별이 빛나는 밤 Rare 야간 시야 확대 (밤 오버레이 감소)
우정의 증표 Epic 관계도 획득량 +10%
탐험가의 발자국 Common 아이템 발견 범위 +15%
요리사의 비밀 Common 간식 2배 획득
보석 사냥꾼 Epic 보석 리스폰 시간 -20%
사교계의 달인 Rare 호감 포인트 +15%
전설의 주민 Legendary 모든 보상 2배

9.2 드롭 확률

트리거별 기본 확률:

트리거 확률
시간제 이벤트 완료 30%
퀘스트 완료 25%
NPC 상호작용 6%
아이템 줍기 4%
기타 3%

등급별 선별 확률: Legendary 2% → Epic 12% → Rare 25% → Common 50%


10. 시간제 이벤트

10.1 이벤트 템플릿 (3종)

유형 제한시간 조건 보상
깜짝 세일 (flash_sale) 120초 NPC 근처 + 요청 아이템 보유 관계도 +15
마을 모임 (gathering) 90초 특정 장소 방문 보석 + 간식
NPC 위기 (npc_emergency) 100초 NPC 근처 도착 관계도 +20
  • 발생: 60~180초마다 40% 확률
  • 표시: 캔버스 상단 빨간 카운트다운 배너, 30초 미만 시 긴급 표시
  • 카드 드롭: 완료 시 30% 확률

11. 날씨 시스템

11.1 날씨 유형

clear(x3 가중치), cloudy, rain(x2), storm, snow, fog — 3~8분마다 전환, 부드러운 intensity/wind 보간

11.2 파티클 시스템 (5종)

파티클 최대 수 발생 조건 특징
150~300 rain/storm 바람 영향, 지면 충돌 시 물튀김
120 snow 사인파 흔들림, 바람 드리프트
물튀김 - 비 착지 시 0.3초 링 이펙트
반딧불 18 공원 근처 야간(20~05시) 사인/코사인 궤적, 발광
낙엽 8 항상 회전하며 바람에 날림

11.3 게임플레이 영향

날씨 영향
폭풍 플레이어 x0.8 감속, NPC 건물 대피, 번개 플래시
도로 웅덩이+파문 애니메이션
플레이어 x0.88 감속, 잔디 위 적설 이펙트
안개 반투명 파란 오버레이

12. 발견(Discovery) 시스템

12.1 발견 포인트 (15개)

이름 조건 보상
비밀 정원 항상 💎
강변의 편지 항상 💌
자정의 빛 야간 (22~4시) 💎
비 오는 날의 버섯 비/폭풍 🍪
숨겨진 우물 항상
노을 전망대 저녁 (17~20시) 🌹
안개 속 그림자 안개 💎
시장 뒷골목 비밀 항상 🍪
밤의 고양이들 야간 🍪
비밀 꽃밭 항상 🌹
폭풍의 수정 폭풍 💎
눈 위의 천사 💎
새벽의 노래 새벽 (4~7시) 💌
광장의 흔적 항상
소원의 가로등 야간 💌

12.2 메커니즘

  • 6타일 이내: 금색 반짝임 파티클 표시 (3타일 이내 시 강화)
  • 발견 시: 알림 배너 + 아이템 획득 + 카드 드롭 기회
  • 진행도: UI에 "발견 N/15" 표시

13. 렌더링 엔진

13.1 아이소메트릭 투영

screenX = (wx - wy) * (tileW/2) + cameraX
screenY = (wx + wy) * (tileH/2) + cameraY - wz * tileH

baseTileW: 40px, baseTileH: 20px, 줌 배율 적용 (1.4~6.0)

13.2 렌더링 레이어 순서 (drawWorld)

  1. 하늘: 시간대별 그래디언트, 태양/달/별/구름
  2. 타일 그리드: 잔디(텍스처 3종), 도로(웅덩이), 강(반짝임), 적설
  3. 건물: 3D 앞면/옆면, 삼각 지붕, 문/창문/간판(건물별 아이콘)
  4. 핫스팟 마커: 금색 원, 출구에 "출구" 라벨
  5. 바닥 아이템: 이모지 + 바운스 애니메이션 + 글로우
  6. 씬 아이템 (깊이 정렬 x+y): 소품 + NPC + 플레이어 + 원격 플레이어
  7. NPC 표시기: 요청 ❗, 기분 이모지, 호감 하트 ♥
  8. 카드 알림 배너: 희귀도별 색상 (회/파/보/주)
  9. 발견 반짝임
  10. 말풍선
  11. 야간 오버레이: 20~05시 점진적 어두움, nightVision 카드로 감소
  12. 가로등 글로우: additive blending 원형 빛
  13. 반딧불: 녹색 발광 파티클
  14. 날씨 이펙트: 비/눈/안개/번개/낙엽/물튀김
  15. 날씨 인디케이터: 우상단 한글 라벨

13.3 캐릭터 스프라이트

140x140 오프스크린 캔버스에 상세 렌더링 후 캐시:

  • 그림자 → 다리+신발 → 트라페조이드 상체 → 팔+손 → 머리+헤어+얼굴
  • 9종 헤어 스타일 (human_a~i), 볼 블러시, 미소
  • 플레이어 피부톤: #f6d5ba, NPC: #f2cfb1

13.4 소품 스프라이트 (5종)

소품 표시 크기 특징
나무 44x60 줄기 + 원형 나뭇잎 3개 (radial gradient)
덤불 34x28 원형 3개 (radial gradient)
16x24 줄기 + 꽃잎 4개 (분홍/노랑 교대)
울타리 20x22 나무 판자 + 가로 레일 2개
가로등 18x42 기둥 + 사각 등

13.5 미니맵

별도 캔버스에 탑뷰 렌더링: 물(파랑), 도로(갈색), 건물(회색), 핫스팟(금색), 아이템(컬러 점), NPC(컬러 점), 플레이어(큰 점+외곽선), 뷰포트 사각형. 모바일에서는 3프레임마다 1회 갱신.


14. 카메라

기능 동작
추종 0.08 lerp로 플레이어 중심 추적
수동 패닝 마우스 드래그/터치 (±320px x ±220px)
스크롤/핀치 (1.4~6.0, 기본 3.2)
대화 카메라 진입 시 3.6 이상 자동 줌인 + NPC 방향 시네마틱 패닝
초기화 Space키 / 모바일 버튼

15. 멀티플레이어 (Firebase)

항목 상세
DB Firebase Realtime Database, playground/players/{sessionId}
브로드캐스트 100ms 간격 (이름, 위치, 색상, 종족, 타임스탬프)
위치 보간 lerp = min(1, dt * 8)
Stale 정리 300프레임마다 12초 이상 비활성 플레이어 제거
연결 해제 onDisconnect().remove() 자동 삭제
보안 x/y [0,34] 클램핑, 이름 <> 제거/20자 제한
표시 씬 + 미니맵에 렌더링, HUD에 "접속자: N명"

16. UI 구조

16.1 데스크톱 (>900px)

  • 좌측 HUD: 조작 카드 (저장/불러오기/이름변경/자동산책), 캐릭터관리 카드 (NPC 생성/제거), 월드상태 카드 (시간, 플레이어, 접속자, 근처, 퀘스트, 관계도, 미니맵)
  • 우측 HUD: 이벤트 로그 (최대 16개)
  • 채팅 독: 우하단 (대상, 상태, 모델, 채팅로그, 입력)
  • 퀘스트 배너: 상단 중앙, 반투명 배경
  • 패널 토글: 좌/우/채팅 개별 표시/숨김, UI 전체 토글

16.2 모바일 (≤900px)

  • 가상 조이스틱 + 액션 버튼 (대화/달리기/일시정지/시점초기화/추가기능/산책)
  • 대화 버튼: 상황별 동적 라벨 (나가기/문 열기/게시판 보기/조사하기/줍기/대화)
  • 채팅 독: 전체 너비 하단, 활성화 시 컨트롤 숨김
  • 상태/로그 섹션 접기 가능
  • 미니맵: 추가기능 패널 오픈 시만 표시

16.3 반응형 브레이크포인트

너비 변화
≤420px 버튼 축소
≤900px 모바일 모드 전환
≤1120px 우측 HUD 숨김
≤1320px HUD 너비 축소

16.4 Z-index 레이어

z-index 요소
40 퀘스트 배너 (모바일)
38 채팅 독 (모바일)
36 모바일 컨트롤
35 HUD 패널 (모바일)
32 퀘스트 배너 (데스크톱)
31 채팅 독 (데스크톱)
30 UI/패널 토글
20 월드 UI 오버레이

17. 월드 이벤트

17.1 일일 이벤트

시간 이벤트
매일 0시 새 날 시작 (이벤트 플래그 리셋)
9시 "카페가 열었습니다"
20시 "야간 시장이 열렸습니다"
야간+공원 근처 "기념비에서 이상한 기운" (2.5타일 이내)

17.2 핫스팟 상호작용

핫스팟 동작
출구 "/" 페이지로 리다이렉트
카페 입구 플레이버 텍스트, 최민영 관계도 +1
시장 게시판 야간 시장 안내문
공원 기념비 퀘스트 3단계: 20시 이후 단서 공개

18. 저장/불러오기

localStorage 키

내용
playground_world_state_v2 전체 게임 상태
playground_ui_pref_v1 UI 패널 표시 설정
playground_player_name_v1 플레이어 이름
playground_mobile_sheet_v1 모바일 탭 상태
playground_auto_walk_v1 자동산책 설정

저장 항목

월드 시간/줌/카메라, 플레이어 이름/위치, 관계도 4개, 퀘스트 전체 상태(동적 포함), NPC 위치/호감도(기본 9명), 인벤토리, 보유 카드/앨범, 제거된 NPC ID, 발견된 디스커버리 ID, 퀘스트 히스토리/카운트


19. 게임 루프

매 프레임 requestAnimationFrame 실행:

1. resizeCanvasToDisplaySize()
2. dt 계산 (최대 0.05초 = 최소 20FPS)
3. [비정지 시]
   ├─ world.totalMinutes += dt * 14
   ├─ updatePlayer(dt)
   ├─ updateNpcs(dt)
   ├─ updateNpcSocialEvents()
   ├─ updateAmbientEvents()
   ├─ updateFavorRequests()
   ├─ updateWeather(dt)
   ├─ updateDiscoveries()
   ├─ updateAmbientSpeech(now)
   ├─ updateConversationCamera()
   ├─ updateCamera()
   └─ [멀티플레이어] mpBroadcast, mpInterpolate, mpCleanStale
4. [항상]
   ├─ updateUI()
   ├─ drawWorld()
   ├─ drawTimedEventHud()
   └─ drawMinimap() (모바일: 3프레임마다 1회)

20. 보안

시스템 설명
Cloudflare Turnstile invisible 위젯, API 호출 시 인증 토큰 첨부
Firebase sanitize 원격 플레이어 데이터 클램핑/필터링
이름 정규화 <> 제거, 공백 정리, 18/20자 제한
onDisconnect 비정상 종료 시 자동 데이터 삭제

21. 전체 수치 요약

항목 수치
코드 줄 수 4,986줄 (JS)
DOM 요소 ID 53개
CSS 클래스 40+
기본 NPC 9명
퀘스트 템플릿 10종 (3티어)
카드 8장 (4등급)
아이템 6종
발견 포인트 15개
날씨 파티클 5종
시간제 이벤트 3종
건물 3개
소품 29개 (5종)
핫스팟 4개
호감 레벨 5단계
맵 크기 34x34 타일

이 보고서는 2026-02-18 기준, merge 완료 후 최종 코드를 분석하여 작성되었습니다.