关于动态
最終更新: 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;
}