MouthMovement (Cocos Creator)
最終更新: 2023年3月14日
概要
MouthMovementは、リップシンク用パラメータの現在の値に対して開閉状態の値を適用する機能です。
モーションに設定されたリップシンク用のカーブや再生している音声ファイルからリアルタイムにサンプリングした値などをモデルに適用することが可能です。
モデルにリップシンクのパラメータを設定する方法については こちら をご覧ください。
MouthMovementで設定するものは口の開閉状態のみです。
口の形状を母音に合わせるというような操作をすることはできません。
Cocos Creator上でリップシンク用のパラメータを指定するには、Cubismエディタでモデルに設定しておく他、Cocos Creator上でユーザが任意に指定することができます。
Cubism SDK for Cocos Creator におけるMouthMovementは3種類の要素によって構成されています。
- パラメータ指定用のコンポーネント
- 各パラメータに値を適用するコンポーネント
- 2で適用する値の操作
1. パラメータ指定用のコンポーネント
MouthMovementに使用するパラメータを指定するには、CubismMouthParameterを使用します。
CubismMouthParameterはComponentを継承したコンポーネントで、
[Prefabのルート]/Parameters/ 以下に配置されたNodeにアタッチして使用します。
これがアタッチされたNodeと同じIDのパラメータをリップシンク用のパラメータとして扱います。
モデル自体にリップシンク用のパラメータが設定されている場合、インポートの際にそのパラメータのNodeにCubismMouthParameterがアタッチされます。
CubismMouthParameterは参照先を取得するためのマーカーとして使用しているので、内部では何も処理を行っておらず、データも持っていません。
2. 各パラメータに値を適用するコンポーネント
各パラメータに開閉の値を適用するには、CubismMouthControllerを使用します。
これはComponentを継承したコンポーネントで、使用する際はCubismのPrefabのルートにアタッチします。
初期化時に、PrefabにアタッチされたすべてのCubismMouthParameterの参照を取得します。
実行中にまばたき用のパラメータを追加/削除した場合、CubismMouthController.refresh()を呼んで参照を取得し直します。
public refresh() {
const model = CoreComponentExtensionMethods.findCubismModel(this);
// Fail silently...
if (model == null || model.parameters == null) {
return;
}
// Cache destinations.
const tags = ComponentExtensionMethods.getComponentsMany(
model.parameters,
CubismMouthParameter
);
this.destinations = new Array(tags.length);
for (let i = 0; i < tags.length; ++i) {
this.destinations[i] = tags[i].getComponent(CubismParameter);
}
// Get cubism update controller.
this.hasUpdateController = this.getComponent(CubismUpdateController) != null;
}
...
protected start() {
// Initialize cache.
this.refresh();
}
CubismMouthControllerは、毎フレームのlateUpdate()のタイミングで、CubismMouthParameterでマーキングされたパラメータに対してCubismMouthController.MouthOpeningの値を適用します。
protected onLateUpdate(deltaTime: number) {
// Fail silently.
if (!this.enabled || this.destinations == null) {
return;
}
// Apply value.
CubismParameterExtensionMethods.blendToValueArray(
this.destinations,
this.blendMode,
this.mouthOpening
);
}
MouthOpeningに設定する値は0.0f~1.0fの範囲です。
CubismMouthControllerはこの値を対象のパラメータに対して、CubismMouthController.blendModeで設定された計算方式で適用します。
このMouthOpeningの値を外から操作することで、モデルの口を開閉させることができます。
@property({ type: CCFloat, slide: true, range: [0.0, 1.0, 0.01] })
public mouthOpening: number = 1.0;
3. 2で適用する値の操作
「2. 各パラメータに値を適用するコンポーネント」で説明した通り、CubismMouthController.mouthOpeningの値を操作することで、リップシンク用のパラメータに値を適用できます。
Cubism SDK for Cocos Creator には、以下の3種類の方法でこの値を操作することができます。
- モーションによって値を操作
- 周期的に値を操作
- AudioClipからサンプリングして値を操作
また、ユーザ側でこの値を操作する処理を実装していただくことで、リップシンクの速度やタイミング等を独自にカスタマイズすることができます。
Tips
CubismMouthController.mouthOpeningを操作するコンポーネントの実行順がCubismMouthControllerよりも後である場合、意図した動作にならない可能性があります。
もし動作に問題が生じた、ユーザ側で明示的にコンポーネントの実行順を制御することで回避が可能です。
Cubism SDK for Cocos Creator では各コンポーネントの実行順をのCubismUpdateControllerで制御しているので、これを利用することもできます。
また、上記3種類の設定方法はそれぞれが同じ値を操作しているため、工夫無しで一つのモデルに共存させることは難しくなっています。
モーションによって値を操作
まばたき用のパラメータを設定したモデルを使用してCubismのAnimatorでモーションを作成する場合、まばたき用のカーブを設定することが可能です。
まばたき用のカーブが設定された.motion3.jsonをCocos Creatorプロジェクトにインポートした場合、AnimationにはCubismMouthController.mouthOpeningの値を対象としてそのカーブが生成されます。
そのため、そのAnimationClipをAnimatorコンポーネントなどで再生することでCubismMouthController.mouthOpeningの値が操作されます。
周期的に値を操作
周期的にリップシンク用の値を操作させるには、CubismAutoMouthInputを使用します。
CubismAutoMouthInputは、正弦波でリップシンクの値を算出して設定するコンポーネントです。
CubismAutoMouthInputを使用するには、CubismのPrefabのルートにアタッチします。
CubismAutoMouthInputには1つの設定項目があります。
- Timescale
正弦波の周期が変化します。
@property(CCFloat) public Timescale: number = 10.0;
lateUpdate(deltaTime: number) {
// Fail silently.
if (this.Controller == null) {
return;
}
// Progress time.
this.T += deltaTime * this.Timescale;
// Evaluate.
this.Controller.mouthOpening = Math.abs(Math.sin(this.T));
}
オーディオからサンプリングして値を操作
Cocos Creator上で再生される音声からリップシンクの値を設定する場合はCubismAudioMouthInputを使用します。
CubismAudioMouthInputは、AudioSourceから取得した再生中の音声情報からサンプリングしてリアルタイムにリップシンクの値を生成、設定します。
CubismAudioMouthInputを使用するには、CubismのPrefabのルートにアタッチします。
protected update(deltaTime: number) {
const { audioInput, samples, target, sampleRate, gain, smoothing } = this;
// 'Fail' silently.
if (audioInput == null || target == null || samples == null || sampleRate == 0) {
return;
}
const { trunc, sqrt } = Math;
const { currentTime } = audioInput;
const pos = trunc(currentTime * this.sampleRate);
let length = 256;
switch (this.samplingQuality) {
case CubismAudioSamplingQuality.veryHigh:
length = 256;
break;
case CubismAudioSamplingQuality.maximum:
length = 512;
break;
default:
length = 256;
break;
}
// Sample audio.
let total = 0.0;
for (let i = 0; i < length; i++) {
const sample = samples.getData((pos + i) % samples.length);
total += sample * sample;
}
// Compute root mean square over samples.
let rms = sqrt(total / length) * gain;
// Clamp root mean square.
rms = math.clamp01(rms);
// Smooth rms.
const output = MathExtensions.Float.smoothDamp(
this.lastRms,
rms,
this.velocityBuffer,
smoothing * 0.1,
undefined,
deltaTime
);
rms = output[0];
this.velocityBuffer = output[1];
// Set rms as mouth opening and store it for next evaluation.
target.mouthOpening = rms;
this.lastRms = rms;
}
CubismAudioMouthInputには4つの設定項目があります。
@property(AudioSource)
public audioInput: AudioSource | null = null;
@property({ type: Enum(CubismAudioSamplingQuality) })
public samplingQuality: CubismAudioSamplingQuality = CubismAudioSamplingQuality.high;
@property({ type: CCFloat, slide: true, range: [1.0, 10.0, 0.01] })
public gain: number = 1.0;
@property({ type: CCFloat, slide: true, range: [0.0, 1.0, 0.01] })
public smoothing: number = 0.0;
- audioInput
サンプリングするAudioSourceの参照です。 - samplingQuality
サンプリングする音声の精度です。 - gain
サンプリングした値の倍率です。
1で等倍、値を大きくするほどリップシンクの値も大きくなります。 - smoothing
サンプリングした値のスムージング量です。
値が大きいほどリップシンクの値が滑らかに変化します。