Tickとは
Tickとは毎フレーム実行される処理のことです。
UnityではUpdateと呼ばれているものに相当します。
UnityではUpdateにたくさんの処理を記述することは珍しい話ではないようですが…
UEでゲームを作成する場合Tickは、できる限り使わない方が良いとされています。
UnrealEngine公式もこちらで「BPはTickをほとんど必要としない」と述べており、実際にTickを使用せずにほぼすべての処理を実装できる機能を備えています。
Tickを使用しない方が良い主な理由は以下です。
- 毎フレーム実行されるので、ゲームが重くなりやすい。
- Tickを利用するアクタが複数あるとき、実行の順序が不定(バグの原因になりやすい)。
- 様々な処理を一つのイベント内に記述するので、可読性、保守性が低下する。
どれもとても嫌ですよね。(Unityは軽いので1は問題になりにくいそうですが、2,3の問題は共通してあると思います。)
しかし、いざTickを使わずゲームを作ろうとするとこれがなかなか難しい。
作り方に悩むのも、調べるのもめんどくさくなって「結局Tickに書くか…」となりがちです。
そんな方のために、よくあるTickを使用したくなる処理をTickなしで記述するテクニックをまとめてきました。(ある程度はUnityにも応用できる…かも?)
①入力処理
UnityだとUpdateに記述することが多いそうで、UnityからUEに入る人用に一応書いておきます。
UEではTickに入力処理を書くことはまずありえません。
公式にEnhancedInputというシステムを使うことが推奨されています。
入力をイベントとして扱い、設定次第で押された時、離した時、押されている間、○○秒長押しした時、などさまざまなタイミングをとることができます。これを使用しましょう。
(EnhancedInputについてはまたどこかで解説したい)
②アクターの継続的な移動
Tickでこのように書くことができますね。(問題だらけのコードではありますが)
Tickを使用するまでもなくBPはアクターの移動について非常に多様なサポートを備えてます。
アクタ自体の移動ならMoveTo系ノード、コンポーネントの移動ならMoveComponentTo、動き方を自由自在に決めたいならTimelineを利用すると良いです。
③変数の変化の監視
ゲームを作る際には、「この変数が○○以上になったとき」や「このフラグがTrueになったとき」に実行したい処理は非常によくでてきます。
Tickを使う場合は、以下のように書くことができます。
書く際の労力としては小さいですが、値に変化がないときも無駄に評価を行い続けてしまいますし、分岐があるので他の処理もTickに書こうとしたときに面倒くさいです。
カスタムイベント等にして切り分けたとしても、監視したい変数が増えるたびにTickに書く処理がダラダラと長くなるのは目に見えていますね…(可読性、保守性の低下)
これと同じことをTickなしで実現してみましょう。
まずひとつの方法としては、
「変数にGetter,Setterを設定し、アクセスする際は必ずそれらを使用すると決めておく」
というものがあります。
Setterの方にこのように記述しておけば値に変化があった際にのみ評価できます。
監視したい変数ごとにSetterを用意するので、ひとつのイベントにダラダラと長く処理を書くことにもなりません。バグがあっても責任の範囲が明確なので簡単に原因を特定できますね。
この方法の欠点としては、「うっかりSetterを介さずに値を変更するとバグる」ということです。
変数をprivate化しておけばクラス外からSetterを介さず変更されることはないですが、クラス内では直接変更してしまうということも一応起こり得ます。特に複数人で開発する際は注意が必要です。
もうひとつの方法として
「GameplayAttributeとGameplayEffectを使用する」
というものがあります。こちらはBPのみで完結せず、C++も使用する必要が出てきます。
GameplayAttributeとGameplayEffectについては「こちらの記事」を参照してください。
GameplayAttributeを使用する際、AttributeSetクラス内でPostGameplayEffectExecuteというイベントを使用することができます。
これはGameplayEffect実行後に必ず実行されるイベントで、ここで値の変化を監視することが可能です。
BP上からGameplayAttributeの値をGameplayEffect以外から変更することはC++側で設定しない限り不可能なのでうっかりの事故も起こり得ません。
また、ここでは解説しませんがGameplayEffectにはTickを減らす以外にも設計上の利点がたくさん存在します。
C++を使用する必要があるとは言え、そこまで難しいことはしないのでオススメの方法ですね。
④ある条件下で定期的に実行したい処理
「○○している間、○○秒に1度」実行したい処理というのもゲームを作る際には非常に多く出てきます。
こちらの実装もTickを用いる方法が思いつきやすいですね。以下のように記述することができます。
FlagがTrueの間、1秒に1度TestFunctionを呼び出す処理
こちらも余分な評価があったり、Tickがダラダラと長くなるという問題があります。
こちらもTickなしで記述してみましょう。
BPには定期的に処理を実行するのにうってつけな「SetTimerbyEvent」というノードが存在します。
これはEventに接続しているイベントをTime秒後に呼び出すというノードです。LoopingがTrueならTime秒間隔で永遠に呼び出し続けます。
返り値はTimerHandleStructureというもので、これを使用してTimerを止めたり、消去したりすることができます。
それではSetTimerbyEventを用いてTickを使わず定期的に行う処理を書いてみます。
ActivateTimerEventでタイマーをセット、DeactivateTimerEventで消去でき、Timerがセットされている間、1秒間隔でTestEventが呼ばれ続けます。
ActivateTimerEventとDeactivateTimerEventは、③で紹介した方法でFlagの変化を監視するなどして適切なタイミングで呼び出しましょう。
また、GameplayEffectを使用すれば、一定間隔でGameplayAtributeの値を変更することが可能です。
一定間隔で行いたいことが変数の更新だけであるなら、GameplayEffectを使用すると良いでしょう。
まとめ
UEではほとんどTickなしに処理を記述することが可能です。
Tickに書きたいと思うような処理については、大抵の場合UE側で代替手段が用意されているのでいきなりTickに書き始める前に少しでも調べてみることをおススメします!
おまけ
この記事を書いている途中にUnity識者にも話を聞く機会がありましたが、UnityでもUpdateに処理をホイホイ書くのはあまり良い設計ではないそうです。
ある程度の拡張機能を入れるだけで良い設計にできるものが多いそうなので、ぜひそちらも調べてみて下さい。