$30 off During Our Annual Pro Sale. View Details »

完全掌握 SpringBone解体新書

COLOPL Inc.
September 19, 2023

完全掌握 SpringBone解体新書

COLOPL Inc.

September 19, 2023
Tweet

More Decks by COLOPL Inc.

Other Decks in Technology

Transcript

  1. 完全掌握💪
    SpringBone
    解体新書
    CA.unity #7

    View Slide

  2. 2
    自己紹介
    春田 晃伸
    2019年に株式会社コロプラへ入社。
    『白猫テニス』の運用を担当後、
    新作『とらべる島のにゃんこ』、技術デモ『PRINCIPLES』などに携
    わりながら、R&D業務を兼務。
    グラフィックスや、最適化など、低レイヤーを得意とする。
    SpringBoneは、2021年から社内要望に応じた
    拡張や、アーティストのサポートを担当

    View Slide

  3. 3
    はじめに
    得られるもの:
    SpringBoneの内部計算を理解し、
    意図せぬ挙動の原因を探ることが
    できるようになる
    物理シミュレーションとは
    感覚で付き合うしかないよね
    ターゲット・必要な知識:
    ● プログラムが多少読めるデザイナー
    ● サポートエンジニア
    ● 高校程度の物理
    ● 感覚的な特徴の話(感想)はしない
    ● より易しい版はBLOGで👉
    https://note.colopl.dev/m/m02f17183dd32

    View Slide

  4. 4
    免責事項
    本解説で述べる内容は、あくまでも、解釈の一つであり、そ
    の正確性を保証しません。
    理解の補助としてご活用いただき、実際にSpringBoneを利
    用する際は、最終的には正確性や適切性を自身で確認し
    てください。

    View Slide

  5. 5
    ● Dynamics(揺れもの)とは?
    ● SpringBoneの歴史・特徴
    ● コンポーネント解説
    ● アルゴリズム解説
    ● 改造例

    View Slide

  6. 6
    ● Dynamics(揺れもの)とは?
    ● SpringBoneの歴史・特徴
    ● コンポーネント解説
    ● アルゴリズム解説
    ● 改造例

    View Slide

  7. 7
    Dynamics(揺れもの)とは?
    物理シミュレーション
    (※必ずしも、現実物理の近似である必要はない)
    → 手付け以外の、プログラムで実行時計算された動き
    👍 アニメーション制作の工数削減
    👍 シーンやモデル外との相互作用の動きの実現
    👎 思い通りに動かない
    👎 その理由も分からず(あまりの理不尽さに、神格化される 󰩃)

    View Slide

  8. 8
    Dynamics(揺れもの)とは?
    DynamicBone
    定番の有名な揺れものアセッ
    ト。Seatライセンスなので組織
    によっては管理が煩雑。
    Rootに1コンポネ付けるだけと
    いうお手軽である一方、各ボー
    ンの詳細設定は限定的。
    4つのC#ファイルのだけなの
    で、コードは超簡単。
    Unity組み込みJoint
    ConfigurableJoint一択、
    他は用途がかなり限定的。
    出来ないことは無いけど、
    Joint
    単位のプリミティブな機能なの
    で、チェーンにしようとすると、修
    羅の道。
    コードレベルの改造不可。
    SpringBone(OS版)
    OSS(MIT)なので、組み込みや
    すく、コミュニティ情報も得やす
    い。一方で、派生や初心者によ
    る情報も多く、信頼できる公式
    ガイドも弱い。
    システムが比較的大きく複雑。
    DynamicBoneが出来ること
    は、SpringBoneでもだいたい
    実現できる。

    View Slide

  9. 9
    ● Dynamics(揺れもの)とは?
    ● SpringBoneの歴史・特徴
    ● コンポーネント解説
    ● アルゴリズム解説
    ● 改造例

    View Slide

  10. 10
    SpringBoneの歴史・特徴
    注意
    ● VRMSpringBoneと混同されがち
    ● 最終コミットが2021
    ● 活発なメンテナンスは…
    ※敬称略

    View Slide

  11. 11
    SpringBoneの歴史・特徴
    揺れものに必要な基礎機能
    慣性/弾性/外力(風・重力)/コライダー/チェーン管理
    スカートに特化した
    貫通防止機能
    (角度制限)
    CSVエクスポーターで
    他キャラに移植できる!

    View Slide

  12. 12
    ● Dynamics(揺れもの)とは?
    ● SpringBoneの歴史・特徴
    ● コンポーネント解説
    ● アルゴリズム解説
    ● 改造例

    View Slide

  13. ゲームシーン
    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

    View Slide

  14. 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

    View Slide

  15. 15
    コンポーネント解説
    風や引力を与える
    ActiveSceneのGameObject
    を総舐めするGlobalな実装
    SpringBone.UpdateSpring
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow

    View Slide

  16. 16
    SpringBone.UpdateSpring
    コンポーネント解説
    ボーンの初期向き(平衡向き)か
    らの変化を、バネモデルで元に戻
    す作用を加える弾性計算
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow ※ ボーンがバネになっているような計算ではない

    View Slide

  17. 17
    コンポーネント解説
    Boneとターゲット間を、
    一定距離に近づける制約
    スカートと太ももなどに設定し、貫
    通抑制に使う想定?
    …ただし、
    係数が小さすぎて
    ほぼ無力😭
    SpringBone.UpdateSpring
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow
    ▲係数を可変にしたデモ

    View Slide

  18. 18
    コンポーネント解説
    → 実は「布」に応用が可能
    クロスシミュレーションでも
    類似アルゴリズムが使われる
    SpringBone.UpdateSpring
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow

    View Slide

  19. 19
    コンポーネント解説
    ターゲットのTransformの回転量
    に近づける制約
    太腿を上げたとき、スカートも同じ
    角度以上に制約、貫通を抑制する
    想定
    SpringBone.UpdateSpring
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow

    View Slide

  20. 20
    コンポーネント解説
    コライダーとの相互作用
    髪が頭や肩に貫通するのを防ぐ
    ただし…
    Bone側の形状はSphereのみなので、Boneに隙間が
    出来がちで、抜ける😭
    SpringBone.UpdateSpring
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow

    View Slide

  21. 21
    コンポーネント解説
    CSVエクスポーター機能
    Boneの名前とパラメータを紐づけ
    て入出力するので
    命名が統一されていると、
    モデル間で設定を使いまわし可
    統一されてなくても、
    CSVなので簡単に変換可能!
    SpringBone.UpdateSpring
    SpringBone.LengthLimits
    SpringBone.AngleLimits
    SpringBone.CheckCollision
    ForceProvider
    LoadSpringBoneSetupWindow

    View Slide

  22. 22
    ● Dynamics(揺れもの)とは?
    ● SpringBoneの歴史・特徴
    ● コンポーネント解説
    ● アルゴリズム解説
    ● 改造例

    View Slide

  23. 23
    アルゴリズム解説
    SpringBone.UpdateSpring
    まずは、制約・コライダの無い状態での
    次のBoneの先端位置を算出する
    VelocityVerlet方程式を使うと、
    現在位置、過去位置、力から、未来位置が求められる
    位置p
    位置p-1 速度v
    加速度a
    力f
    位置p
    位置p+1
    R
    (t+Δt)
    ≓ R
    (t)
    + v * Δt + 1/2 * a * Δt2
    2回積分
    1回積分

    View Slide

  24. 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の方程式を、変形・近似

    View Slide

  25. 25
    アルゴリズム解説
    慣性v が勝てば、オーバーラン
    弾性f' が勝てば、切り替えし
    という挙動
    幾何的なイメージ

    View Slide

  26. 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# に落とし込まれ
    ている
    (※コメントは追記)

    View Slide

  27. 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項に集約

    View Slide

  28. 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;
    }

    View Slide

  29. 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 を実行

    View Slide

  30. 30
    アルゴリズム解説
    見ての通り、扇形の範囲と関係した、
    制約(弾性力)を掛ける
    forwardを中心とした、-90°~+90°
    といった、「角度」に適用

    View Slide

  31. 31
    アルゴリズム解説
    通常のワールド空間から角度空間に変換
    // EXP-2の方程式
    // C: 角度空間への変換関数
    R
    (t+dt)
    = C-1(C( R
    (t+dt)
    ))
    = C-1(C( R'
    (t)
    + (1/2 * f'/m * dt2) ))// ③
    また、
    UpdateSpringと異なり、角度空間で計算しているので先
    端のリニア移動ではなく、回転として処理される

    View Slide

  32. 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からの角度のヨー成分

    View Slide

  33. 33
    アルゴリズム解説
    SpringBone.CheckCollision
    Boneの先端に付いている球 vs Collider
    で当たり判定 & めりこみ解決
    LoadSpringBoneSetupWindow
    SpringBoneManager配下の、SpringBoneを総なめして
    CSVにパラメータを、シリアライズ/デシリアライズ
    時間の都合で、実装の詳細は割愛 …

    View Slide

  34. 34
    ● Dynamics(揺れもの)とは?
    ● SpringBoneの歴史・特徴
    ● コンポーネント解説
    ● アルゴリズム解説
    ● 改造例

    View Slide

  35. 35
    改造例
    距離制限・角度制限
    距離制限を、有効に使える
    ように係数を調整可能に。
    角度制限の設定を直感的に
    寄せるエディタ改良。
    https://note.colopl.dev/n/n1
    37afb8296e5
    https://note.colopl.dev/n/n9
    01df3b9d8b8
    暴れ抑制
    高速移動・回転時や、
    シーンの開始時に、非現実
    的な運動をしたときに発生す
    る、大きな暴れ。
    これを抑制するための色々
    https://note.colopl.dev/n/n8
    fd987878d51
    コライダー形状追加
    SpringBoneの先端形状に、
    カプセルやボックス形状のコ
    ライダーを追加

    View Slide

  36. 36
    https://note.colopl.dev/magazines

    View Slide

  37. 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 ライセンス表記

    View Slide

  38. 38
    ご清聴ありがとうございました!

    View Slide