UpdateとFixedUpdate
UnityのUpdateとFixedUpdateには次のような使い分けが必要になっています。
-
Inputによる入力イベントはUpdate()内でしか正しく取得できない -
RigidBodyへの操作はFixedUpdate()内で実行しないと正しく動作しない
そのため、「入力イベントをもとにRigidBodyを操作する」といったことがしたくなった場合、Update()からFixedUpdate()のタイミングへと処理のタイミングをずらす必要があります。
今回はこういった処理をUniRxを使って書いてみます。
「タイミング」のみをUpdateからFixedUpdateへ変換する
「ジャンプボタンが押された」といった入力されたタイミングが重要で、メッセージの値そのものに価値がない場合はBatchFrameオペレータが利用できます。
BatchFrameオペレータ
BatchFrameは「OnNextメッセージをバッファにたくわえ、指定したUnityのイベントタイミングに同期して出力する」といったオペレータです。
ジェネリクスをとる場合とUnit型を取る場合で挙動が異なるのですが、今回はUnit型版を利用することにします。
Unit型をとるBatchFrame
public static IObservable<Unit> BatchFrame(this IObservable<Unit> source, int frameCount, FrameCountType frameCountType)
-
this IObservable<Unit> source: 入力となるストリーム -
int frameCount: 何フレーム分遅延させるか。0を指定した場合は次のイベントタイミングになる。 -
FrameCountType frameCountType: Unityのどのイベントタイミングに同期させるか。
Unit型をとるBatchFrameはシンプルに「イベントのタイミングを変換する」といった挙動をします。
ジェネリクスを取る場合は複数入力されたイベントをバッファするのですが、Unit型を取る版ではバッファの長さは1に固定されています。
実際のコード
実際にBatchFrameを使ってUpdateからFixedUpdateへタイミングを変換するコードになります。
using UniRx;
using UniRx.Triggers;
using UnityEngine;
public class RigidBodyMover : MonoBehaviour
{
void Start()
{
var rigidBody = GetComponent<Rigidbody>();
this.UpdateAsObservable()
.Where(_ => Input.GetKeyDown(KeyCode.Space))
.AsUnitObservable() // Unit型に変換
.BatchFrame(0, FrameCountType.FixedUpdate) //入力メッセージを次のFixedUpdate時に出力する
.Subscribe(_ =>
{
rigidBody.AddForce(Vector3.up * 10, ForceMode.VelocityChange);
});
}
}
このように、 BatchFrame(0, FrameCountType.FixedUpdate) を指定することで、入力イベントを次のFixedUpdateのタイミングへと移動させることができます。
Update内で設定された値をFixedUpdate内で利用する場合
Input.GetAxisのような、最新の入力値をFixedUpdateで利用したいといった場合はWithLatestFromオペレータが使えます。
WithLatestFromオペレータ
WithLatestFromは、1つのストリームを主軸とし、そこに別のストリームの最新値を合成するオペレータです。
public static IObservable<TResult> WithLatestFrom<TLeft, TRight, TResult>(this IObservable<TLeft> left, IObservable<TRight> right, Func<TLeft, TRight, TResult> selector)
-
this IObservable<TLeft> left: 主軸になる方のストリーム。このストリームの入力タイミングで出力される。 -
IObservable<TRight> right: ペアにする方のストリーム。このストリームの最新値が主軸のメッセージと合成される。 -
Func<TLeft, TRight, TResult> selector: 入力値を加工して最終結果に変換する関数。
実際のコード
実際にWithLatestFromを用いて、Updateの入力値をFixedUpdate時に取り出すコードになります。
using UniRx;
using UniRx.Triggers;
using UnityEngine;
public class RigidBodyMover2 : MonoBehaviour
{
void Start()
{
var rigidBody = GetComponent<Rigidbody>();
//入力ストリーム
var inputStream = this.UpdateAsObservable()
.Select(_ => new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")));
//FixedUpdateを主軸にし、そこにinputStreamを合成する
this.FixedUpdateAsObservable()
.WithLatestFrom(inputStream, (_, input) => input)
.Subscribe(input =>
{
rigidBody.AddForce(input, ForceMode.Acceleration);
});
}
}
まとめ
UniRxにはいろいろオペレータが用意されており、その組み合わせで意外となんでもできてしまいます。
機会があればオペレータを全部まとめて紹介したいです。
