BoneCollectionに関する実装について
About
αバージョンではBoneCollectionは空のコンストラクタを公開し、BoneManagerなるBoneCollectionを管理するクラスも実装されていた。しかしながらβバージョンではこれを取り除き、BoneCollectionは空のコンストラクタを公開しなくなった。主な理由は次のとおりである。
あるJointを変更した時、対応するBoneが変化する、という対応がとれない。
BoneCollectionはその内部にJointCollectionを持たず、またBoneManagerはJointCollectionも同時に管理するが、BoneCollectionとJointCollectionを同期する機能は持っていなかった。BoneCollectionにJointCollectionを持たせることは酷く冗長である。
冗長性の排除
基本的に、各Jointの組み合わせから構成されるBoneCollectionに対し、まったく同じJointを独立して持ったJointCollectionを追加することは避けるべきである。これはBoneManagerとJointManagerの関係にも言える(BoneManagerはJointManagerの拡張で、内部でJointCollectionのQueueとBoneCollectionのQueue2つを持っていた)。データの重複による冗長性の加速ということもあるし、アクセスの冗長性と言うことでもそう。参照関係もやや複雑になってくる。
条件分岐の回避と最適化
一方で、BoneCollectionに対しインデクサからアクセスした際に、それに応じたJointCollectionから新規にBoneを生成し、そのインスタンスを返す、という手法で実装しようとすると、インデクサ→Boneインスタンスの生成の間に、どのJointを参照するべきか、の条件分岐が生じる。さらに、仮に固定されたプロパティによる実装で条件分岐を回避しようとすると、それはJointCollectionのプロパティとして実装すれば良いということになる。
またBoneCollectionに対して、あるJointを指定して更新しようという場合、コンストラクタなどから予めBoneインスタンスを生成する実装形態をとっていると、少なくとも二つのBoneを参照して更新しなければならない。ここに条件分岐が生じるのは先に説明した通りであるし、よしんば内部的にJointCollectionを持っていて、Jointの参照だけ変更すれば良い、という手法を取るなら、そもそもJointCollectionのプロパティとして同じことをすれば良い、というのも先に説明した通り。
BoneCollectionとJointCollectionEx.Bone系プロパティの使い分け
そういうわけでBoneCollectionは空のコンストラクタを持たず、使い捨てのためのCollectionになった。多くのBoneの参照が必要な場合、例えばAとBとCとDとEとFの骨の長さを求めて合算する、といった場合は、JointCollectionExからBoneCollectionを受け取ってインデクサによる参照を行った方が恐らく簡潔である。また全てのBoneに対して再帰的な処理を行う場合は、BoneCollectionが必須になる。
一方で、ある特定のBoneが欲しい場合には、JointCollectionExの該当プロパティを呼び出せば、内部のJointデータからBoneインスタンスを生成して返されるのでBoneCollectionは特に必要としない。BoneCollectionとBoneプロパティの使い分けは、Boneの参照数で決定されるべきである。
BoneManagerが廃止され、JointManagerのみ提供されるようになったが、同様の処理は変わらず実装可能である(つまりEKL全体から冗長性が排除された)。数フレーム分のBoneCollectionが欲しい場合は、JointManagerにJointCollectionExを入れておく(JointManager.Updateメソッドによる)。そして複数のBoneCollectionが必要になった際に、JointManagerから該当のBoneCollectionを生成することが出来るJointCollectionExを呼び出せばよい。
ここで、JointCollectionExからBoneCollectionのインスタンスを生成する場合に、各Boneインスタンスの生成が実行されるため、多少の処理負荷がかかる。複数のBoneCollectionを一度に生成することで、リアルタイムアプリケーションの実装に干渉する可能性がある。旧実装では、BoneManagerによって各フレームでBoneCollectionのインスタンスを生成しておき、複数必要になった場合は既存のBoneCollectionを参照するので1フレームあたり、1度の処理負荷で済んでいた。この実装の変更が問題を起こす可能性はあるが、処理負荷は参照と新スタンスの生成のみであって、特に大きいわけではなく、また全身のBoneを対象とするために、一度に複数のフレームを参照する場合は少ないと判断した。
全身のBoneを対象とする処理の殆どは、全てのJointを対象とする処理に置き換えることが出来るので、どうしても高速化が必要である場合には、JointCollectionからJointを参照し、該当する処理の実装を行うことで解決できる可能性がある。