Initial Dansori character workspace
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# 반응 시퀀서와 트리거
|
||||
|
||||
WPF 앱은 상황키를 보낸다. Live2D 런타임은 `reactions.json`과 `clips/*.json`을 읽어 motion, expression, mouth/caption, sfx를 실행한다.
|
||||
|
||||
## 트리거 매퍼
|
||||
|
||||
```json
|
||||
{
|
||||
"map": {
|
||||
"idle": "idle_dance",
|
||||
"error": "gesture_no",
|
||||
"success": "gesture_heart"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 반응 클립 스키마
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "gesture_heart",
|
||||
"duration": 2.2,
|
||||
"return": "idle",
|
||||
"live2d": {
|
||||
"motion": { "group": "Reaction", "name": "motion_heart", "file": "motions/heart.motion3.json" },
|
||||
"expression": { "name": "exp_love", "file": "expressions/love.exp3.json" },
|
||||
"mouth": { "t": 0.5, "say": "잘됐어요", "dur": 1.1, "driver": "volume" },
|
||||
"caption": { "t": 0.5, "text": "잘됐어요", "dur": 1.5 },
|
||||
"sfx": { "t": 0.45, "id": "success" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 런타임 처리
|
||||
|
||||
1. WPF가 WebView2에 `{ "type": "react", "key": "success" }` 전송.
|
||||
2. JS 런타임이 `reactions.json`에서 클립을 선택.
|
||||
3. `clips/*.json` 로드.
|
||||
4. Cubism 모델에 motion과 expression 적용.
|
||||
5. mouth driver가 있으면 `ParamMouthOpenY`를 시간/볼륨에 따라 구동.
|
||||
6. caption, TTS, sfx를 앱 설정에 따라 표시 또는 재생.
|
||||
7. 종료 후 idle motion으로 복귀.
|
||||
|
||||
## 상황 카탈로그
|
||||
|
||||
| 상황키 | 클립 | Motion | Expression | 대사 |
|
||||
|---|---|---|---|---|
|
||||
| `idle` | `idle_dance` | `motion_idle_dance` | `exp_smile` | 없음 |
|
||||
| `error` | `gesture_no` | `motion_no` | `exp_negative` | 안돼요 |
|
||||
| `success` | `gesture_heart` | `motion_heart` | `exp_love` | 잘됐어요 |
|
||||
| `greet` | `gesture_greet` | `motion_greet` | `exp_smile` | 안녕하세요 |
|
||||
| `explain` | `gesture_present` | `motion_present` | `exp_neutral` | 상황별 |
|
||||
| `thinking` | `gesture_thinking` | `motion_thinking` | `exp_thinking` | 없음 |
|
||||
|
||||
## WPF API 초안
|
||||
|
||||
```csharp
|
||||
Mascot.SetIdle("idle");
|
||||
Mascot.React("success");
|
||||
Mascot.React("error");
|
||||
Mascot.Say("환영합니다", expression: "exp_smile");
|
||||
Mascot.SetVisible(true);
|
||||
```
|
||||
|
||||
## WebView2 메시지
|
||||
|
||||
```json
|
||||
{ "type": "react", "key": "success" }
|
||||
{ "type": "say", "text": "환영합니다", "expression": "exp_smile" }
|
||||
{ "type": "setIdle", "key": "idle" }
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "gesture_heart",
|
||||
"desc": "성공/완료: 손하트 느낌의 밝은 반응과 '잘됐어요'",
|
||||
"duration": 2.2,
|
||||
"return": "idle",
|
||||
"live2d": {
|
||||
"motion": {
|
||||
"group": "Reaction",
|
||||
"name": "motion_heart",
|
||||
"file": "motions/heart.motion3.json",
|
||||
"fadeIn": 0.15,
|
||||
"fadeOut": 0.25,
|
||||
"priority": "normal"
|
||||
},
|
||||
"expression": {
|
||||
"name": "exp_love",
|
||||
"file": "expressions/love.exp3.json",
|
||||
"fade": 0.15
|
||||
},
|
||||
"mouth": {
|
||||
"t": 0.5,
|
||||
"say": "잘됐어요",
|
||||
"dur": 1.1,
|
||||
"driver": "timedVolume",
|
||||
"param": "ParamMouthOpenY"
|
||||
},
|
||||
"caption": {
|
||||
"t": 0.5,
|
||||
"text": "잘됐어요",
|
||||
"dur": 1.5
|
||||
},
|
||||
"sfx": {
|
||||
"t": 0.45,
|
||||
"id": "success"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "gesture_no",
|
||||
"desc": "오류/금지 동작: 고개를 저으며 '안돼요'",
|
||||
"duration": 2.4,
|
||||
"return": "idle",
|
||||
"live2d": {
|
||||
"motion": {
|
||||
"group": "Reaction",
|
||||
"name": "motion_no",
|
||||
"file": "motions/no.motion3.json",
|
||||
"fadeIn": 0.15,
|
||||
"fadeOut": 0.2,
|
||||
"priority": "force"
|
||||
},
|
||||
"expression": {
|
||||
"name": "exp_negative",
|
||||
"file": "expressions/negative.exp3.json",
|
||||
"fade": 0.15
|
||||
},
|
||||
"mouth": {
|
||||
"t": 0.55,
|
||||
"say": "안돼요",
|
||||
"dur": 1.2,
|
||||
"driver": "timedVolume",
|
||||
"param": "ParamMouthOpenY"
|
||||
},
|
||||
"caption": {
|
||||
"t": 0.55,
|
||||
"text": "안돼요",
|
||||
"dur": 1.6
|
||||
},
|
||||
"sfx": {
|
||||
"t": 0.5,
|
||||
"id": "nope"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "idle_dance",
|
||||
"desc": "대기 상태: 가벼운 춤과 호흡 루프",
|
||||
"duration": 2.0,
|
||||
"return": "idle",
|
||||
"live2d": {
|
||||
"motion": {
|
||||
"group": "Idle",
|
||||
"name": "motion_idle_dance",
|
||||
"file": "motions/idle_dance.motion3.json",
|
||||
"fadeIn": 0.3,
|
||||
"fadeOut": 0.3,
|
||||
"priority": "idle",
|
||||
"loop": true
|
||||
},
|
||||
"expression": {
|
||||
"name": "exp_smile",
|
||||
"file": "expressions/smile.exp3.json",
|
||||
"fade": 0.2
|
||||
},
|
||||
"autoBlink": true,
|
||||
"physics": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "LeeSori Live2D reactions map",
|
||||
"version": 1,
|
||||
"idleDefault": "idle_dance",
|
||||
"map": {
|
||||
"idle": "idle_dance",
|
||||
"error": "gesture_no",
|
||||
"success": "gesture_heart"
|
||||
},
|
||||
"plannedExpansion": {
|
||||
"greet": "gesture_greet",
|
||||
"explain": "gesture_present",
|
||||
"thinking": "gesture_thinking"
|
||||
},
|
||||
"runtimeMessages": {
|
||||
"react": { "type": "react", "key": "success" },
|
||||
"say": { "type": "say", "text": "환영합니다", "expression": "exp_smile" },
|
||||
"setIdle": { "type": "setIdle", "key": "idle" }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user