对各部位执行不同的动态管理

最終更新: 2022年10月6日

此页面适用于Cubism 4.2及更早版本的旧版本。 点击此处查看最新页面

在本页面上,以为了实装左右手不同的动态管理,
增加LAppModel的CubismMotionManager的占有,并嵌入LAppmodel.update的情况为例,
看一看管理和创建动态时的注意事项等。

增加CubismMotionManager的原因

CubismMotionManager是为了应对渐变,支持临时播放多个动态的功能。
但是,开始播放的API会淡出正在播放的动态并退出,
所以没有保持并行播放动态的功能。

要解决此问题,请准备多个动态管理器副本,
通过分离要播放的动态管理器,实现了并行动态播放。

增加CubismMotionManager

追加动态管理器

首先,我们看一下程序端的实装。
首先要做的是向您的模型追加一个管理类。
我们将CubismMotionManager追加到范例LAppModel。
此外,将生成处理追加到构造函数。

public class LAppModel extends CubismUserModel{
    ...
    private CubismMotionManager rightArmMotionManager; // 追加
    private CubismMotionManager leftArmMotionManager; // 追加
}
public LAppModel(){
    ...
    rightArmMotionManager = new CubismMotionManager(); // 追加
    leftArmMotionManager = new CubismMotionManager(); // 追加
}

更新处理

还要追加到update处理中,使播放的动态影响参数。

public void update(){
    ...
    // 读取上次保存的状态
    _model.loadParameters();

    // 如果没有动态播放,则从待机动态开始随机播放
    if(_motionManager.isFinished()){
        startRandomMotion(LAppDefine.MotionGroup.IDLE.getId(), LAppDefine.Priority.IDLE.getPriority());
    } else{
        // 更新动态
        isMotionUpdated = _motionManager.updateMotion(_model, deltaTimeSeconds);
    }
  
    isMotionUpdated |= rightArmMotionManager.updateMotion(_model, deltaTimeSeconds); // 追加
    isMotionUpdated |= leftArmMotionManager.updateMotion(_model, deltaTimeSeconds); // 追加

    // 保存模型状态
    _model.saveParameters();
}

在LAppModel.update内部追加时,请在_model.loadParameters();和_model.saveParameters();之间设置updateMotion。
另外,如果多次执行updateMotion,可能会导致要更新的参数重叠。
请注意,在这种情况下,后面执行的updateMotion内容将优先。

开始播放

然后,可以通过复制和修改startMotion系统的函数的形式,使用追加的动态管理器来指示动态播放。
请注意,startHandMotion函数将动态管理器作为参数指定。
在以下示例中,仅播放.motion3.json指定的动态(由preLoad函数读取的动态)。

// 通过从startMotion复制创建
public int startHandMotion(
    CubismMotionManager targetManage,
    final String group,
    int number,
    int priority
) {
    if (priority == LAppDefine.Priority.FORCE.getPriority()) {
        targetManage.setReservationPriority(priority);
    } else if (!targetManage.reserveMotion(priority)) {
        if (_debugMode) {
            LAppPal.logger.print("Cannot start motion.");
            return -1;
        }
    }
    final String fileName = _modelSetting.getMotionFileName(group, number);

    // ex) idle_0
    String name = group + "_" + number;
    CubismMotion motion = (CubismMotion) _motions.get(name);

    if (motion == null) {
        // 如果没有被preLoad读取,则不播放
        return -1;
    }
    if (_debugMode) {
        LAppPal.logger.print("Start motion: [" + group + "_" + number + "]");
    }
    
    return targetManage.startMotionPriority(motion, priority);
}

// 通过startRandomMotion复制创建
public int startRandomRightHandMotion(final String group, int priority) {
    if(_modelSetting.getMotionCount(group) == 0){
        return -1;
    }

    Random random = new Random();
    int number = random.nextInt(Integer.MAX_VALUE) % _modelSetting.getMotionCount(group);
    return startHandMotion(rightArmMotionManager, group, number, priority);
}

// 通过startRandomMotion复制创建
public int startRandomLeftHandMotion(final String group, int priority) {
    if(_modelSetting.getMotionCount(group) == 0){
        return -1;
    }

    Random random = new Random();
    int number = random.nextInt(Integer.MAX_VALUE) % _modelSetting.getMotionCount(group);
    return startHandMotion(leftArmMotionManager,group,number,priority);
}


动态播放为模型追加了重叠检测,使其通过单击动作。

public void onTap(float x, float y) {
    if (DEBUG_LOG_ENABLE) {
        CubismFramework.coreLogFunction("tap point: {" + x + ", y: " + y);
    }

    for (LAppModel model : _models) {
        // 敲击头部时随机播放表情
        if (model.hitTest(HitAreaName.HEAD.getId(), x, y)) {
            if (DEBUG_LOG_ENABLE) {
                LAppPal.logger.print("hit area: " + HitAreaName.HEAD.getId());
            }
            model.setRandomExpression();
        }
        // 敲击身体时开始随机动态
        else if (model.hitTest(HitAreaName.BODY.getId(), x, y)) {
            if (DEBUG_LOG_ENABLE) {
                LAppPal.logger.print("hit area: " + HitAreaName.BODY.getId());
            }
            model.startRandomMotion(MotionGroup.TAP_BODY.getId(), Priority.NORMAL.getPriority(), _finishedMotion);
        }
      
        // 从这里追加
        else if (model.hitTest("Right", x, y)) {
            if (DEBUG_LOG_ENABLE) {
                LAppPal.logger.print("hit area: [" + HitAreaName.BODY.getId() + "]");
            }
            model.startRandomRightHandMotion("Right", Priority.FORCE.getPriority());
        } else if (model.hitTest("Left", x, y)) {
            if (DEBUG_LOG_ENABLE) {
                LAppPal.logger.print("hit area: [" + HitAreaName.BODY.getId() + "]");
            }  
            model.startRandomLeftHandMotion("Left", Priority.FORCE.getPriority());
        }
        // 追加到这里
    }
}

负责参数的分布

如更新处理所示,如果多个要播放的动态之间的参数重复,则后面执行的更新内容将被优先执行,参数将被覆盖。
因此,在使用多个动态管理器时,需要决定哪个动态管理器负责哪个部位的动作、以及负责哪个参数操作。

上述视频中,刚刚退出播放优先顺序较高的动态管理器(负责左臂和右臂的动态)后,通过优先顺序较低的动态管理器(负责模型全体闲置动态)更新了左臂和右臂的参数。
如果您希望将优先顺序较高的动态管理器更新的参数保持在刚刚退出动态播放后的状态,这不是一个理想的表达方式。
当优先顺序较低的动态管理器播放的动态数据中不打算更新的参数设置参考值时,就会出现这种现象。
此时,通过将要更新的参数对各动态数据进行分开,可以将优先顺序较高的动态管理器更新的参数保持在刚刚退出播放后的状态。

针对是否分开各参数或以覆盖为前提,
在创建动态之前明确定义规范非常重要,以免进行巨大的修正。

请问这篇文章对您有帮助吗?
关于本报道,敬请提出您的意见及要求。