关于动态

最終更新: 2022年10月6日

播放动态所需的类

1. 保持动态数据并对模型进行操作的类

a. 创建副本(导入motion3.json文件)

b. 如何播放动态的设置

c. 放弃副本

2.动态管理类

a. 创建副本

b. 动态播放

c. 模型参数更新

d. 退出动态

e. 接收用户触发器

1-a. 创建动态副本(导入.motion3.json文件)

使用派生自ACubsimMotion类的CubismMotion类来播放动态。
用于动态的数据是文件扩展名为“.motion3.json”的动态文件。
Cubism 2.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循环动作不保证与Editor循环动作完全匹配。
* 目前,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文件中设置为一个全体值

共有三个,优先级按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函数

如果在渐变途中等同时播放两个或多个动态时,将全部退出。

// C++
	motionManager->StopAllMotions();
// TypeScript
	motionManager.stopAllMotions();
// Java
	motionManager.stopAllMotions();

2-e. 接收Event

当播放动态中设置的“Event”时,您可以接收带有注册的回调调用,该回调使用CubismMotionQueueManager中的以下任意函数注册。

        – Native(C++)的SetUserTriggerCallback函数

        – Web(TypeScript)的setUserTriggerCallback函数

        – Java的setUserTriggerCallback函数

只能注册一个回调。
当您想调用一个副本的函数时,将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 增量时间的累积值[秒]
	 * @returntrue参数值应用在模型中
	 * @returnfalse 参数值不应用在模型中(动态没有变化)
	 */
    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);
    }
// 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);
    }
关于本报道,敬请提出您的意见及要求。