About Motion
Updated: 01/26/2023
Classes required before playing back motion
1. Class that holds motion data and operates on the model
a. Create an instance (import motion3.json files)
b. Set a playback method for motion
c. Dispose of instances
2. Class that manages motion
a. Create an instance
b. Playback motion
c. Update model parameters
d. Terminate motion
e. Receive user triggers
1-a. Create an Instance of Motion (Import .motion3.json Files)
The CubismMotion class, derived from the ACubismMotion class, is used for motion playback.
The data used for motion is a motion file with the extension “.motion3.json.”
“.mtn” in Cubism 2.1 cannot be used.
To load the .motion3.json file, use one of the following functions:
– CubismMotion::Create function in Native (C++)
– CubismMotion.create function in Web (TypeScript)
-CubismMotion.create function in Java
Load the JSON file once and then load it passing the buffer and size.
For SDK for Java, only buffers are passed to the method.
// 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. Set a Playback Method for Each Motion File
The main items to be set for motion are as follows.
Playback is possible without these settings.
Fade-in time at start of motion:
Settings can be made using one of the following functions:
– ACubismMotion::SetFadeInTime function in Native (C++)
– ACubismMotion.setFadeInTime function in Web (TypeScript)
– ACubismMotion.setFadeInTime function in Java
It can be obtained with one of the following functions:
– ACubismMotion::GetFadeInTime function in Native (C++)
– ACubismMotion.getFadeInTime function in Web (TypeScript)
– ACubismMotion.getFadeInTime function in Java
Specify the fade-in time in seconds.
Fade-out time at end of motion:
Settings can be made using one of the following functions:
– ACubismMotion::SetFadeOutTime function in Native (C++)
– ACubismMotion.setFadeOutTime function in Web (TypeScript)
– ACubismMotion.setFadeOutTime function in Java
It can be obtained with one of the following functions:
– ACubismMotion::GetFadeOutTime function in Native (C++)
– ACubismMotion.getFadeOutTime function in Web (TypeScript)
– ACubismMotion.getFadeOutTime function in Java
Specify the fade-out time in seconds.
Loop playback ON/OFF:
Settings can be made using one of the following functions:
– void CubismMotion::IsLoop(csmBool loop) function in Native (C++)
– CubismMotion.setIsLoop function in Web (TypeScript)
– CubismMotion.isLoop function in Java
Current values can be obtained with one of the following functions:
– csmBool CubismMotion::IsLoop() function in Native (C++)
– CubismMotion.isLoop function in Web (TypeScript)
– CubismMotion.isLoop function in Java
If set to true, playback starts from the beginning at the end.
It continues to loop indefinitely until another motion interrupts it or the end instruction is called.
If not set, the default value is false (no loop).
Note: Framework’s looping behavior is not guaranteed to be exactly the same as the Editor’s looping behavior.
Note: Currently, Animator is unable to reflect loop settings in the motion3.json file,
so the Framework ignores the loop setting in the motion3.json file and sets it to false.
Setting example (Note: These settings should be made before the motion is played back.)
// 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. Dispose of Instances
// C++ ACubismMotion::Delete(motion);
Cubism SDK for Web and Java do not require explicit disposal.
There are three ways to set motion fade values from a file.
A. Set as an overall value in the .motion3.json file (overall fade)
B. Set as individual parameter values in the .motion3.json file (parameter fade)
C. Set as an overall value in the .model3.json file
The priority of these methods is applied in the order B, C, A.
If none is specified, the default value of 1 second is set.
2-a. Create an Instance of Motion Management Class
To apply (animate) an instance of the CubismMotion class created in the previous section to a model, use the CubismMotionManager class.
// C++ CubismMotionManager* motionManager = CSM_NEW CubismMotionManager();
// TypeScript let motionManager: CubismMotionManager = new CubismMotionManager();
// Java CubismMotionManager motionManager = new CubismMotionManager();
2-b. Play Back Motion
Use one of the following functions to play back motion:
– CubismMotionManager::StartMotionPriority function in Native (C++)
– CubismMotionManager.startMotionPriority function in Web (TypeScript)
– CubismMotionManager.startMotionPriority function in Java
First argument: ACubismMotion instance, motion data
Pass an instance of motion data.
This argument can be instances of CubismMotion and CubismExpressionMotion, which are derived instances of ACubismMotion.
Generally, only one instance type should be handled by a single motion manager.
Second argument: Boolean, flag for automatic deletion
This is a flag indicating whether or not motion data is automatically deleted when playback is finished.
Used for motions that are played back only once.
Third argument: Int, priority
Specifies the priority setting for playback managed by CubismMotionManager.
Denial of playback by priority must be done outside of CubismMotionManager.
Please see the CubismMotionManager section at the bottom of the page regarding priority.
// 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);
If you want to play multiple motions simultaneously, increase the number of CubismMotionManager instances.
This can be used for things such as controlling the motion of the right and left hands separately.
When playing back motions simultaneously, avoid making settings for the same parameter as much as possible.
In this case, the parameters of the last updated motion will take effect.
Also, the fade may not be applied cleanly.
2-c. Update Model Parameters
– CubismMotionManager::StartMotionPriority function in Native (C++)
– CubismMotionManager.startMotionPriority function in Web (TypeScript)
– CubismMotionManager.startMotionPriority function in Java
Simply playing back the motion with the above functions does not animate the model.
To set the parameters of the currently playing motion to the model, call one of the following functions at each drawing:
– CubismMotionManager::UpdateMotion function in Native (C++)
– CubismMotionManager.updateMotion function in Web (TypeScript)
– CubismMotionManager.updateMotion function in Java
First argument: CubismModel instance, model to which motion is applied
Used only to obtain and manipulate parameter information.
Second argument: Float, difference time since last run
Enter the difference time calculated by Update, etc.
Enter a float-type real number in seconds. For example, enter 0.016 (1/60 seconds) for execution at 60FPS and 0.03 (1/30 seconds) for execution at 30FPS.
// C++ motionUpdated = motionManager->UpdateMotion(model, deltaTimeSeconds);
// TypeScript motionUpdated = motionManager.updateMoiton(model, deltaTimeSeconds);
// Java isMotionUpdated = motionManager.updateMotion(model, deltaTimeSeconds);
You can slow, stop, or fast-forward by adjusting the deltaTimeSeconds input.
However, the design does not take into account reverse playback using negative values.
// C++ const csmFloat32 playSpeed = pow(2, (csmFloat32)_motionSpeed / 10.0); motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds * playSpeed); // Update motion.
// 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); // Update motion.
2-d. Terminate Motion
Motion will automatically end when the playback time has expired,
but if you want to terminate at a particular time, you can use one of the following functions:
– CubismMotionQueueManager::StopAllMotions function in Native (C++)
– CubismMotionQueueManager.stopAllMotions function in Web (TypeScript)
– CubismMotionQueueManager.stopAllMotions function in Java
If two or more motions are playing at the same time, such as in the middle of a fade, all of them are terminated.
// C++ motionManager->StopAllMotions();
// TypeScript motionManager.stopAllMotions();
// Java motionManager.stopAllMotions();
2-e. Receive Events
When an “Event” set in motion is played back, it can be called by a callback registered with one of the following functions in the CubismMotionQueueManager.
– SetUserTriggerCallback function in Native (C++)
– setUserTriggerCallback function in Web (TypeScript)
– setUserTriggerCallback function in Java
Only one callback can be registered.
When you want to call a function of an instance, register a static function with an instance pointer,
and use the instance pointer registered from a static function to call the desired function.
If you want to perform multiple actions, call them one after the other, starting with one callback.
//C++ /** * @brief User-triggered callback function definition * * Function type information that can be registered in the callback of the user trigger * * @param[in] caller CubismMotionQueueManager that replayed the fired user trigger * @param[in] userTriggerValue Value Character string data of the user trigger that ignited * @param[in] customData The data specified during registration returned in the callback */ typedef void(*UserTriggerFunction)(const CubismMotionQueueManager* caller, const csmString& userTriggerValue, void* customData);
// C++ class SampleClass { public: void UserTriggerEventFired(const csmString& userTriggerValue) { // Process } 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 /** * Function type information that can be registered in the callback of the user trigger * * @param caller CubismMotionQueueManager that replayed the fired user trigger * @param userTriggerValue Value Character string data of the user trigger that ignited * @param customData The data specified during registration returned in the callback */ export interface UserTriggerFunction { ( caller: CubismmotionQueueManager, userTriggerValue: string, customData: any ): void; }
// TypeScript class SampleClass { public userTriggerEventFired(userTriggerValue): void { // Process } 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) { // Process } public static class SampleCallback implements ICubismMotionEventFunction { @Override public void apply( CubismMotionQueueManager caller, String eventValue, Object customData) { if (customData ! = null) { ((CubismUserModel) customData).motionEventFired(eventValue); } } }
The CubismUserModel class has this mechanism built in by default.
– (CubismUserModel::MotionEventFired function or CubismUserModel::CubismDefaultMotionEventCallback function) in Native (C++)
– (CubismUserModel.cubismDefaultMotionEventCallback function) in Web (TypeScript)
– (CubismUserModel.motionEventFired function or CubismUserModel.cubismDefaultMotionEventCallback variable) in Java
Automatic Deletion of Motion
If you put true in the second argument autoDelete when calling the CubismMotionQueueManager::StartMotion function in Native (C++) or the CubismMotionQueueManager.startMotion function in Web (TypeScript),
when the motion finishes playing, the motion is deleted along with the deletion of the CubismMotionQueueEntry.
This is intended to be used for files that are played only once.
// C++ csmBool CubismMotionQueueManager::DoUpdateMotion(CubismModel* model, csmFloat32 userTimeSeconds) { csmBool updated = false; // ------- Process -------- // If there is already a motion, set a termination flag. for (csmVector<CubismMotionQueueEntry*>::iterator ite = _motions.Begin(); ite ! = _motions.End();) { CubismMotionQueueEntry* motionQueueEntry = *ite; /* Omitted */ // ----- Delete any completed processing ------ if (motionQueueEntry->IsFinished()) { CSM_DELETE(motionQueueEntry); ite = _motions.Erase(ite); // Delete. } else { ++ite; } } return updated; }
// C++ CubismMotionQueueEntry::~CubismMotionQueueEntry() { if (_autoDelete && _motion) { ACubismMotion::Delete(_motion); } }
// TypeScript /** * Update the motion and reflect the parameter values in the model. * * @param model Target model * @param userTimeSeconds Delta time integrated value [sec] * @return true Parameter value is reflected in the model * @return false Parameter value is not reflected in the model (no change in motion) */ public doUpdateMotion(model: CubismModel, userTimeSeconds: number): boolean { let updated: boolean = false; // ------- Process -------- // If there is already a motion, set a termination flag. for(let ite: iterator<CubismMotionQueueEntry> = this._motions.begin(); ite.notEqual(this._motions.end());) { let motionQueueEntry: CubismMotionQueueEntry = ite.ptr(); /* Omitted */ // ------ Delete any completed processing ------ if(motionQueueEntry.isFinished()) { motionQueueEntry.release(); motionQueueEntry = void 0; motionQueueEntry = null; ite = this._motions.erase(ite); // Delete. } else { ite.preIncrement(); } } return updated; }
// TypeScript /** * Destructor-equivalent processing */ public release(): void { if(this._autoDelete && this._motion) { ACubismMotion.delete(this._motion); // } }
SDK for Java does not require a flag for automatic deletion of motions, because when a reference to a CubismMotionQueueEntry is removed, the motion instance that is its field is also automatically deleted.
CubismMotionQueueManager and CubismMotionManager Classes
The motion management classes include the CubismMotionQueueManager class and
a CubismMotionManager class that extends the CubismMotionQueueManager class.
CubismMotionQueueManager
The CubismMotionQueueManager enables switching between motions with fading based on the compatibility of motion values.
When adding motion playback, StartFadeout brings forward the end time for motions that are already playing using one of the following functions:
– LAppModel::StartMotion function in Native (C++)
– LAppModel.startMotion function in Web (TypeScript)
– LAppModel.startMotion function in Java
This operation enables the motion currently playing to switch to a new motion with a fade.
// C++ CubismMotionQueueEntryHandle CubismMotionQueueManager::StartMotion(ACubismMotion* motion, csmBool autoDelete, csmFloat32 userTimeSeconds) { if (motion == NULL) { return InvalidMotionQueueEntryHandleValue; } CubismMotionQueueEntry* motionQueueEntry = NULL; // If there is already a motion, set a termination flag. for (csmUint32 i = 0; i < _motions.GetSize(); ++i) { motionQueueEntry = _motions.At(i); if (motionQueueEntry == NULL) { continue; } motionQueueEntry->StartFadeout(motionQueueEntry->_motion->GetFadeOutTime(), userTimeSeconds); // Starts and ends the fade-out } motionQueueEntry = CSM_NEW CubismMotionQueueEntry(); // Discards at the end. motionQueueEntry->_autoDelete = autoDelete; motionQueueEntry->_motion = motion; _motions.PushBack(motionQueueEntry, false); return motionQueueEntry->_motionQueueEntryHandle; }
// TypeScript /** * Start specified motion * * Starts specified motion. If a motion of the same type already exists, flag the existing motion as finished and begin the fade-out. * * @param motion Motion to be started * @param autoDelete Enter true if you want to delete the instance of the motion that has finished playing * @param userTimeSeconds Delta time integrated value [sec] * @return Returns the identification number of the started motion. Used in the argument of IsFinished(), which determines whether or not an individual motion is finished. If the motion cannot be started, returns [-1] */ public startMotion(motion: ACubismMotion, autoDelete: boolean, userTimeSeconds: number) : CubismMotionQueueEntryHandle { if(motion == null) { return InvalidMotionQueueEntryHandleValue; } let motionQueueEntry: CubismMotionQueueEntry = null; // If there is already a motion, set a termination flag. 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); // Starts and ends the fade-out } motionQueueEntry = new CubismMotionQueueEntry(); // Discards at the end. 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; } // If there is already a motion, set a termination flag. 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
The CubismMotionManager class has the ability to save the priority of a motion to be played and to register the priority of the upcoming playback as an integer.
It is envisioned this will be used to create a function that regulates motion playback with lower priority by comparing it to this recorded priority.
The part that regulates playback must be prepared outside of the CubismMotionManager.
One of the following functions supports playback from an asynchronous thread:
– LAppModel::StartMotion function in Native (C++)
– LAppModel.startMotion function in Web (TypeScript)
– LAppModel.startMotion function in Java
At the beginning of the function, the priority of the playback is registered by SetReservePriority and ReserveMotion.
Next, loading is performed. If this function is called asynchronously, the priority is registered at the beginning,
so the system regulates other low-priority playback on other threads during loading.
At the end of playback, the priority is set to 0, which is fixed, and other control is left to code outside the function.
// 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; } /* Motion data preparation part is omitted */ return _motionManager->StartMotionPriority(motion, autoDelete, priority); }
// TypeScript /** * Starts playback of the motion specified by the argument. * @param group Motion group name * @param no Number in group * @param priority Priority * @return Returns the identification number of the started motion. * Used in the argument of isFinished(), which determines whether or not an individual motion is finished. * If the motion cannot be started, returns “-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; } /* Motion data preparation part is omitted */ 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; } /* Motion data preparation part is omitted */ if (motionManager.startMotionPriority(motion, priority) ! = -1) { return motionManager.startMotionPriority(motion, priority); } return -1; }