cleanyourtv-app
A community-built phone app for Clean Your TV: clean up an Android TV from your phone. No laptop, no coding agent, nothing uninstalled.
Rapier + three.js로 만든 웹 기반 액티브 랙돌 — 밀리고 맞고 넘어지고 일어서는 반응을 물리로 사람답게 시뮬레이션 (Euphoria 스타일)
Translation pending
OtherGitHub @junjun612OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗웹에서 돌아가는 Euphoria 스타일 액티브 랙돌
밀리고, 맞고, 넘어지고, 다시 일어서는 반응을 애니메이션 재생이 아니라 물리로 — 근육으로 버티고 균형을 잡는 캐릭터를 Rapier + three.js로 만든다.
일반 랙돌은 맞는 순간 힘이 빠진 인형이 된다. 애니메이션 기반 피격은 늘 같은 동작을 재생한다. Advanced Ragdoll의 캐릭터는 매 순간 근육으로 자세를 유지하면서, 충격의 방향·세기·부위·지형에 따라 매번 다르게 반응한다.
모든 장면은 같은 캐릭터 · 같은 설정 · 키프레임 없이 물리로 계산된 결과다. 화면 속 라벨은 디버그 표시(활성 행동, 균형 전략, 넘어짐 확실도 등).
git clone https://github.com/junjun612/advanced-ragdoll.git
cd advanced-ragdoll
npm ci
npm run dev # → http://localhost:5173
브라우저에서 시나리오를 고르거나, 캐릭터를 직접 밀고 · 쏘고 · 폭발시키고 · 상자를 던져 보자.
import { World } from './src/sim/api';
import xbot from './config/characters/xbot.json';
import ragdoll from './config/ragdoll.json';
const world = await World.create({ seed: 1 }); // 고정 스텝 1/120 s, 결정론
const hero = world.addCharacter(xbot, ragdoll, {
position: [0, 0, 0], yaw: 0, state: 'Active', pose: 'stand',
});
hero.on('fell', (e) => console.log('넘어짐', e.t));
hero.on('gotUp', (e) => console.log('일어섬', e.t));
// 가슴을 앞에서 중간 세기로 민다 — 세기는 전신 질량중심 속도 변화 Δv(m/s)
hero.hit({ type: 'push', body: 'Spine2', direction: [0, 0, -1], magnitude: 'medium' });
// 오른 허벅지에 총탄
hero.hit({ type: 'bullet', body: 'RightUpLeg', direction: [0, 0, -1], magnitude: 0.35 });
function frame(dt: number) {
world.advance(dt); // 필요한 만큼 고정 스텝
draw(hero.getState().bodies); // 바디별 위치 · 회전
}
전체 API — 이벤트, 상태 저장·복원(World.save / load), 행동 트리거, 근육 세기 조절 — 는 docs/API.md.
flowchart TB
IN["외부 자극<br/>밀기 · 총탄 · 폭발 · 접촉"] --> BEH
subgraph CTRL["제어 (매 스텝 1/120 s)"]
BEH["행동 계층<br/>피격 반응 · 움켜쥐기 · 손 짚기 · 머리 보호<br/>잡기 · 벽 짚기 · 일어서기 …"]
REF["레퍼런스<br/>Kimodo 모션 클립<br/>(걷기 · 반응 · 겟업)"]
BAL["균형 · 스텝<br/>DCM / 캡처 포인트"]
QP["전신 운동량 QP<br/>(43 자유도)"]
BEH --> QP
REF --> QP
BAL --> QP
end
QP --> MUS["근육<br/>관절 모터 (강도 · 감쇠)"]
MUS --> PHY["Rapier 물리<br/>16 바디 랙돌"]
PHY -->|상태 · 접촉| BEH
PHY --> R["three.js<br/>X Bot 스킨 렌더"]
| 층 | 하는 일 |
|---|---|
| 근육 | 관절 모터가 목표 자세로 당긴다. 맞은 부위는 잠깐 힘이 빠지고 서서히 회복한다 |
| 균형 · 스텝 | 질량중심이 넘어갈 곳(캡처 포인트)을 예측해 발목·엉덩이 전략을 쓰고, 안 되면 발을 디딘다 |
| 전신 QP | 균형·자세·손 과제를 동시에 만족하는 관절 가속도와 접촉력을 푼다 |
| 행동 | 상황(맞음, 넘어지는 중, 누움, 손이 닿는 난간 …)에 맞는 행동을 섞는다 — 23종 |
| 레퍼런스 | 생성 AI 모션을 "목표"로만 쓴다. 따라가는 건 언제나 물리다 |
정직한 보조 힘. 몸을 허공에서 잡아 주는 숨은 힘은 없다. 필요한 보조(루트 · 핀 · 겟업)는 명시적으로 켜고 끄며, 사용량을 매 프레임 기록한다. 피격 반응 중 핀 보조는 0이다.
결정론. 같은 시드면 Node에서 바이트 단위로 같은 결과가 나온다. 82개 시나리오가 회귀 테스트로 이를 지킨다.
통과/실패보다 사람처럼 보이는가가 목표다. 사람 모션 레퍼런스와 비교한 지표로 판정한다.
| 지표 | 사람 | 지금 | 목표 |
|---|---|---|---|
| 밀린 뒤 팔 들림 (상위 10%) | 33° | 56° | 38° |
| 팔 사용량 | 78°/s | 101°/s | 102°/s |
| 몸통 최대 기울기 | 16° | 28° | 21° |
| 근처 입력 넘어짐률 | 0% | 11% | 5% |
근처 입력 138회 통계 (config/qualityTargets.json). 시나리오 하나가 아니라 시드 묶음으로 잰다 — 액티브 랙돌은 작은 차이에도 결과가 갈리는 혼돈계이기 때문이다.
npm test # 단위 테스트 (vitest)
npm run sim:all # 전체 시나리오 → out/report.md
npm run sim -- push_left_medium # 시나리오 하나 → out/<이름>/log.jsonl, summary.json
npm run film -- push_left_medium # 필름스트립 (0.1초 간격 컷 + 디버그 라벨)
npx tsx tools/reel.ts # 하이라이트 GIF · MP4
npx vite build # 웹 데모 → dist/
| 문서 | 내용 |
|---|---|
| SPEC.md | 전체 명세 — 목표, 원칙, 마일스톤, 시나리오, 품질 지표 |
| docs/API.md | 공개 API |
| docs/ALGORITHMS.md | 근육 · 균형 · 스텝 · 행동 알고리즘 |
| docs/KIMODO.md | 생성 AI 모션 클립 만들기 · 고르기 |
| docs/DEVLOG.md | 진행 상황과 결정 기록 (날짜 · 결정 · 이유 · 대안) |
| docs/RESEARCH_APPROACHES.md | 접근법 조사 — 해석적 제어 vs 학습형 추종기 |