About Motion

Updated: 10/06/2022

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);
    }
// 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);
    }
Please let us know what you think about this article.