『あんさんぶるスターズ!!Music』足の動きに合わせてパーティクルを出す機能
はじめに
『あんさんぶるスターズ!!Music』(以下、あんスタ!!Music)でゲームエンジニアをしている M.M. です。
入社してちょうど4年、色々とあって1年半ほど前からあんスタ!!Musicのチームにジョインしました。
ジョインしてからも色々とあって現在は新規開発・運用のマネジメントをしつつ、実作業ベースでは3DライブMVの開発(主に初期のセットアップ)を担当しております。
あんスタ!!Musicでの推しキャラは「青葉 つむぎ」ですが、
眼鏡の一彩くんを見た瞬間気づいたら全てのダイヤが溶けていました。。
さて、この記事ではあんスタ!!Musicで開発した機能を紹介したいと思います。
「夏鳥の詩 -サマーバード-」の水面
「夏鳥の詩 -サマーバード-」ではキャラクターが湖の上でダンスするパートがあり、
キャラクターの足と水面が常に接触していて波紋や水しぶきのパーティクルの設定に課題がありました。
手付けで設定するには物量が多く、またキャラクターを入れ替えることで足の位置も変わってきます。
今回はキャラクター足の動きに合わせてパーティクルを自動発生させる機能を作成して対応しました。
接触判定〜パーティクル発生まで
自作のタイムラインスクリプトで以下のような順序で処理を行います。
- キャラクターの足と床が接触しているかを判定
- 「接触開始時」「接触終了時」「接触しながら足を動かした時」にイベントを発火
- バースト設定からパーティクルを発生
まず、キャラクターの足と床の接触については足の指先(toes)と足首(foot)の位置を基準に床と接触しているかを判定します。
また足の位置は一定フレーム数記録しておいて速度の計算に使用します。
// clip 変数は後述の ParticleSystemBursterClip
var detectHeight = floorHeight + clip.ContactPositionOffset;
GetCharacterTransforms(out var foot, out var toes);
var toesPosition = toes.position - new Vector3(0f, ToesContactSize, 0f);
var footPosition = foot.position - new Vector3(0f, FootContactSize, 0f);
if (toesPosition.y < detectHeight)
{
if (footPosition.y < detectHeight)
{
var averagePosition = 0.5f * (toesPosition + footPosition);
_contactPosition = new Vector3(averagePosition.x, floorHeight, averagePosition.z);
}
else
{
_contactPosition = new Vector3(toesPosition.x, floorHeight, toesPosition.z);
}
_contacting = true;
}
else if (footPosition.y < detectHeight)
{
_contactPosition = new Vector3(footPosition.x, floorHeight, footPosition.z);
_contacting = true;
}
else
{
_contacting = false;
}
// この中で足の位置を記録
_footRecordedPosition.Record(playableTime, footPosition);
_toesRecordedPosition.Record(playableTime, toesPosition);
次に前回の接触判定を考慮しつつ「接触開始時」「接触終了時」「接触しながら足を動かした時」にイベントを発火します。
if (_contacting)
{
if (contactingBefore)
{
Burst(playableTime, GetDragSpeed(), clip.BurstOnDrag);
}
else
{
Burst(playableTime, GetTouchOnOffSpeed(), clip.BurstOnTouchOn);
}
}
else
{
if (contactingBefore)
{
Burst(playableTime, GetTouchOnOffSpeed(), clip.BurstOnTouchOff);
}
}
最後に実際にバーストさせる部分では設定されたパラメーターを考慮しつつ Burst データを作成して、
ParticleSystem.emission.SetBursts()
でパーティクルを発生させます。
Timeline のクリップによる制御
例えば波紋の設定をしているクリップは以下のようなパラメーターで制御しています。
ParticleSystem はキャラクターの片足ごとに作成する想定で、
今回の MV では 3人 * 2(両足) * パーティクル3種(波紋、波紋ノーマルマップ、水しぶき) で作成しています。
ParticleSystemBursterClip
パラメーター | 説明 |
---|---|
float ContactPositionOffset | 接触判定をする高さ(床の高さに対する相対値)。 |
float EmissionPositionOffset | パーティクルを発生させる高さ。 |
BurstData BurstOnTouchOn | 接触開始時にバーストさせる場合の設定。 |
BurstData BurstOnTouchOff | 接触終了時にバーストさせる場合の設定。 |
BurstData BurstOnDrag | 接触しながら足を動かした時にバーストさせる場合の設定。 |
BurstData
パラメーター | 説明 |
---|---|
bool IsEnabled | 有効かどうか。 |
float SpeedMin | イベント発生時の足の速さが SpeedMin 以上だったらバーストさせる。 |
float SpeedMax | 後述の Modifiers で速度を参照するとき SpeedMin〜SpeedMax 間で InverseLerp された値が使われます。 |
float ThrottleTime | 前回のバーストからの最小待機時間。 |
float BurstDelay | バーストを遅らせる時間。ParticleSystem.Burst.time |
float BurstProbability | バーストを起動する確率。ParticleSystem.Burst.probability |
int BurstCount | パーティクルの発生数。ParticleSystem.Burst.count |
int BurstCycles | バーストを行う回数。ParticleSystem.Burst.cycleCount |
float BurstInterval | バーストのサイクルの間隔時間。ParticleSystem.Burst.repeatInterval |
float StartSize | パーティクルのサイズ。ParticleSystem.MainModule.startSize |
Color StartColor | パーティクルの初期カラー。ParticleSystem.MainModule.startColor |
float StartSpeed | パーティクルの初期速度。ParticleSystem.MainModule.startSpeed |
List<BurstDataModifier> Modifiers | 速度や乱数値を使って BurstProbability 以降のパラメーターの値を変更。 |
BurstDataModifier
パラメーター | 説明 |
---|---|
BurstDataModifierSource Source | Random / Speed から選択。 |
BurstDataModifierTarget Target | BurstProbability / BurstCount / BurstCycles / BurstInterval / StartSize / StartColor / StartSpeed から選択。 |
BurstDataModifierMode Mode | Overwrite / Add / Multiply から選択。 |
AnimationCurve Curve | Target で StartColor 以外を選択した時に使用。 |
Gradient Gradient | Target で StartColor を選択した時に使用。 |
おわりに
いかがでしたでしょうか。
簡単にですが今回作成した機能について紹介させていただきました。
足元のパーティクルなのでなかなか気づきにくいですが、こうした細部にもこだわってあんスタ!!Musicの MV は作成されています。
来年も新しい楽曲がどんどん公開されると思うので、キャラクター以外の演出部分にも注目していただけると幸いです。
おまけ
ちなみに今回作成した機能は「FIST OF SOUL」の砂煙でも使用されています!
(かなり注意して見ないと気づかないかも……?!)
Discussion