Upgrade to Pro — share decks privately, control downloads, hide ads and more …

完全掌握 SpringBone解体新書

COLOPL Inc.
September 19, 2023

完全掌握 SpringBone解体新書

COLOPL Inc.

September 19, 2023
Tweet

More Decks by COLOPL Inc.

Other Decks in Technology

Transcript

  1. 3 はじめに 得られるもの: SpringBoneの内部計算を理解し、 意図せぬ挙動の原因を探ることが できるようになる 物理シミュレーションとは 感覚で付き合うしかないよね ターゲット・必要な知識: •

    プログラムが多少読めるデザイナー • サポートエンジニア • 高校程度の物理 • 感覚的な特徴の話(感想)はしない • より易しい版はBLOGで👉 https://note.colopl.dev/m/m02f17183dd32
  2. 8 Dynamics(揺れもの)とは? DynamicBone 定番の有名な揺れものアセッ ト。Seatライセンスなので組織 によっては管理が煩雑。 Rootに1コンポネ付けるだけと いうお手軽である一方、各ボー ンの詳細設定は限定的。 4つのC#ファイルのだけなの

    で、コードは超簡単。 Unity組み込みJoint ConfigurableJoint一択、 他は用途がかなり限定的。 出来ないことは無いけど、 Joint 単位のプリミティブな機能なの で、チェーンにしようとすると、修 羅の道。 コードレベルの改造不可。 SpringBone(OS版) OSS(MIT)なので、組み込みや すく、コミュニティ情報も得やす い。一方で、派生や初心者によ る情報も多く、信頼できる公式 ガイドも弱い。 システムが比較的大きく複雑。 DynamicBoneが出来ること は、SpringBoneでもだいたい 実現できる。
  3. ゲームシーン 13 コンポーネント解説 SpringBoneの 基本システム SpringBoneManagerと、 その配下のSpringBones のセットで1つの系 キャラモデル SpringManager

    SpringBone SpringBone SpringBone SpringBone SpringBone SpringBone キャラモデル SpringManager SpringBone SpringBone SpringBone SpringBone SpringBone SpringBone ForceProvider ForceProvider キャラモデル SpringManager SpringBone SpringBone SpringBone SpringBone SpringBone SpringBone SpringCollider SpringCollider
  4. 14 コンポーネント解説 void SpringBoneManager.UpdateDynamics() { var timeStep = (simulationFrameRate >

    0) ? (1f / simulationFrameRate) : Time.deltaTime; foreach(var springBone in this.springBones) { if (springBone.enabled) { var sumOfForces = GetSumOfForcesOnBone(springBone); springBone.UpdateSpring(timeStep, sumOfForces); springBone.SatisfyConstraintsAndComputeRotation(...); } } } void SpringBone.SatisfyConstraintsAndComputeRotation(float deltaTime, float dynamicRatio) { currTipPos = ApplyLengthLimits(deltaTime); CheckForGroundCollision() || CheckForCollision(); ApplyAngleLimits(deltaTime); transform.localRotation = …; } ForceProvider SpringBone. UpdateSpring SpringBone. ApplyLengthLimits SpringBone. CheckForCollision SpringBone. ApplyAngleLimits
  5. 24 アルゴリズム解説 // D : 空気抵抗[N] // k : 抵抗係数

    // f': 空気抵抗以外の力 R (t+dt) ≓ R (t) + v * Δt + 1/2 * a * Δt2 ≓ R (t) + v * Δt + 1/2 * f/m * Δt2 ≓ R (t) + v * Δt + f * (Δt2 / 2m) ≓ R (t) + v * Δt + (D + f') * (Δt2 / 2m) ≓ R (t) + v * Δt + (kv + f') * (Δt2 / 2m) ≓ R (t) + v * Δt + (kv * Δt2 / 2m) + (f' * Δt2 / 2m) ≓ R (t) + v * Δt + (k * Δt / 2m) * (v * Δt) + (f' * Δt2 / 2m) ≓ R (t) + (1 + k * Δt / 2m) * (v * Δt) + (f' * Δt2 / 2m) ≓ R (t) + (1 + k * Δt / 2m) * (R (t) - R (t-dt) ) * Δt + (f' * Δt2 / 2m) // ① // dragForceは直接与えられるが、逆算してその意味を考えるなら … dragForce = -(k * Δt / 2m) = -(k/2m * Δt) = -(k/2m * 1/ SimurationFrameRate) = -(k / SimurationFrameRate) / m // Sim頻度の影響を受けるという謎 VelocityVerletの方程式を、変形・近似
  6. 26 アルゴリズム解説 void UpdateSpring(float deltaTime, Vector3 externalForce) { skinAnimationLocalRotation =

    transform.localRotation; // Animation結果を控え var baseWorldRotation = transform.parent.rotation * initialLocalRotation; // 平衡状態だった場合の姿勢 var orientedInitialPosition = transform.position + baseWorldRotation * boneAxis * springLength; // 変数名・単位に要注意 // Hooke's law: force to push us to equilibrium var force = stiffnessForce * (orientedInitialPosition - currTipPos); // F=kx フックの法則の方程式 force += springForce + externalForce; // springForceは重力、externalForceはForceProviderによるテレキネシス var sqrDt = deltaTime * deltaTime; force *= 0.5f * sqrDt; // mass=1の場合の①第3項(もう単位が力ではないけど) // Verlet var temp = currTipPos; force += (1f - dragForce) * (currTipPos - prevTipPos); // ①の第2項 currTipPos += force; // ①第1項に2,3項を加算 prevTipPos = temp; // Boneに鉛直方向の伸びを握り潰し、magnitudeが平衡状態のmagnitudeになるよう矯正 // Inlined because FixBoneLength is slow ... } 先ほどの式が C# に落とし込まれ ている (※コメントは追記)
  7. 27 アルゴリズム解説 制約条件(距離/角度/コライダー) 制約適応時、既に prevTipPos/currTipPosは上書 きされてしまっている… // VelocityVerletの方程式 // R':

    制約条件を加味する前の位置関数(UpdateSpring) // R : 制約条件を加味した位置関数 // (UpdateSpring+SatisfyConstraintsAndComputeRot) // f': 制約条件による力 R (t+dt) ≓ R (t) + v * Δt + 1/2 * (f + f') /m * Δt2 ≓ R (t) + v * Δt + (1/2 * f/m * Δt2) + (1/2 * f'/m * Δt2) ≓ R' (t) + (1/2 * f'/m * Δt2) // ② ※ currTipPosに②の第2項を加算ということ 追加の力を加えたい 第3項だけ計算して継ぎ足 しができる! VelocityVerletの特徴で 力成分が①の第3項に集約
  8. 28 アルゴリズム解説 距離制限 Bone先端↔TransformTargets でバネモデルを適用 (複数Targetsある場合、力のベクトル和) UpdateSpringと同じ springConstant = 0.5

    では小さすぎて影響がほぼ無い Vector3 ApplyLengthLimits(float deltaTime) { const float springConstant = 0.5f; var movement = new Vector3(0f, 0f, 0f); foreach (var targetPosition in lengthLimitTargets) { // currTipPos,targetPosition間で // ①第3項 (f'*Δt2/2m) としてmovementに合計 ... } return currTipPos + movement; }
  9. 29 アルゴリズム解説 角度制限 y方向(ヨー) とz方向(ピッチ) 角に バネモデルを適用 void ApplyAngleLimits(float deltaTime)

    { var origin = transform.position; var vector = currTipPos - origin; var pivot = GetPivotTransform(); // nodePivot→親Transform→自身の順にFallback var forward = -pivot.right; if (yAngleLimits.active) yAngleLimits.ConstrainVector( -pivot.up, -pivot.forward, forward, angularStiffness, deltaTime, ref vector); if (zAngleLimits.active) zAngleLimits.ConstrainVector( -pivot.forward, -pivot.up, forward, angularStiffness, deltaTime, ref vector); currTipPos = origin + vector; } ※ y-zはMayaのy-z相当、実際のUnity座標系ではy-x ここでは、ボーン平衡から見た y、z方向を正面とした座標系の、 基本ベクトル (fundamental vector) で、 AngleLimits.ConstrainVector を実行
  10. 31 アルゴリズム解説 通常のワールド空間から角度空間に変換 // EXP-2の方程式 // C: 角度空間への変換関数 R (t+dt)

    = C-1(C( R (t+dt) )) = C-1(C( R' (t) + (1/2 * f'/m * dt2) ))// ③ また、 UpdateSpringと異なり、角度空間で計算しているので先 端のリニア移動ではなく、回転として処理される
  11. 32 アルゴリズム解説 // Returns true if exceeded bounds bool ConstrainVector(

    Vector3 basisSide, Vector3 basisUp, Vector3 basisForward, float springStrength, float deltaTime, ref Vector3 vector) { var upProjection = Vector3.Project(vector, basisUp); var projection = vector - upProjection; var projectionMagnitude = projection.magnitude; var originalSine = Vector3.Dot(projection/projectionMagnitude, basisSide); // The above math might have a bit of floating point error // so clamp the sine value into a valid range so we don't get NaN later originalSine = Mathf.Clamp(originalSine, -1f, 1f); // Use soft limits based on Hooke's Law to reduce jitter, // then apply hard limits var newAngle = Mathf.Rad2Deg * Mathf.Asin(originalSine); var acceleration = -newAngle * springStrength; newAngle += acceleration * deltaTime * deltaTime; // ③ var minAngle = min; var maxAngle = max; var preClampAngle = newAngle; newAngle = Mathf.Clamp(newAngle, minAngle, maxAngle); // Apply falloff var curveLimit = (newAngle < 0f) ? minAngle : maxAngle; newAngle = ComputeFalloff(newAngle, curveLimit) * curveLimit; var radians = Mathf.Deg2Rad * newAngle; var newProjection = Sin(radians) * basisSide + Cos(radians) * basisForward; newProjection *= projectionMagnitude; vector = newProjection + upProjection; return newAngle != preClampAngle; } STEP.1 入力のBoneベクトルからside-foward 平面上のfowerdとの成す角(sin-angle)を 計算 STEP.2 角度空間(1D)上で、angleを0°方向へバ ネモデルで引っ張る STEP.3 減衰…と見せかけて何もしません! (実装ミス????) STEP.4 angleからBoneのベクトルを再構築 ※ angleはforwardからの角度のヨー成分
  12. 33 アルゴリズム解説 SpringBone.CheckCollision Boneの先端に付いている球 vs Collider で当たり判定 & めりこみ解決 LoadSpringBoneSetupWindow

    SpringBoneManager配下の、SpringBoneを総なめして CSVにパラメータを、シリアライズ/デシリアライズ 時間の都合で、実装の詳細は割愛 …
  13. 35 改造例 距離制限・角度制限 距離制限を、有効に使える ように係数を調整可能に。 角度制限の設定を直感的に 寄せるエディタ改良。 https://note.colopl.dev/n/n1 37afb8296e5 https://note.colopl.dev/n/n9

    01df3b9d8b8 暴れ抑制 高速移動・回転時や、 シーンの開始時に、非現実 的な運動をしたときに発生す る、大きな暴れ。 これを抑制するための色々 https://note.colopl.dev/n/n8 fd987878d51 コライダー形状追加 SpringBoneの先端形状に、 カプセルやボックス形状のコ ライダーを追加
  14. 37 MIT License Copyright (c) 2018 Unity Technologies ApS Permission

    is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. https://github.com/unity3d-jp/UnityChanSpringBone/blob/dev/main/LICENSE SpringBone ライセンス表記