关于动态
最終更新: 2023年1月26日
播放动态所需的类
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文件中设置为单独的参数值(参数渐变)
共有三个,优先级按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 增量时间的累积值[秒] * @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; }