メインコンテンツまでスキップ

VRM モジュール

Vrm クラスは @hmcs/sdk のコアモジュールです。VRM 3D キャラクターの完全なライフサイクルを管理します -- スポーン、検索、アニメーション、表情制御、ポインタ/ドラッグイベントの処理、リップシンクスピーチなどを提供します。

import { Vrm } from "@hmcs/sdk";

キャラクターのスポーン

Vrm.spawn() を使用して MOD アセットから新しい VRM キャラクターを作成します。慣例として、アセット ID は "mod-name:asset-name" 形式を使用します。

import { Vrm } from "@hmcs/sdk";

// 基本的なスポーン
const character = await Vrm.spawn("my-mod:character");

// 初期トランスフォームとペルソナを指定してスポーン
const character = await Vrm.spawn("my-mod:character", {
transform: {
translation: [0, 0, 0],
scale: [1, 1, 1],
},
persona: {
profile: "A cheerful virtual assistant",
ocean: { openness: 0.8, extraversion: 0.7 },
metadata: {},
},
});

Vrm.spawn() はスポーンされたエンティティにバインドされた Vrm インスタンスを返します。以降の操作はすべてこのインスタンスを使用します。

既存キャラクターの検索

キャラクターがすでにスポーンされている場合(例:別の MOD や以前のセッションで)、名前で検索したり、すべてのインスタンスを一覧表示したりできます。

// VRM モデル名で検索
const character = await Vrm.findByName("MyAvatar");

// キャラクターの読み込みを待機(準備完了までブロック)
const character = await Vrm.waitLoadByName("MyAvatar");

// 読み込み済みのすべての VRM インスタンスを取得
const allCharacters = await Vrm.findAll();

// すべての VRM の詳細スナップショットを取得(状態、トランスフォーム、表情、アニメーション)
const snapshots = await Vrm.findAllDetailed();
for (const s of snapshots) {
console.log(`${s.name}: ${s.state} at (${s.globalViewport?.[0]}, ${s.globalViewport?.[1]})`);
}

アニメーションの再生

playVrma() を使用してキャラクター上で VRMA アニメーションを再生します。アニメーションはアセット ID で参照します。ビルトインの @hmcs/assets MOD はデフォルトのアニメーションを提供します:vrma:idle-maidvrma:grabbedvrma:idle-sitting

import { Vrm, repeat } from "@hmcs/sdk";

const character = await Vrm.spawn("my-mod:character");

// 0.5秒のクロスフェードでループするアイドルアニメーションを再生
await character.playVrma({
asset: "vrma:idle-maid",
repeat: repeat.forever(),
transitionSecs: 0.5,
});

// ワンショットアニメーションを再生(1回再生後に停止)
await character.playVrma({
asset: "my-mod:wave",
repeat: repeat.never(),
});

// アニメーションをN回再生
await character.playVrma({
asset: "my-mod:nod",
repeat: repeat.count(3),
});

// ワンショットアニメーションの完了を待ってから続行
await character.playVrma({
asset: "my-mod:dance",
repeat: repeat.never(),
waitForCompletion: true,
});

// トランジション中にスプリングボーンをリセットして髪/衣服の揺れを防止
await character.playVrma({
asset: "vrma:grabbed",
repeat: repeat.forever(),
resetSpringBones: true,
});

アニメーション状態の照会、アニメーションの停止、再生速度の制御も可能です:

// すべてのアクティブなアニメーションを取得
const animations = await character.listVrma();

// 特定のアニメーションの状態を確認
const state = await character.vrmaState("vrma:idle-maid");
console.log(`Playing: ${state.playing}, Elapsed: ${state.elapsedSecs}s`);

// 再生速度を変更
await character.setVrmaSpeed("vrma:idle-maid", 1.5);

// アニメーションを停止
await character.stopVrma("vrma:idle-maid");

キャラクターイベント

events() を使用してキャラクターからのリアルタイムイベントを購読します。キャラクターの SSE ストリームに接続する Server-Sent Events 接続を開きます。

