Nvidia Flexの水をカスタマイズしたときの最適化に関するメモ
z軸固定
全パーティクルについてfor分を回して、GetVelocity SetVelocityは重い。
全パーティクルは3000程度いくのできっつい。
新しくループを作るのではなく、もとのNvidiaを書き換える事で対応。
→ 書き換えたのは、新規に生成された水粒子に関してのみだったのでダメだった。
新規の水粒子以外でSetVelocityが呼ばれているのは、ApplyImpulseとTeleportParticlesだが、DebugLogしてもどちらも呼び出されていないようだった。
しかし、CustomWater.cs内のOnFlexUpdateでGetVelocityするとzの値が0ではなくなっている。
つまりSetVelocity以外の部分でVelocityの変化が起きていることになる。
原因を探ったがよく分からず(ネイティブプラグイン内かも)、その過程でSetVelocities関数を発見した。
SetVelocityをfor分で回すより効率が良いと考え、利用してみた。
var velocities = new Vector3[indices.Length]; particleData.GetVelocities(indices[0], indices.Length, velocities); for(var i = 0; i < velocities.Length; i++) velocities[i].z = 0; particleData.SetVelocities(indices[0], indices.Length, velocities);
結果、ほぼ無視できるレベルに軽量化に成功した。
が、代わりにとんでもないGCが発生した。
原因はGetVelocitiesの時のnew Vector3[]だろう。

ちなみに、SetVelocityを使った場合との比較は↓である。

| 手法 | Total | GC |
|---|---|---|
SetVelocity |
2.90ms (7.6%) | 29.5kB |
SetVelocities |
0.03ms (0.1%) | 約60kB (スパイク時:117.7kB) |
SetVelocitiesを使う場合はindicesはスタートだけあればよいので、
int[] indices = new int[instance.numParticles]; → int[] indices = new int[1]; に変える事でnewを減らすことに成功。
GCを60kB → 44kB (スパイク時:88kB)まで減らした。
ここでのスパイクはFixedUpdateが1フレーム内で2回呼ばれた時の話だと考えられる。

さらに、_velocitiesをクラス変数にして初期化する事でnewを呼ぶ回数は最初の初期化時に限定。
結果、GCを44kB → 0.072kB (72B) に減らすことに成功!
private Vector3[] _velocities; private void Start(){ _velocities = new Vector3[_flexActor.asset.maxParticles]; } private void OnFlexUpdate(FlexContainer.ParticleData particleData){ particleData.GetVelocities(indices[0], instance.numParticles, _velocities); for(var i = 0; i < _velocities.Length; i++) _velocities[i].z = 0; particleData.SetVelocities(indices[0], instance.numParticles, _velocities); }

これにて、Z軸固定は完了した。
当たり判定及びAddForce
上記同様に、GetVelocities 及び GetParticlesによる軽量化を行った。
さらに、IsInCollider(Vector3 pos) 内の targetCollider.ClosestPoint(pos)が非常に重い。
これは水粒子がコライダー内部にあるかの判定に利用していた。
そこで、コライダーをSphereColliderに限定して、水粒子のコライダー内部判定を(particle - colPos).magnitude < radiusで置き換えた。
汎用性は下がるが、単純なVector3の演算のみになる。
実際にこれでかなりの軽量化に成功した。
しかしながら、magnitudeの計算等でそれなりに処理時間を要していた。
そこで今回はz軸方向を利用しないという点を活かして、Vector3の演算からVector2の演算に変更を行った。
これでさらに若干軽くなった。


| 手法 | Total | GC |
|---|---|---|
WaterCollider |
22.50ms (37.0%) | 67.4kB |
WaterSphereCollider |
1.70ms (6.6%) | 36B |



