Initial Dansori character workspace
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# 반응 시퀀서 & 트리거 (Reactions)
|
||||
|
||||
상황 → 반응을 정의하는 레이어. **트리거 매퍼**(`reactions.json`) + **반응 클립**(`clips/*.json`)으로 구성.
|
||||
> ✅ **Phase 2 런타임 구현됨**: `../07_Viewer/reactions.html`(더블클릭). idle=배경춤(풀캔버스 리그, 허리 봉인 튜닝) + 트리거(idle/error/success). baked 바디 + 표정 머리(목 정합·회전)는 `../../_tools/reactions_layout_render.py`→`_layout.json` 사용. head base=`sori_head_short`. 오프라인 검증 `_reaction_preview.png`.
|
||||
|
||||
## 트리거 매퍼 (`reactions.json`)
|
||||
상황키 → 반응 클립 이름. 앱은 상황키만 던지면 된다.
|
||||
```json
|
||||
{ "error": "gesture_no", "success": "gesture_heart", "idle": "dance_idle" }
|
||||
```
|
||||
|
||||
## 반응 클립 스키마 (`clips/<name>.json`)
|
||||
하나의 반응 = 레이어드 타임라인.
|
||||
```jsonc
|
||||
{
|
||||
"name": "gesture_no",
|
||||
"duration": 2.4, // 초
|
||||
"return": "idle", // 종료 후 복귀 클립(보통 배경 idle)
|
||||
"layers": {
|
||||
"body": [ // Body 트랙: 시간에 따라 rig 클립 or baked 포즈
|
||||
{ "t":0.0, "mode":"rig", "clip":"idle" },
|
||||
{ "t":0.15,"mode":"baked", "image":"sori_body_track_armscross", "fade":0.2 }
|
||||
],
|
||||
"face": [ // 표정 프레임 트랙
|
||||
{ "t":0.0, "expr":"neutral" },
|
||||
{ "t":0.3, "expr":"negative" }
|
||||
],
|
||||
"mouth": [ // 말하기(유사 립싱크): expr↔talk 순환
|
||||
{ "t":0.5, "say":"안돼요", "dur":1.2, "pattern":"talk" }
|
||||
],
|
||||
"transform": { // 리그 위 잔모션(본별 delta) — Animation.md 스키마와 동일
|
||||
"head": { "rot":[ {"t":0.5,"v":0},{"t":0.8,"v":9},{"t":1.1,"v":-9},{"t":1.4,"v":9},{"t":1.7,"v":0} ] },
|
||||
"chest":{ "ty":[ {"t":0.0,"v":0},{"t":0.2,"v":-4},{"t":0.5,"v":0} ] }
|
||||
},
|
||||
"caption": [ { "t":0.5, "text":"안돼요", "dur":1.6 } ], // 옵션 말풍선
|
||||
"sfx": [ { "t":0.5, "id":"nope" } ] // 옵션 효과음
|
||||
}
|
||||
}
|
||||
```
|
||||
### 필드 규칙
|
||||
- `body[]`: 시간순. `mode:"rig"`+`clip` 또는 `mode:"baked"`+`image`(파일명, 확장자 생략 가능). `fade`=크로스페이드 초.
|
||||
- `face[]`: `expr` = 표정 프레임 키(20종 중). 시간에 스냅.
|
||||
- `mouth[]`: `say` 대사, `dur` 길이, `pattern:"talk"`(talk/talk_wide/현재 감정 프레임 순환). 립싱크는 근사.
|
||||
- `transform`: 본별 키프레임(리그 delta). Body가 baked여도 head/chest 등 트랜스폼은 적용(단 baked는 통짜라 파츠 분리 트랜스폼은 제한적 → 주로 전체/머리에 적용).
|
||||
- `caption`/`sfx`: 옵션. 앱 설정(말풍선/TTS)에 따라 사용.
|
||||
|
||||
## 상황 → 반응 카탈로그
|
||||
| 상황키 | 클립 | Body | Face | Mouth | 잔모션 |
|
||||
|---|---|---|---|---|---|
|
||||
| `error` | `gesture_no` | baked armscross | negative | "안돼요" | 고개 젓기 |
|
||||
| `success` | `gesture_heart` | baked heart | love/positive | "잘됐어요" | 통통 바운스 |
|
||||
| `idle` | `dance_idle` | rig | smile/neutral | — | 그루브 루프 |
|
||||
| *(확장)* `greet` | `gesture_wave` | rig wave | smile | "안녕하세요" | 손 흔들기 |
|
||||
| *(확장)* `explain` | `gesture_present` | rig present | neutral | 안내 대사 | 제시 |
|
||||
| *(확장)* `thinking` | `gesture_think` | rig idle_upper | thinking | — | 갸웃 |
|
||||
|
||||
## 트리거 API(개념)
|
||||
```
|
||||
Mascot.React("success") // reactions.json으로 클립 결정 → 시퀀서 재생 → 종료 후 return 클립
|
||||
Mascot.SetIdle("dance_idle")// 배경 기본 루프
|
||||
Mascot.Say("...", expr) // 임시 대사(mouth+face)만
|
||||
```
|
||||
상세 앱 연동: `../08_Roadmap/App_Integration.md`.
|
||||
Reference in New Issue
Block a user