const character = await Vrm.spawn("my-mod:character");
const eventSource = character.events();

// 状態変更:idle、drag、sitting など
eventSource.on("state-change", async (e) => {
console.log("New state:", e.state);
});

// ポインタインタラクション
eventSource.on("pointer-click", (e) => {
console.log(`Clicked at (${e.globalViewport[0]}, ${e.globalViewport[1]}), button: ${e.button}`);
});

// ドラッグイベント
eventSource.on("drag-start", (e) => console.log("Drag started"));
eventSource.on("drag", (e) => console.log(`Dragging, delta: ${e.delta}`));
eventSource.on("drag-end", (e) => console.log("Drag ended"));

// ホバーイベント
eventSource.on("pointer-over", (e) => console.log("Mouse entered character"));
eventSource.on("pointer-out", (e) => console.log("Mouse left character"));

// アニメーションイベント
eventSource.on("vrma-play", (e) => console.log("Animation started:", e.state));
eventSource.on("vrma-finish", (e) => console.log("Animation finished:", e.state));

// ペルソナ変更
eventSource.on("persona-change", (e) => {
console.log("Persona updated:", e.persona.profile);
});

// 完了したらイベントストリームを閉じる
eventSource.close();

VrmEventSourceDisposable を実装しているため、TypeScript 5.2+ の using で使用できます:

using eventSource = character.events();
eventSource.on("state-change", (e) => { /* ... */ });

利用可能なイベント

イベントペイロード説明
state-change{ state: string }キャラクターの状態が変更(idle、drag、sitting など)
expression-change{ state: string }表情が変更
vrma-play{ state: string }VRMA アニメーションの再生開始
vrma-finish{ state: string }VRMA アニメーションの完了
pointer-click{ globalViewport, button }キャラクターがクリックされた
pointer-press{ globalViewport, button }キャラクター上でマウスボタンが押された
pointer-release{ globalViewport, button }キャラクター上でマウスボタンが離された
pointer-over{ globalViewport }マウスがキャラクター領域に入った
pointer-out{ globalViewport }マウスがキャラクター領域を離れた
pointer-move{ globalViewport }キャラクター領域内でマウスが移動した
pointer-cancel{ globalViewport }ポインタインタラクションがキャンセルされた
drag-start{ globalViewport }ドラッグ開始
drag{ globalViewport, delta }ドラッグ中(カーソルの差分を含む)
drag-end{ globalViewport }ドラッグ終了
persona-change{ persona }ペルソナデータが更新された

主要な API

ライフサイクル

メソッド説明
Vrm.spawn(asset, options?)MOD アセット ID から新しい VRM をスポーンします。Vrm インスタンスを返します。
Vrm.findByName(name)モデル名で VRM を検索します。見つからない場合はスローします。
Vrm.waitLoadByName(name)VRM の読み込み完了を待ってから返します。
Vrm.findAll()読み込み済みのすべての VRM インスタンスを Vrm[] として取得します。
Vrm.findAllEntities()読み込み済みのすべての VRM エンティティ ID を number[] として取得します。
Vrm.findAllDetailed()すべての VRM の詳細スナップショット(状態、トランスフォーム、表情、アニメーション、ペルソナ)を取得します。
Vrm.stream(callback)既存および今後作成される VRM インスタンスをストリームします。
vrm.despawn()この VRM をシーンから削除します。

アニメーション

メソッド説明
vrm.playVrma(options)リピート、トランジション、完了オプション付きで VRMA アニメーションを再生します。
vrm.stopVrma(asset)アセット ID で指定した VRMA アニメーションを停止します。
vrm.listVrma()この VRM にアタッチされたすべての VRMA アニメーションを一覧表示します。
vrm.vrmaState(asset)特定のアニメーションの再生状態(再生中、速度、経過時間)を取得します。
vrm.setVrmaSpeed(asset, speed)アニメーションの再生速度を変更します。

表情

