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にはいろいろオペレータが用意されており、その組み合わせで意外となんでもできてしまいます。
機会があればオペレータを全部まとめて紹介したいです。