ECSで新しく追加されたAPI GetSingleton
と SetSingleton
というAPIについてです。
一つだけのComponentDataを取得する機能
GetSingleton<T>()
とSetSingleton<T>(...)
ですが、ComponentDataを一つだけ取得して操作する機能です。
その機能は非常に単純で、同一World内の特定ComponentDataを一つ取得したり設定したりします。Singletonと名前が付いているのは、そのインスタンスが一つだけという前提の機能だからです。*1
Unity的に最もイメージが近いのはGameObject.FindObjectOfType<T>()
でしょうか。これも複数の該当オブジェクトがあった場合でも、単一のComponentしか取得しません。
普通のComponentSystemのフィールドと事なり全てのComponentSystemから参照出来る点、そしてstaticと異なりWorld単位で分離している点が特徴として挙げられます。また参照するデータはEntityのComponentDataなので、JobSystemからそう探しやすいという点も少し便利かもしれません。
悪意のある非常識なややこしさですが、デザインパターンであるSingletonのようなクラスのインスタンスが1つしか生成されないことを保証するデザインパターンではないです。 要素が一つ以上あった時に警告が出るようになってました。違う、そうじゃない
実装例
流れは非常に単純で、GetSingleton<Score>()
でWorld内に存在する最初のScoreComponentDataを取得し、SetSingleton<Score>(...)
で値を反映させます。
なおSingletonと言っても「インスタンスが必ずしもある訳ではない」ので、Entityに何も登録されていなければエラーとなります。例えば今回の場合、ScoreComponentDataが無ければ以下のようなエラーが表示されます。
「InvalidOperationException: GetSingleton<Score>() requires that exactly one Score exists but there are 0.」
これの対策として、初回起動時にComponentDataの有無をチェックします。RequireSingletonForUpdate<T>()
を実行時に起動しておくと、ComponentSystemの要求するGroup内にScoreが登録されます。もし該当のComponentSystemがScoreしか要求してないようなら、コレでComponentSysteは処理をスキップするようになります。
ジョブの終了待ちを行う
GetSingletonが扱うデータはComponentDataのため、JobSystem上で処理することが出来ます。ただし当然他のジョブが操作しているときにGetSingletonを取得する等は出来ません。つまり、GetSingletonは該当のComponentDataを処理するジョブが完了するまで待機します。
これは場合によってはメインスレッドを結構長い時間止めてしまうことになるかもしれません。
感想
殆どのケースで、ComponentSystemのフィールドに値を登録するほうが理に適ってる気がするのですが、どうなんでしょう。
*1:特に制限を設けていないので、複数作ることは可能