モーションについて
最終更新: 2023年1月26日
モーションを再生するまでに必要なクラス
1、モーションデータを保持し、モデルに対して操作するクラス
a.インスタンスの作成(motion3.jsonファイルの読み込み)
b.モーションの再生方法についての設定
c.インスタンスの破棄
2、モーション管理クラス
a.インスタンスの作成
b.モーション再生
c.モデルのパラメータ更新
d.モーションの終了
e.ユーザトリガーの受取り
1-a.モーションのインスタンスの作成(.motion3.jsonファイルの読み込み)
モーションの再生にはACubsimMotionクラスから派生したCubismMotionクラスを使用します。
モーションに利用するデータは拡張子が「.motion3.json」となっているモーションファイルです。
Cubism2.1の「.mtn」は使用できません。
この.motion3.jsonファイルの読み込みには以下のいずれかの関数を利用します。
– Native(C++)のCubismMotion::Create関数
– Web(TypeScript)のCubismMotion.create関数
– JavaのCubismMotion.create関数
jsonファイルを一度ロードしてからバッファとサイズを渡してロードします。
SDK for Javaの場合はバッファのみをメソッドに渡します。
// C++ csmString path = "example.motion3.json"; csmByte* buffer; csmSizeInt size; buffer = CreateBuffer(path.GetRawString(), &size); CubismMotion* motion = CubismMotion::Create(buffer, size);
// TypeScript let path: string = "example.motion3.json"; fetch(path).then( (response) => { return response.arrayBuffer(); } ).then( (arrayBuffer) => { let buffer: ArrayBuffer = arrayBuffer; let size = buffer.byteLength; let motion: CubismMotion = CubismMotion.create(buffer, size); } );
// Java String path = "example.motion3.json"; byte[] buffer = createBuffer(path); CubismMotion motion = loadMotion(buffer);
1-b.モーションファイルごとの再生方法設定
モーションには主に以下の項目を設定します。
これらの設定は行わなくても再生することができます。
モーション開始時のフェードインの時間:
設定は以下のいずれかの関数で行います。
– Native(C++)のACubismMotion::SetFadeInTime関数
– Web(TypeScript)のACubismMotion.setFadeInTime関数
– JavaのACubismMotion.setFadeInTime関数
以下のいずれかの関数で取得できます。
– Native(C++)のACubismMotion::GetFadeInTime関数
– Web(TypeScript)のACubismMotion.getFadeInTime関数
– JavaのACubismMotion.getFadeInTime関数
フェードインの時間を秒で指定します。
モーション終了時のフェードアウトの時間:
設定は以下のいずれかの関数で行います。
– Native(C++)のACubismMotion::SetFadeOutTime関数
– Web(TypeScript)のACubismMotion.setFadeOutTime関数
– JavaのACubismMotion.setFadeOutTime関数
以下のいずれかの関数で取得できます。
– Native(C++)のACubismMotion::GetFadeOutTime関数
– Web(TypeScript)のACubismMotion.getFadeOutTime関数
– JavaのACubismMotion.getFadeOutTime関数
フェードアウトの時間を秒で指定します。
ループ再生のON/OFF:
設定は以下のいずれかの関数で行います。
– Native(C++)のvoid CubismMotion::IsLoop(csmBool loop)関数
– Web(TypeScript)のCubismMotion.setIsLoop関数
– JavaのCubismMotion.isLoop関数
以下のいずれかの関数で現在の値を取得できます。
– Native(C++)のcsmBool CubismMotion::IsLoop()関数
– Web(TypeScript)のCubismMotion.isLoop関数
– JavaのCubismMotion.isLoop関数
trueを設定すると終了時に最初から再生します。
他のモーションが割り込むか終了命令が呼ばれるまで無限にループ再生し続けます。
設定しない場合の初期値はfalse(ループしない)です。
※Frameworkのループの動作はエディターのループ動作と完全な一致を保証していません。
※現在Animatorはmotion3.jsonファイルにループ設定を反映させられないため、
Frameworkはmotion3.jsonファイルのループ設定を無視してfalseを設定します。
設定例 (※これらの設定はモーションの再生前に行ってください)
// C++ motion->SetFadeInTime( 1.0f ); motion->SetFadeOutTime( 1.0f ); motion->IsLoop( true );
// TypeScript motion.setFadeInTime(1.0); motion.setFadeOutTime(1.0); motion.setIsLoop(true);
// Java motion.setFadeInTime(1.0f); motion.setFadeOutTime(1.0f); motion.isLoop(true);
1-c.インスタンスの破棄
// C++ ACubismMotion::Delete(motion);
Cubism SDK for Web及びJavaでは明示的に破棄する必要はありません。
モーションのフェード値をファイルから設定する方法については
A、.motion3.jsonファイルに全体値として設定する方法(全体のフェード)
B、.motion3.jsonファイルにパラメータ個別の値として設定する方法(パラメータフェード)
C、.model3.jsonファイルに全体値として設定する方法
この3つがあり、優先度はB、C、Aの順で適用されます。
いずれの指定もない場合はデフォルト値の1秒が設定されます。
2-a.モーション管理クラスのインスタンスの作成
前項で作成したCubismMotionクラスのインスタンスをモデルに適用する(アニメーションさせる)ためには、 CubismMotionManagerクラスを使用します。
// C++ CubismMotionManager* motionManager = CSM_NEW CubismMotionManager();
// TypeScript let motionManager: CubismMotionManager = new CubismMotionManager();
// Java CubismMotionManager motionManager = new CubismMotionManager();
2-b.モーションの再生
モーションの再生には以下のいずれかの関数を使用します。
– Native(C++)のCubismMotionManager::StartMotionPriority関
– Web(TypeScript)のCubismMotionManager.startMotionPriority関数
– JavaのCubismMotionManager.startMotionPriority関数
第一引数:ACubismMotionインスタンス、モーションデータ
モーションデータのインスタンスを渡します。
この引数にはACubismMotionの派生インスタンスであるCubismMotionとCubismExpressionMotionのインスタンスが指定できます。
一般的に一つのモーションマネージャが取り扱うインスタンスタイプは一方だけにします。
第二引数:Boolean、自動削除のフラグ
再生が終了したときに自動的にモーションのデータを削除するかどうかのフラグです。
一度だけ再生されるようなモーションに使用します。
第三引数:Int、優先度
CubismMotionManagerで管理する再生の際の優先度設定を指定します。
優先度による再生拒否はCubismMotionManagerの外部で行う必要があります。
優先度に関してはページ下部にあるCubismMotionManagerの項目をご覧ください。
// C++ csmBool autoDelete = true; csmInt32 priority = PriorityNormal;// 2 motionManager->StartMotionPriority( motion, autoDelete, priority);
// TypeScript let autoDelete: boolean = true; let priority: number = priorityNormal; // 2 motionManager.startmotionPriority(motion, autoDelete, priority);
// Java boolean autoDelete = true; int priority = LAppDefine.Priority.NORMAL; // 2 motionManager.startMotionPriority(motion, autoDelete, priority);
モーションを複数同時に再生させたい場合は、CubismMotionManagerインスタンスを増やしてください。
これは右手と左手のモーションを別々に制御するなどのことに使えます。
モーションを同時に再生する場合は、できるだけ同じパラメータについては設定しないでください。
その場合最後に更新したモーションのパラメータが有効になります。
また、フェードが綺麗にかからない場合があります。
2-c.モデルのパラメータ更新
– Native(C++)のCubismMotionManager::StartMotionPriority関数
– Web(TypeScript)のCubismMotionManager.startMotionPriority関数
– JavaのCubismMotionManager.startMotionPriority関数
以上の関数でモーションを再生しただけでは、モデルはアニメーションしません。
現在再生中のモーションのパラメータをモデルに設定するためには、毎描画時に以下のいずれかの関数を呼び出します。
– Native(C++)のCubismMotionManager::UpdateMotion関数
– Web(TypeScript)のCubismMotionManager.updateMotion関数
– JavaのCubismMotionManager.updateMotion関数
第一引数:CubismModelインスタンス、モーションを適用するモデル
パラメータ情報の取得、操作のみに使用されます。
第二引数:Float、前回実行時からの差分時間
Updateなどで計算される差分時間を入力します。
Float実数で秒単位で入力します。 60FPSでの実行ならば1/60秒で0.016を、30FPSならば1/30秒で0.03を入力します。
// C++ motionUpdated = motionManager->UpdateMotion(model, deltaTimeSeconds);
// TypeScript motionUpdated = motionManager.updateMoiton(model, deltaTimeSeconds);
// Java isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds);
入力するdeltaTimeSecondsを調整することでスローや停止、早送りをすることができます。
ただし、マイナスの値を使った逆再生は考慮の範囲外の設計になっています。
// C++ const csmFloat32 playSpeed = pow(2, (csmFloat32)_motionSpeed / 10.0); motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds * playSpeed); // モーションを更新
// TypeScript let playSpeed:number = Math.pow(2, _motionSpeed / 10.0); motionUpdated = motionManager.updateMoiton(model, deltaTimeSeconds);
// Java final float playSpeed = Math.pow(2, (float)motionSpeed / 10.0f); isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds * playSpeed); // モーションを更新
2-d.モーションの終了
モーションは再生時間を過ぎたときに自動で終了しますが
任意のタイミングで終了したいときは以下のいずれかの関数を使用します。
– Native(C++)のCubismMotionQueueManager::StopAllMotions関数
– Web(TypeScript)のCubismMotionQueueManager.stopAllMotions関数
– JavaのCubismMotionQueueManager.stopAllMotions関数
フェードの途中などで、同時に2つ以上のモーションを再生している場合、それらすべてを終了します。
// C++ motionManager->StopAllMotions();
// TypeScript motionManager.stopAllMotions();
// Java motionManager.stopAllMotions();
2-e.イベントの受取り
モーションに設定された「イベント」が再生されたときに、CubismMotionQueueManagerに以下のいずれかの関数で登録したコールバックで呼び出しを受けることができます。
– Native(C++)のSetUserTriggerCallback関数
– Web(TypeScript)のsetUserTriggerCallback関数
– JavaのsetUserTriggerCallback関数
登録できるコールバックは1つのみです。
インスタンスの関数を呼び出したい時にはstaticな関数をインスタンスのポインタと一緒に登録し、
staticな関数から登録しておいたインスタンスのポインタを使って目的の関数を呼び出すようにしてください。
複数の動作をさせたい時には一つのコールバックから順に呼び出すようにしてください。
//C++ /** * @brief ユーザトリガーのコールバック関数定義 * * ユーザトリガーのコールバックに登録できる関数の型情報 * * @param[in] caller 発火したユーザトリガーを再生させたCubismMotionQueueManager * @param[in] userTriggerValue 発火したユーザトリガーの文字列データ * @param[in] customData コールバックに返される登録時に指定されたデータ */ typedef void(*UserTriggerFunction)(const CubismMotionQueueManager* caller, const csmString& userTriggerValue, void* customData);
// C++ class SampleClass { public: void UserTriggerEventFired(const csmString& userTriggerValue) { //処理 } static void SampleCallback( const CubismMotionQueueManager* caller, const csmString& userTriggerValue, void* customData) { SampleClass* sample = reinterpret_cast<SampleClass*>(customData); if (sample != NULL) { sample->UserTriggerEventFired(userTriggerValue); } } }; SampleClass sampleA; motionManager->SetUserTriggerCallback(SampleClass::SampleCallback, &sampleA);
// TypeScript /** * ユーザトリガーのコールバックに登録できる関数の型情報 * * @param caller 発火したユーザトリガーを再生させたCubismMotionQueueManager * @param userTriggerValue 発火したユーザートリガーの文字列データ * @param customData コールバックに返される登録時に指定されたデータ */ export interface UserTriggerFunction { ( caller: CubismmotionQueueManager, userTriggerValue: string, customData: any ): void; }
// TypeScript class SampleClass { public userTriggerEventFired(userTriggerValue): void { // 処理 } public static sampleCallback(caller: CubismMotionQueueManager, userTriggerValue: string, customData: any): void { let sample: SampleClass = <SampleClass>customData; if(sample != null) { sample.userTriggerEventFired(userTriggerValue); } } }; let sampleA: SampleClass = new SampleClass(); motionManager.setUserTriggerCallback(SampleClass.sampleCallback, sampleA);
// Java public interface ICubismMotionEventFunction { public void apply( CubismMotionQueueManager caller, String eventValue, Object customData); }
// Java public class SampleClass { public void userTriggerEventFired(final String userTriggerValue) { // 処理 } public static class SampleCallback implements ICubismMotionEventFunction { @Override public void apply( CubismMotionQueueManager caller, String eventValue, Object customData) { if (customData != null) { ((CubismUserModel) customData).motionEventFired(eventValue); } } }
CubismUserModelクラスには標準でこの仕組みが組み込まれています。
– Native(C++)の (CubismUserModel::MotionEventFired関数、CubismUserModel::CubismDefaultMotionEventCallback関数)
– Web(TypeScript)の (CubismUserModel.cubismDefaultMotionEventCallback関数)
– Javaの (CubismUserModel.motionEventFired関数、CubismUserModel.cubismDefaultMotionEventCallback変数)
モーションの自動削除
Native(C++)のCubismMotionQueueManager::StartMotion関数、またはWeb(TypeScript)のCubismMotionQueueManager.startMotion関数の呼び出し時に第二引数のautoDeleteにtrueを入れると、
モーションが再生終了するときにCubismMotionQueueEntryの削除とともにモーションも削除されます。
一度のみ再生されるファイルに対して使用されることを想定しています。
// C++ csmBool CubismMotionQueueManager::DoUpdateMotion(CubismModel* model, csmFloat32 userTimeSeconds) { csmBool updated = false; // ------- 処理を行う -------- // 既にモーションがあれば終了フラグを立てる for (csmVector<CubismMotionQueueEntry*>::iterator ite = _motions.Begin(); ite != _motions.End();) { CubismMotionQueueEntry* motionQueueEntry = *ite; /*省略*/ // ----- 終了済みの処理があれば削除する ------ if (motionQueueEntry->IsFinished()) { CSM_DELETE(motionQueueEntry); ite = _motions.Erase(ite); // 削除 } else { ++ite; } } return updated; }
// C++ CubismMotionQueueEntry::~CubismMotionQueueEntry() { if (_autoDelete && _motion) { ACubismMotion::Delete(_motion); } }
// TypeScript /** * モーションを更新して、モデルにパラメータ値を反映する。 * * @param model 対象のモデル * @param userTimeSeconds デルタ時間の積算値[秒] * @return true モデルへパラメータ値の反映あり * @return false モデルへパラメータ値の反映なし(モーションの変化なし) */ public doUpdateMotion(model: CubismModel, userTimeSeconds: number): boolean { let updated: boolean = false; // ------- 処理を行う -------- // 既にモーションがあれば終了フラグを立てる for(let ite: iterator<CubismMotionQueueEntry> = this._motions.begin(); ite.notEqual(this._motions.end());) { let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); /*省略*/ // ------ 終了済みの処理があれば削除する ------ if(motionQueueEntry.isFinished()) { motionQueueEntry.release(); motionQueueEntry = void 0; motionQueueEntry = null; ite = this._motions.erase(ite); // 削除 } else { ite.preIncrement(); } } return updated; }
// TypeScript /** * デストラクタ相当の処理 */ public release(): void { if(this._autoDelete && this._motion) { ACubismMotion.delete(this._motion); // } }
SDK for JavaではCubismMotionQueueEntryへの参照がなくなった時点で、そのフィールドであるモーションインスタンスも自動的に削除されるため、モーションの自動削除のフラグは不要です。
CubismMotionQueueManagerクラスとCubismMotionManagerクラス
モーションの管理クラスにはCubismMotionQueueManagerクラスと、
CubismMotionQueueManagerクラスを継承したCubismMotionManagerクラスがあります。
CubismMotionQueueManager
CubismMotionQueueManagerはモーションの値の適用度合いにフェードが効いた切り替えを担当します。
以下のいずれかの関数でモーション再生追加時にすでに再生しているモーション群に対してStartFadeoutによって終了時間の前倒しを行います。
– Native(C++)のLAppModel::StartMotion関数
– Web(TypeScript)のLAppModel.startMotion関数
– JavaのLAppModel.startMotion関数
この操作によって再生中のモーションがフェードをもって新しいモーションとの切り替わりを実現します。
// C++ CubismMotionQueueEntryHandle CubismMotionQueueManager::StartMotion(ACubismMotion* motion, csmBool autoDelete, csmFloat32 userTimeSeconds) { if (motion == NULL) { return InvalidMotionQueueEntryHandleValue; } CubismMotionQueueEntry* motionQueueEntry = NULL; // 既にモーションがあれば終了フラグを立てる for (csmUint32 i = 0; i < _motions.GetSize(); ++i) { motionQueueEntry = _motions.At(i); if (motionQueueEntry == NULL) { continue; } motionQueueEntry->StartFadeout(motionQueueEntry->_motion->GetFadeOutTime(), userTimeSeconds); //フェードアウトを開始し終了する } motionQueueEntry = CSM_NEW CubismMotionQueueEntry(); // 終了時に破棄する motionQueueEntry->_autoDelete = autoDelete; motionQueueEntry->_motion = motion; _motions.PushBack(motionQueueEntry, false); return motionQueueEntry->_motionQueueEntryHandle; }
// TypeScript /** * 指定したモーションの開始 * * 指定したモーションを開始する。同じタイプのモーションが既にある場合は、既存のモーションに終了フラグを立て、フェードアウトを開始させる。 * * @param motion 開始するモーション * @param autoDelete 再生が終了したモーションのインスタンスを削除するなら true * @param userTimeSeconds デルタ時間の積算値[秒] * @return 開始したモーションの識別番号を返す。個別のモーションが終了したか否かを判定するIsFinished()の引数で使用する。開始できない時は「-1」 */ public startMotion(motion: ACubismMotion, autoDelete: boolean, userTimeSeconds: number) : CubismMotionQueueEntryHandle { if(motion == null) { return InvalidMotionQueueEntryHandleValue; } let motionQueueEntry: CubismMotionQueueEntry = null; // 既にモーションがあれば終了フラグを立てる for(let i: number = 0; i < this._motions.getSize(); ++i) { motionQueueEntry = this._motions.at(i); if(motionQueueEntry == null) { continue; } motionQueueEntry.startFadeout(motionQueueEntry._motion.getFadeOutTime(), userTimeSeconds); // フェードアウトを開始し終了する } motionQueueEntry = new CubismMotionQueueEntry(); // 終了時に破棄する motionQueueEntry._autoDelete = autoDelete; motionQueueEntry._motion = motion; this._motions.pushBack(motionQueueEntry); return motionQueueEntry._motionQueueEntryHandle; }
// Java public int startMotion(ACubismMotion motion, float userTimeSeconds) { if (motion == null) { return -1; } // 既にモーションがあれば終了フラグを立てる for (CubismMotionQueueEntry entry : motions){ if (entry == null) { continue; } entry.setFadeOut(entry.getMotion().getFadeOutTime()); } CubismMotionQueueEntry motionQueueEntry = new CubismMotionQueueEntry(); motionQueueEntry.setMotion(motion); motions.add(motionQueueEntry); return System.identityHashCode(motionQueueEntry); }
CubismMotionManager
CubismMotionManagerクラスは再生するモーションの優先度を保存する機能と、これから再生する予定の優先度を整数として登録する機能があります。
この記録された優先度と比較することで優先度の低いモーション再生を規制する機能を作成することを想定しています。
再生規制をする部分に関してはCubismMotionManagerの外部に用意する必要があります。
以下のいずれかの関数は非同期スレッドからの再生に対応しています。
– Native(C++)のLAppModel::StartMotion関数
– Web(TypeScript)のLAppModel.startMotion関数
– JavaのLAppModel.startMotion関数
関数の冒頭で再生の優先をSetReservePriorityやReserveMotionによって登録されます。
次に読み込みが実行されますが、非同期でこの関数を呼ばれた場合冒頭で優先度を登録されるので、
読み込み中に他のスレッドで他の低優先度の再生は規制される仕組みになっています。
再生終了時は優先度が0に設定されるのは固定で、その他の制御は外部に任されます。
// C++ CubismMotionQueueEntryHandle LAppModel::StartMotion(const csmChar* group, csmInt32 no, csmInt32 priority) { if (priority == PriorityForce) { _motionManager->SetReservePriority(priority); } else if (!_motionManager->ReserveMotion(priority)) { if (_debugMode) { LAppPal::PrintLog("[APP]can't start motion."); } return InvalidMotionQueueEntryHandleValue; } /*モーションデータ準備部分省略*/ return _motionManager->StartMotionPriority(motion, autoDelete, priority); }
// TypeScript /** * 引数で指定したモーションの再生を開始する * @param group モーショングループ名 * @param no グループ内の番号 * @param priority 優先度 * @return 開始したモーションの識別番号を返す。 * 個別のモーションが終了したか否かを判定するisFinished()の引数で使用する。 * 開始できない時は[-1] */ public startMotion(group: string, no: number, priority: number) : CubismMotionQueueEntryHandle { if(priority == LAppDefine.PriorityForce) { this._motionManager.setReservePriority(priority); } else if(!this._motionManager.reserveMotion(priority)) { if(this._debugMode) { LAppPal.printLog("[APP]can't start motion."); } return InvalidMotionQueueEntryHandleValue; } /*モーションデータ準備部分省略*/ return this._motionManager.startMotionPriority(motion, autoDelete, priority); }
// Java public int startMotion(final String group, int number, int priority, IFinishedMotionCallback onFinishedMotionHandler ) { if (priority == LAppDefine.Priority.FORCE.getPriority()) { motionManager.setReservationPriority(priority); } else if (!motionManager.reserveMotion(priority)) { if (debugMode) { LAppPal.printLog("Cannot start motion."); } return -1; } /*モーションデータ準備部分省略*/ if (motionManager.startMotionPriority(motion, priority) != -1) { return motionManager.startMotionPriority(motion, priority); } return -1; }