◤ Introduction / はじめに
この記事では2つの時間操作系アセットを比較し、それぞれの機能や使い勝手を紹介します。時間操作系アセットのうち、無料かつ高機能そうなアセットとして Agamotto と Chronos を選びました。
Agamotto は 2020 年 10 月、Red Girafe Games からリリースされた比較的新しいアセットです。対して Chronos は Ludiq から 2015 年 4 月にリリースされ、2019 年 7 月まで開発されていた古参アセットです。いずれも元々は有料アセットだったようですが、現在は無料化されています。
どこかで使う機会がないかと以前から目を付けていたのですが、Unity ゲーム開発者ギルド の Slack で「Unity アセット真夏のアドベントカレンダー 2021 Summer!」の枠が余っているとの話を聞き、便乗させてもらうことにしました。
筆者は C# や Unity にそこまで詳しくないので、間違った解釈・記述をしている可能性があります。誤りを見つけられた際には、コメントもしくは編集リクエストにてお伝えいただければ幸いです。
◤ Overview / 概観
さて、どちらも似たようなアセットに見えますが、どのような違いがあるのでしょうか。上に挿入した動画を見ると、Agamotto はビジュアライズ、Chronos はハンドリングに力を入れているように思えます。より詳しく確認したいので、さっそく公式の資料を見に行きましょう。
まずは Agamotto から見ていきます。
Agamotto
What is it の項目では、Agamotto の時間操作に関する基本的な機能の紹介や、アセットの用法に関する注意事項が書いてあります。
Warning : Agamotto is not a tool to slow, freeze or accelerate time in real time. It contains utility tools to freeze objects and you can playback or rewind your objects state over time at the desired speed, but you'll need to first record or simulate their state.
Agamotto is made to store and control objects state over time.
For example, if you're searching a tool to activate a bullet time... this is not the asset you're search for.
いきなり、え、そうなの?という感じですが、Time.timeScale
1 を操作するときのようにリアルタイムにゲーム内時間の流れを変えるわけではなく、あくまでも一定期間の動作を記録して、再生/逆再生ができるものとのこと。説明にある Simulation という語は「再現」くらいの意味で読めば良いのでしょうか。
映画「マトリックス」のように任意のタイミングで物理演算をスローにするといったことはできません。しかし、先に物理シミュレーションやアニメーションを回し、その記録に沿って任意のスピード(負値も可)で動かすことはできます。パーティクルも含めて時間停止はできるので、機能的には大抵のゲームでは困らないのではと思います。
インスペクター上で各種コントロールを試すことができるので、軽く動作確認する程度であれば、コードを書く必要がないのは良いですね。ただ、オンラインの API ドキュメント がちょっと読みづらく、パッと見では、どうコードを書けば良いのか分かりにくいと思います。
実はコード上に XML ドキュメント2 が埋め込まれているので、それを読むほうが分かりやすいという方には、こちらをお勧めします。スクリプティングに関してはチュートリアルがなかったり、一応ユーザーが自分で拡張して使うこともできる設計になっていたり、ある程度コードが読める人をターゲットにしていることが伺えるアセットです。
また、Agamotto にはビジュアライズ系コンポーネントが2つ用意されています。軌跡を表示する Path Visualizer と、残像(or 未来像)を表示する Onion Visualizer です。
パスはともかく、オニオンってなんだ?と思った人もいるでしょう。DAW ソフトを触ったことがある人はすぐに分かるかもしれませんが、つまり、玉ねぎのように薄く影を重ねて表示する機能です。マリオカート等でゴースト機能として似たようなものを見たことがある人もいるのではないでしょうか。複数の時点の影が描画されるので、影分身がダブったような見た目になります。
パスの描画は Unity 組み込みコンポーネントである LineRenderer の力を借りて実行されています。一方で、オニオンのほうは対象のゲームオブジェクト群をそのままそっくりコピーして利用する仕様らしく、スクリプトをアタッチしている場合は注意するよう書かれていました。Agamotto のコード中にもコピーか否かを判定して処理を変えているところがあり、案外シンプルな実装なようです。処理が重い場合は自分で拡張するなり自作するなりすることを考えるよう書かれています。
image source Agamotto_sorcerer_demo.gif
では、具体的な使い方に関しては以降の章にまわすとして、そろそろ Chronos のほうもざっと見ていきます。
Chronos
こちらはさすが Bolt(Visual Scripting 3 のベースとなったアセット)の開発元 Ludiq が手掛けただけあって、ドキュメントがかなりしっかりしています。読むのは少し大変ですが、スクリプトから操作する方法も含めて丁寧に書かれているので、こちらのほうが易しい印象を受けるかもしれません。
ただ、こちらもクセのあるアセットで、既存プロジェクトに組み込む際には注意が必要です。というのも、すでに書いたスクリプトで Time
や Rigidbody
、Animator
等々、時間遷移に関わる各種クラスを利用している場合、Chronos が用意している API に置き換えたり、ロジックの修正が必要になるからです。既存プロジェクトに導入する際には、この工数を加味しなければいけません。その分、Agamotto にはない時間操作機能が使える点は魅力です。
Agamotto は記録と再生を行う機能をコアとして、巻き戻し機能や、可視化機能によるシミュレーション風の演出も提供するアセットでした。Chronos はどうなのでしょうか?ドキュメントを読み進めていくと、マニュアルの Features という章で以下の説明がされていました。
Chronos is not a replay plugin. It only manipulates time continuously.
Therefore, it cannot:
- Scrub to a specific point in previous recorded data
- Replay rewind data in a forward direction
- Export or import rewind data to file
Chronos には Agamotto のように、任意の時点の記録を再生する機能はないとのこと。上手く工夫すれば似たような処理ができそうですが、基本的には、指定したオブジェクトの時間が「どう流れるか」をコントロールすることを目的としたアセットのようです。
ちなみに PV で印象的だった、時間の速度に合わせてマテリアルの色が変化する演出ですが、デモ用シーンにも該当スクリプトが含まれているものの、公式にサポートされている機能ではありません。自分のプロジェクトで利用する際には、操作対象にアタッチした Timeline
コンポーネントの timeScale
フィールドを読み取り、その値に応じてマテリアルやシェーダーを操作することになります。
また、チュートリアルでは Chronos が提供する Occurence クラスの用法についても説明がありました。Chronos の逆再生は、Agamotto と同じく一定期間の挙動を記録して、それを逆再生することで実現されています。問題は、そのままでは記録範囲外の挙動は再現できないことです。
Chronos では Timeline
コンポーネントの Plan()
メソッド等で、処理内容とそのタイミングを登録すれば、記録開始以前に起こったであろうイベントも再現できるようになっています。仕様上、想定通りの挙動を保証するには、ほかのオブジェクトとのインタラクションに依らずに実行される挙動であることが求められます。巻き戻し中にもインタラクションを効かせるには、メッシュのみを持つオブジェクトとインタラクション用の不可視オブジェクトを分ける、などの方法が考えられます。
Unity の物理演算は非決定論的なので、時間を前方へ進める際、結果が同じにならないことにも注意が必要です。リプレイ用プラグインではない、という説明もありましたが、「並行世界への分岐を前提としたタイムトラベル」と考えればイメージがしやすいでしょうか。さらに時間を戻す際にも、記録間隔によっては回転運動などが視覚上、上手く再現できない場合があるので、こちらも考慮する必要があります。
Timeline
コンポーネントは記録間隔を指定する際、メモリ使用量をインスペクター上で表示してくれます。複数の Timeline
コンポーネントを選択している場合は、選択対象のメモリ使用量をまとめて表示してくれるので、この値を参考にしながら、設定を調整してみると良いでしょう。
さて、本当は API の仕様などを細かく見ていきたいところですが、文章量も多くなってきたので、Agamotto と Chronos それぞれで何ができ、何ができないのかざっくり整理していきます。
◤ Supproted Features / サポートされている機能
多分に漏れがありそうですが、理解できた範囲で挙げておきます。
Agamotto
- 指定した時間区間の記録と、それに基づく再生・逆再生
- 再生もしくは逆再生の位置指定
- タグ・レイヤーによる操作対象の指定
- 軌跡および残像の表示
- スクリプトベースの挙動シミュレーション
- 任意のパブリックフィールドの記録・再現
Chronos
- 指定した時間区間の記録と、それに基づく再生・逆再生
- 再生もしくは逆再生の速度指定
- コライダーによる操作対象の指定
- 指定する再生速度の乗算・加算
- 記録外の処理の実行
- 任意のパブリックフィールドの記録・再現
- サードパーティとの統合
- DOTween
- LeanTween
- iTween
- PlayMaker
◤ Unsupproted Features / 未サポートの機能
- 決定論的な未来方向への物理演算(共通)
- インポート/エクスポート機能(共通)
- 操作対象の途中変更(Agamotto)
- 記録範囲外のシミュレーション(Agamotto)
そもそも Agamotto の物理シミュレーションはすり抜けや吹っ飛びが起きやすい。筆者の設定ミス?
◤ How to use / 使い方
細かい設定や使い方は色々あるのですが、とりあえず空のシーンでサクッと試す方法を載せておきます。
Agamotto
- 床を作ります。
- キューブを量産します。
- 床以外のキューブに
Rigidbody
コンポーネントをアタッチします。 -
Rigidbody
コンポーネントを付けたキューブに、TimeAgent
コンポーネントも付けます。 - 空オブジェクトを作り、
TimeStone
コンポーネントをアタッチします。 -
TimeStone
コンポーネントで、時間操作を行う対象を指定します。(今回はマニュアルで指定) -
TimeStone
コンポーネントで、Record on start にチェックを入れます。 - シーンを再生して、
TimeStone
のシークバーをいじってみましょう。(freeze しないと暴れます)
スクリプトからの操作方法を試したい方は、ひとまず以下のスクリプトをアタッチしてみましょう。
using UnityEngine;
using RedGirafeGames.Agamotto.Scripts.Runtime;
public class AgamottoTest : MonoBehaviour
{
public TimeStone timeStone; // 操作対象を統括する TimeStone を指定
public int seekPoint; // どの時点の記録を再現するか指定
public bool isLocked; // 物理挙動のロック(再現時に暴れないように)
void OnValidate()
{
timeStone.FreezeTimeAgents(isLocked);
timeStone.SetPlaybackTick(seekPoint);
}
}
Chronos
- 床を作ります。
- キューブを量産します。
- 床以外のキューブに
Rigidbody
コンポーネントをアタッチします。 -
Rigidbody
コンポーネントを付けたキューブに、Timeline
コンポーネントも付けます。 - 空オブジェクトを作り、
Timekeeper
およびGlobalClock
コンポーネントをアタッチします。(ヒエラルキー上の右クリックメニューから「Timekeeper」を選択しても OK) -
GlobalClock
の Key を指定します。(今回は「Root」に設定) - 操作対象の
Timeline
で Mode を Global に、Global Clock を Root に指定します。 - シーンを再生して
Timekeeper
のTimeScale
を操作してみましょう。
TimeKeeper
はシーンの時間を統括するシングルトンです。また、GlobalClock
の参照関係は入れ子にできます。
併用できるか
時間の扱い方が違うので、そのあたりを吸収するスクリプトを自分で用意する必要があります。Time
クラスを自作してネームスペースなしで定義することで擬似的に上書きができるかもと考えていますが、あまりやりたくない方法です。
◤ Conclusion / まとめ
ドキュメントがすべて英語で書かれているからか、詳しく解説している記事が少ないので、ドキュメントを見ながら分かる範囲で書いてみました。意外と調べるのに時間がかかり、急に駆け足になってしまいましたが、細かい使い方は時間が空いたら別記事で書こうと思います。
はじめて2つのアセットを見つけたとき、どちらも同じようなアセットだと思っていましたが、今回、記事の執筆にあたって調査を進めるなか、よく似ている部分もあれば、全く違う部分もあることが分かりました。また、Agamotto は動作の可視化機能も使えるリプレイ系システム、Chronos は柔軟な範囲指定が可能な加減速系システムであり、「時間操作」が想起させるものとは少し異なる実態がありました。時間操作のアプローチを学ぶ良い機会になったと思います。
仕様の違いとして面白かったのは、コンポーネントのセットアップを行う際、Agamotto はマネージャー系システムが操作対象を指定するのに対して、Chronos は操作対象の側からマネージャー系システムを参照することです。方向性の違いが、そのまま設計に反映されていることが分かります。
それぞれ異なる用途を想定したアセットなので、その違いを踏まえて実際のプロジェクトでも活用したいですね。インポート/エクスポート機能など、サポートされていない機能は必要に応じて拡張・自作することで、さらに多くのことができそうです。
以前、インポート/エクスポートが可能な記録・再生システムをテキトウに自作したときは、JSON で各プロパティの値をそのまま出力して、動画並みの容量になったので、その辺が気になる方は、重複データを省いたり、値の桁数を抑えたり、進数変換してみたり、色々調整してみると良いと思います。
-
[ref] https://docs.unity3d.com/ScriptReference/Time-timeScale.html [cf.] https://tech.pjin.jp/blog/2016/12/20/unity_skill_7/ ↩
-
[ref] https://docs.microsoft.com/en-us/dotnet/api/system.xml.xmldocument?view=netframework-4.8 [cf.] https://ufcpp.net/study/csharp/sp_xmldoc.html ↩
-
[ref] https://docs.unity3d.com/2021.1/Documentation/Manual/com.unity.visualscripting.html [cf.] https://unity.com/ja/products/unity-visual-scripting [cf.] https://learning.unity3d.jp/tag/visual-scripting/ ↩