- AnimatorControllerはVisualScriptingと考えること
- 再利用できるようにする
- コードと同じように常にメンテナンスする
- パラメーターの数は多くてもいい.アニメーションが多ければ必要なパラメータが増えるのは当然.
- Firewatchではこれくらいパラメータがある
- 大切なのはパラメーターの数を減らすことではなくて,パラメーターをどう扱うか
FPSSampleのアニメーションの実装を読む(後編)
前回の
FPSSampleのアニメーションの実装を読む(前編) - siunのメモ
基本的に大まかな流れはここ読めばわかる
FPSSample/Animation.md at master · Unity-Technologies/FPSSample · GitHub
三人称視点の実装
- すべてPlayableAPIを使って実装してる
- AnimatorControllerの代わりはテンプレートになっていて,AnimGraph_StateSelectorで処理されてる
- 自分以外のすべてのプレイヤーのアニメーションは,以下二つで毎フレーム更新されている
- class ApplyPresentationState : BaseComponentSystem
- AnimStateController.ApplyPresentationState()を毎フレーム実行
- class UpdateCharPresentationState : BaseComponentSystem
- AnimStateController.UpdatePresentationState()を毎フレーム実行
- class ApplyPresentationState : BaseComponentSystem
- ラグの補間は各AnimGraphでやってる
UnityのDrawLineとDrawRayのメモ
1. DrawLineは2点を結ぶ線
点Aと点Bがある時,その2つを結ぶ線を引きたい時使う
2. DrawRayは1点と1距離から作る線
点Aがある時,そこからある方向へ,ある長さの線を引きたい時使う*1
3. メモ
DrawRayは内部でDrawLineを使っている
UnityCsReference/Gizmos.cs at master · Unity-Technologies/UnityCsReference · GitHub
DrawLineだけじゃなくDrawRayも存在してるのは,単純に使いやすさのため.
ある点から100右向きの線引きたい時に
DrawLine(origin, origin + Vector3.right * 100);
と書くより
DrawRay(origin, Vector3.right * 100);
と書けるほうが便利だから
4. 注意点
Rayのdirectionは自動で正規化されるが,DrawRayのdirectionは正規化されない
*1:DrawRay(Ray r)を使う場合は正規化された長さしか引けない.長さを指定する場合はDrawRay(Vector3 from, Vector3 direction)を使う.
C#の構造体は参照渡しで代入するのが一番速い
目次
- newするよりも値を代入するほうが速い
- 返り値にすると一番遅い
1. newするよりも値を代入するほうが速い
gist.github.com
100万回呼んだ場合
代入の方が速い
2. 返り値にすると一番遅い
gist.github.com 100万回呼んだ場合 Aとあまり差がないように見えるが,これは構造体のサイズが小さいからである
構造体のサイズが大きい場合
gist.github.com gist.github.com 100万回呼んだ場合 差がはっきり見て取れる
まとめ
FPSSampleのアニメーションの実装を読む(前編)
1. はじめに
UnityのAnimatorControllerは,ステートの状態が多くなると管理しづらくなる(矢印だらけになる)という問題がある.
そこでUnity公式のサンプルではどのようにアニメーションを実装しているのか知るためソースを読んだ.
2. 前知識
まずソースを読む前に公式のドキュメントを読んでみる.
FPSSample/Animation.md at master · Unity-Technologies/FPSSample · GitHub
三人称視点
FPSSampleはマルチプレイヤーシューティングなので,アニメーションをネットワーク上で同期(複製,ロールバック,予測,ラグ処理など)するためにカスタムPlayable Graphを使用している.一人称視点
ローカルクライアントでのみ再生されるので,AnimatorControllerを使用している. AnimatorControllerを使うとは言ったが,PlayableAPIを使わないとは言っていない(
PlayableAPIのドキュメントも読んでおく.
* Playable API - Unity マニュアル
* PlayableGraph - Unity マニュアル
* ScriptPlayable と PlayableBehaviour - Unity マニュアル
* Playable の例 - Unity マニュアル
3. 一人称視点の実装
まず簡単そうな方から読んでいく.
3.1 ファイル構成
Robot_A_1Pのコンポーネント
3.2 Animatorで再生される大まかな流れ
1. AnimGraph_AnimatorController.csでAnimatorControllerの参照を持つ
2. AnimGraph_Stack.csでAnimGraph_AnimatorController.csの参照を持つ
3. AnimStateController.csでAnimGraph_Stack.csの参照を持つ
AnimatorはAnimationPlayableOutputのsource Playableを再生するので*1それによりアニメーションが再生されている
3.3 PlayableGraph
ソースを読んで作った図.
この1枚にすべてが集約されている.
3.4 AnimGraph_AnimatorController.cs
まずソースを簡略化してみる gist.github.com
- RuntimeAnimatorControllerを保持している
- AnimGraphAssetを継承している
- 内部クラスとして,IAnimGraphInstanceを継承したInstanceがある
親クラスとインターフェースをみる gist.github.com gist.github.com
- InstantiateがInstatiateになっているが,おそらくUnityのAPIと被らないようにするためだと思われる
内部クラスInstanceをみる gist.github.com
- 主な処理をCharacterAnimatorControllerに委譲していることが見て取れる
- EntityManagerがあるので,ECSを使っている模様
CharacterAnimatorControllerをみる
コンストラクタとvoid Update()が大きい処理をしているので,その2つをみていく4.1 CharacterAnimatorControllerのコンストラクタ
gist.github.com- AnimatorControllerPlayableでRuntimeAnimatorControllerをラップしてる
- AnimatorController内のパラメータへのハッシュを,AnimStateParams型やActionStateParams型の配列として保持してる(文字列でパラメータにアクセスするよりint型のハッシュでアクセスする方が早いから)
パラメータ一覧
4.2 CharacterAnimatorControllerのUpdate gist.github.com gist.github.com - AnimatorController内のパラメータを更新している
以上からCharacterAnimatorControllerのUpdateに,アニメーションがとるべき次の状態(CharacterPredictedData.LocoStateや.Action)を渡すことで,アニメーションの状態(AnimatorController内のパラメータ)を更新していることがわかった.
3.5 AnimGraph_Stack.cs
簡略化 gist.github.com
- AnimGraph_AnimatorController.csと同じような構造
- しかしRuntimeAnimatorControllerではなく,List<AnimGraphAsset> を保持している
内部クラスGraphInstance gist.github.com コンストラクタで下図の部分の処理をしてる
3.6 AnimStateController.cs
簡略化 gist.github.com
- コンポーネントが非アクティブになるとDeinitialize()が呼ばれる
- public関数が4つある
public関数の中身をみる(簡略化ver) gist.github.com
- Deinitialize()でグラフの終了処理
- UpdatePresentationState()でグラフロジックを更新
- ApplyPresentationState()でグラフに現在のStateを適用(多分)
Initializeの中身をみる(簡略化ver) gist.github.com
- PlayableGraphを作って再生している
- AnimationPlayableOutputを作る時に,情報源としてAnimGraphのOutputを使っている
Initializeで下図の部分の処理をしている
UV座標のOverrideを理解する
背景
上の画像で,左上にあるテクスチャのUV空間を,GradientNoiseでOverrideした場合,右下のようなテクスチャが得られる.
この時Overrideは具体的にどのような処理を行っているのか理解できなかったので調べた.
UVの基本
まず,UV空間は左下を(0,0)右上を(1,1)として定義されている.
次にこのUV空間をX軸方向成分,Y軸方向成分のみ取り出すと以下のような1次元配列となる.
この1次元配列をUVに繋げた場合,0は(0,0),0.1は(0.1,0.1),1は(1,1)のようにして渡される(注1)
UV座標の(0,0),(0.1,0.1),・・・(1,1)が意味するのは画像のピンクの直線部分である
対応関係を示すと以下のようになる
本題
以上を踏まえると最初の画像が読み解けるようになる
注1
シェーダコードを実際にみて確かめた
上:今回のように値が1つの場合
下:デフォルト
なおvec = (1,2,3)があった場合
vec.xx = (1, 1)
vec.xy = (1, 2)
vec.zz = (3, 3)
となる