メソッド説明
vrm.expressions()すべての表情とその現在のウェイトを取得します。
vrm.setExpressions(weights)表情のウェイトを設定し、以前のすべてのオーバーライドを置換します。
vrm.modifyExpressions(weights)表情のウェイトを部分的に更新します(他のオーバーライドは維持)。
vrm.clearExpressions()すべての表情オーバーライドをクリアし、VRMA アニメーションに制御を戻します。
vrm.modifyMouth(weights)リップシンク用の口の表情を設定します(口以外のオーバーライドは維持)。

視線追従(Look-At)

メソッド説明
vrm.lookAtCursor()キャラクターの目をマウスカーソルに追従させます。
vrm.lookAtTarget(entity)キャラクターの目を特定のエンティティに向けます。
vrm.unlook()視線追従動作を無効にします。

スピーチ

メソッド説明
vrm.speakWithTimeline(audio, keyframes, options?)WAV オーディオをフレーム同期された表情キーフレームで再生し、リップシンクを行います。

状態と位置

メソッド説明
vrm.state()現在の状態文字列を取得します(例:"idle"、"drag"、"sitting")。
vrm.setState(state)キャラクターの状態を設定します。
vrm.position()画面座標(globalViewport)とワールド座標の両方で位置を取得します。
vrm.name()VRM モデル名を取得します。

ペルソナ

メソッド説明
vrm.persona()キャラクターのペルソナ(プロフィール、性格、OCEAN 特性、メタデータ)を取得します。
vrm.setPersona(persona)キャラクターのペルソナデータを設定します。

物理

メソッド説明
vrm.springBones()すべてのスプリングボーン(spring bone)チェーン(髪、衣服の物理)を取得します。
vrm.springBone(chainId)エンティティ ID で特定のスプリングボーンチェーンを取得します。
vrm.setSpringBone(chainId, props)スプリングボーンの物理プロパティ(剛性、抵抗、重力)を更新します。

イベント

メソッド説明
vrm.events()リアルタイムイベントストリーミング用の VrmEventSource を開きます。
vrm.findBoneEntity(bone)名前付きボーン(例:"head"、"leftHand")のエンティティ ID を取得します。

完全な使用例

以下は @hmcs/elmer MOD の完全なサービスコードです。キャラクターのスポーン、状態に基づくアニメーション再生、カーソル追従を示しています。

import { type TransformArgs, Vrm, preferences, repeat } from "@hmcs/sdk";

// プリファレンスからキャラクターの最後の位置を読み込む
const transform = await preferences.load<TransformArgs>("transform::elmer:vrm");

// VRM アセットを使用して Elmer キャラクターをスポーン
const elmer = await Vrm.spawn("elmer:vrm", {
transform,
});

// 非同期ディレイのヘルパー
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// 共通のアニメーションオプション:0.5秒のクロスフェードで永続ループ
const option = {
repeat: repeat.forever(),
transitionSecs: 0.5,
} as const;

// アイドルアニメーションで開始
await elmer.playVrma({
asset: "vrma:idle-maid",
...option,
});

// キャラクターの状態変更に反応
elmer.events().on("state-change", async (e) => {
if (e.state === "idle") {
// アイドル時:アイドルアニメーションを再生し、カーソルを追従
await elmer.playVrma({
asset: "vrma:idle-maid",
...option,
});
await sleep(500);
await elmer.lookAtCursor();
} else if (e.state === "drag") {
// ドラッグ中:カーソル追従を停止し、つかまれアニメーションを再生
await elmer.unlook();
await elmer.playVrma({
asset: "vrma:grabbed",
...option,
resetSpringBones: true,
});
} else if (e.state === "sitting") {
// 座っている時:座りアニメーションを再生
await elmer.playVrma({
asset: "vrma:idle-sitting",
...option,
});
await sleep(500);
await elmer.lookAtCursor();
}
});

次のステップ

  • SDK 概要 -- すべての SDK モジュールとその説明の完全なリスト。