モーションについて
[最終更新日:2019/09/03]
モーションを再生するまでに必要なクラス
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関数を利用します。
jsonファイルを一度ロードしてからバッファとサイズを渡してロードします。
1 2 3 4 5 6 |
// C++ csmString path = "example.motion3.json"; csmByte* buffer; csmSizeInt size; buffer = CreateBuffer(path.GetRawString(), &size); CubismMotion* motion = CubismMotion::Create(buffer, size); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 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); } ); |
1-b.モーションファイルごとの再生方法設定
モーションには主に以下の項目を設定します。
これらの設定は行わなくても再生することができます。
* モーション開始時のフェードインの時間
Native(C++)のACubismMotion::SetFadeInTime関数、またはWeb(TypeScript)のACubismMotion.setFadeInTime関数で設定、
Native(C++)のACubismMotion::GetFadeInTime関数、またはWeb(TypeScript)のACubismMotion.getFadeInTime関数で取得できます。
フェードインの時間を秒で指定します。
* モーション終了時のフェードアウトの時間
Native(C++)のACubismMotion::SetFadeOutTime関数、またはWeb(TypeScript)のACubismMotion.setFadeOutTime関数で設定、
Native(C++)のACubismMotion::GetFadeOutTime関数、またはWeb(TypeScript)のACubismMoiton.getFadeOutTime関数で取得できます。
フェードアウトの時間を秒で指定します。
* ループ再生のON/OFF
Native(C++)のvoid CubismMotion::IsLoop(csmBool loop)関数、またはWeb(TypeScript)のCubismMotion.setIsLoop関数で設定、
Native(C++)のcsmBool CubismMotion::IsLoop()関数、またはWeb(TypeScript)のCubismMotion.isLoop関数で現在の値を取得できます。
trueを設定すると終了時に最初から再生します。
他のモーションが割り込むか終了命令が呼ばれるまで無限にループ再生し続けます。
設定しない場合の初期値はfalse(ループしない)です。
※Frameworkのループの動作はエディターのループ動作と完全な一致を保証していません。
※現在Animatorはmotion3.jsonファイルにループ設定を反映させられないため、
Frameworkはmotion3.jsonファイルのループ設定を無視してfalseを設定します。
設定例 (※これらの設定はモーションの再生前に行ってください)
1 2 3 4 |
// C++ motion->SetFadeInTime( 1.0f ); motion->SetFadeOutTime( 1.0f ); motion->IsLoop( true ); |
1 2 3 4 |
// TypeScript motion.setFadeInTime(1.0); motion.setFadeOutTime(1.0); motion.setIsLoop(true); |
1-c.インスタンスの破棄
12
// C++ ACubismMotion::Delete(motion);
1 2 |
// C++ ACubismMotion::Delete(motion); |
Cubism SDK for Webでは明示的に破棄する必要はありません。
モーションのフェー��値をファイルから設定する方法については
A、.motion3.jsonファイルに全体値として設定する方法(パラメータフェード)
B、.motion3.jsonファイルにパラメータ個別の値として設定する方法(全体のフェード)
C、.model3.jsonファイルに全体値として設定する方法
この3つがあり、優先度はB、C、Aの順で適用されます。
いずれの指定もない場合はデフォルト値の1秒が設定されます。
2-a.モーション管理クラスのインスタンスの作成
前項で作成したCubismMotionクラスのインスタンスをモデルに適用する(アニメーションさせる)ためには、 CubismMotionManagerクラスを使用します。
1 2 |
// C++ CubismMotionManager* motionManager = CSM_NEW CubismMotionManager(); |
1 2 |
// TypeScript let motionManager: CubismMotionManager = new CubismMotionManager(); |
2-b.モーションの再生
モーションの再生にはNative(C++)のCubismMotionManager::StartMotionPriority関数、またはWeb(TypeScript)のCubismMotionManager.startMotionPriority関数を使用します。
第一引数:ACubismMotionインスタンス、モーションデータ
モーションデータのインスタンスを渡します。
この引数にはACubismMotionの派生インスタンスであるCubismMotionとCubismExpressionMotionのインスタンスが指定できます。
一般的に一つのモーションマネージャが取り扱うインスタンスタイプは一方だけにします。
第二引数:Boolen、自動削除のフラグ
再生が終了したときに自動的にモーションのデータを削除するかどうかのフラグです。
一度だけ再生されるようなモーションに使用します。
第三引数:Int、優先度
CubismMotionManagerで管理する再生の際の優先度設定を指定します。
優先度による再生拒否はCubismMotionManagerの外部で行う必要があります。
優先度に関してはページ下部にあるCubismMotionManagerの項目をごらんください。
1 2 3 4 |
// C++ csmBool autoDelete = true; csmInt32 priority = PriorityNormal;// 2 motionManager->StartMotionPriority( motion, autoDelete, priority); |
1 2 3 4 |
// TypeScript let autoDelete: boolean = true; let priority: number = priorityNormal; // 2 motionManager.startmotionPriority(motion, autoDelete, priority); |
モーションを複数同時に再生させたい場合は、CubismMotionManagerインスタンスを増やしてください。
これは右手と左手のモーションを別々に制御す��などのことに使えます。
モーションを同時に再生する場合は、できるだけ同じパラメータについては設定しないでください。
その場合最後に更新したモーションのパラメータが有効になります。
また、フェードが綺麗にかからない場合があります。
2-c.モデルのパラメータ更新
Native(C++)のCubismMotionManager::StartMotionPriority関数、またはWeb(TypeScript)のCubismMotionManager.startMotionPriority関数で
モーションを再生しただけでは、モデルはアニメーションしません。
現在再生中のモーションのパラメータをモデルに設定するためには、
毎描画時にNative(C++)のCubismMotionManager::UpdateMotion関数、またはWeb(TypeScript)のCubismMotionmanager.updateMotion関数を呼び出します。
第一引数:CubismModelインスタンス、モーションを適用するモデル
パラメータ情報の取得、操作のみに使用されます。
第二引数:Float、前回実行時からの差分時間
Updateなどで計算される差分時間を入力します。
Float実数で秒単位で入力します。 60FPSでの実行ならば1/60秒で0.016を、30FPSならば1/30秒で0.03を入力します。
1 2 |
// C++ motionUpdated = motionManager->UpdateMotion(model, deltaTimeSeconds); |
1 2 |
// TypeScript motionUpdated = motionManager.updateMoiton(model, deltaTimeSeconds); |
入力するdeltaTimeSecondsを調整することでスローや停止、早送りをすることができます。
ただし、マイナスの値を使った逆再生は考慮の範囲外の設計になっています。
1 2 3 |
// C++ const csmFloat32 playSpeed = pow(2, (csmFloat32)_motionSpeed / 10.0); motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds * playSpeed); // モーションを更新 |
1 2 3 |
// TypeScript let playSpeed:number = Math.pow(2, _motionSpeed / 10.0); motionUpdated = motionManager.updateMoiton(model, deltaTimeSeconds); |
2-d.モーションの終了
モーションは再生時間を過ぎたときに自動で終了しますが
任意のタイミングで終了したいときはNative(C++)のCubismMotionQueueManager::StopAllMotions関数、
またはWeb(TypeScript)のCubismMotionQueueManager.stopAllMotions関数を使用します。
フェードの途中などで、同時に2つ以上のモーションを再生している場合、それらすべてを終了します。
1 2 |
// C++ motionManager->StopAllMotions(); |
1 2 |
// TypeScript motionManager.stopAllMotions(); |
2-e.イベントの受取り
モーションに設定された「イベント」が再生されたときに、
CubismMotionQueueManagerにNative(C++)のSetUserTriggerCallback関数、
またはWeb(TypeScript)のsetUserTriggerCallback関数で登録したコールバックで呼び出しを受けることができます。
登録できるコールバックは1つのみです。
インスタンスの関数を呼び出したい時にはstaticな関数をインスタンスのポインタと一緒に登録し、
staticな関数から登録しておいたインスタンスのポインタを使って目的の関数を呼び出すようにしてください。
複数の動作をさせたい時には一つのコールバックから順に���び出すようにしてください。
1 2 3 4 5 6 7 8 9 10 11 |
//C++ /** * @brief ユーザトリガーのコールバック関数定義 * * ユーザトリガーのコールバックに登録できる関数の型情報 * * @param[in] caller 発火したユーザトリガーを再生させたCubismMotionQueueManager * @param[in] userTriggerValue 発火したユーザトリガーの文字列データ * @param[in] customData コールバックに返される登録時に指定されたデータ */ typedef void(*UserTriggerFunction)(const CubismMotionQueueManager* caller, const csmString& userTriggerValue, void* customData); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 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); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// TypeScript /** * ユーザトリガーのコールバックに登録できる関数の型情報 * * @param caller 発火したユーザトリガーを再生させたCubismMotionQueueManager * @param userTriggerValue 発火したユーザートリガーの文字列データ * @param customData コールバックに返される登録時に指定されたデータ */ export interface UserTriggerFunction { ( caller: CubismmotionQueueManager, userTriggerValue: string, customData: any ): void; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// 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); |
CubismUserModelクラスには標準でこの仕組みが組み込まれています。
Native(C++)の(CubismUserModel::UserTriggerEventFired関数、CubismUserModel::CubismDefaultUserTriggerCallback関数)
またはWeb(TypeScript)の(CubismUserModel.cubismDefaultUserTriggerCallback関数)
モーションの自動削除
Native(C++)のCubismMotionQueueManager::StartMotion関数、またはWeb(TypeScript)のCubismMotionQueueManager.startMotion関数の呼び出し時に第二引数のautoDeleteにtrueを入れると、
モーションが再生終了するときにCubismMotionQueueEntryの削除とともにモーションも削除されます。
一度のみ再生されるファイルに対して使用されることを想定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// 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; } |
1 2 3 4 5 6 7 8 |
// C++ CubismMotionQueueEntry::~CubismMotionQueueEntry() { if (_autoDelete && _motion) { ACubismMotion::Delete(_motion); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// 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; } |
1 2 3 4 5 6 7 8 9 10 11 |
// TypeScript /** * デストラクタ相当の処理 */ public release(): void { if(this._autoDelete && this._motion) { ACubismMotion.delete(this._motion); // } } |
CubismMotionQueueManagerクラスとCubismMotionManagerクラス
モーションの管理クラスにはCubismMotionQueueManagerクラスと、
CubismMotionQueueManagerクラスを継承したCubismMotionManagerクラスがあります。
CubismMotionQueueManager
CubismMotionQueueManagerはモーションの値の適用度合いにフェードが効いた切り替えを担当します。
Native(C++)のLAppModel::StartMotion関数、またはWeb(TypeScript)のLAppModel.startMotion関数でモーション再生追加時にすでに再生しているモーション群に対して
StartFadeoutによって終了時間の前倒しを行います。
この操作によって再生中のモーションがフェードをもって新しいモーションとの切り替わりを実現します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// 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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
// 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; } |
CubismMotionManager
CubismMotionManagerクラスは再生するモーションの優先度を保存する機能と、これから再生する予定の優先度を整数として登録する機能があります。
この記録された優先度と比較することで優先度の低いモーション再生を規制する機能を作成することを想定しています。
再生規制をする部分に関してはCubismMotionManagerの外部に用意する必要があります。
Native(C++)のLAppModel::StartMotion関数、またはWeb(TypeScript)のLAppModel.startMotion関数は非同期スレッドからの再生に対応しています。
関数の冒頭で再生の優先をSetReservePriorityやReserveMotionによって登録されます。
次に読み込みが実行されますが、非同期でこの関数を呼ばれた場合冒頭で優先度を登録されるので、
読み込み中に他のスレッドで他の低優先度の再生は規制される仕組みになっています。
再生終了時は優先度が0に設定されるのは固定で、その他の制御は外部に任されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 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); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// 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); } |