フィールド・エンジニア 安原 祐二
東京版
白く塗装した2つの球。
片方は純金製、
片方は純銀製。
素材を特定する方法は?
・色、形、大きさは同じ
・質量、重心も同じ
・純金製の球は中空(上図)
・純銀製の球は稠密(下図)
積分
完全マスター
4列
3個
3×4=12
3個×4列=12個
4m
3m
3×4=12
3m×4m=12m2
3m×4m=12m2
3×4=12
4m
3m
長さと長さを掛けると
面積?
ぜんぜん違う話!
12個 12m2
4列
3個
4m
3m
3m×4m=12m2
3×4=12
4m
3m
長さと長さを掛けると
面積?
積分の神秘は
これと同じ
3
極小の長さぶんを
3
範囲ぶん足す
3m
単位が変わる
4m
12m2
極小長さ: 方向の微小区間
(デルタ )という
y
x
x
dx x
で積分する
面積を出す
y
x
x
長さmを長さmで積分→面積m2
長さmを長さmで積分→面積m
面積m を長さmで積分→体積m
2
2
3
積分すると必ず単位が変化する
積分の計算には を使う
計算は呪文のようなもの
理解そのものとは無関係
(いんてぐらる)
Z
例:球の体積(部分)
r
S
t
V =
Z t
r
Sdx
=
Z t
r
⇡(r2
x2
)dx
= ⇡[r2
x
x3
3
]t
r
= ⇡{r2
t
t3
3
( r2
r
r3
3
)}
= (
t3
3
+ r2
t +
2
3
r3
)⇡
円の面積を重ねる
呪文
理解
x
y
〜今回のおはなし〜
力積
慣性テンソル
最小実装
外力
接触
物理シミュレーションの
最小実装
加速度m/s を時間tで積分→速度m/s
2
加速度m/s を時間tで積分→速度m/s
2
velocity += acceleration * dt;
速度m/sを時間tで積分→位置m
加速度m/s を時間tで積分→速度m/s
2
velocity += acceleration * dt;
速度m/sを時間tで積分→位置m
加速度m/s を時間tで積分→速度m/s
2
velocity += acceleration * dt;
position += velocity * dt;
これが物理シミュ
動画
最小実装はこれだ!
public class MyRigidbody : MonoBehaviour {
public Vector3 acceleration;
public Vector3 velocity;
public Vector3 position;
const float dt = 1f/60f;
public void AddForce(Vector3 force) {
acceleration += force;
}
void FixedUpdate() {
velocity += acceleration * dt;
position += velocity * dt;
if (position.y < 0.5f) {
velocity = -velocity;
}
transform.position = position;
acceleration = Vector3.zero;
}
}
Unityの物理更新と
FixedUpdate
1秒
60回
60fpsのゲームは
1秒に60回画面を更新している
dt
1秒
60回
60fpsのゲームは
1秒に60回画面を更新している
dt
余裕
1/60秒
余裕期間は待機
処理
余裕
1/60秒
余裕
1/60秒
1/60秒
余裕
1/60秒
余裕
超過
1/60秒
待機
1/60秒
1/60秒
責任重大
Update
FixedUpdate
物理計算
1/60秒
通常更新
FixedUpdate
物理計算
1/60秒
物理更新
通常更新
Update
FixedUpdate
物理計算
FixedUpdate
物理計算
物理更新は複数回実行される
物理更新
物理更新
責任重大
フレーム
Update
FixedUpdate
物理計算
FixedUpdate
物理計算
Time.fixedDeltaTime
Time.deltaTime
固定
変動
Updateは が変動
責任重大
フレーム
※TimeManagerの設定で事情は変化
Update
dt
FixedUpdate
物理計算
FixedUpdate
物理計算
OnCollision*は    から
OnCollisionEnter
OnCollisionExit
OnTriggerEnter
物理更新
外力
:力
:質量
:加速度
F am=
F
m
a
運動方程式
:力
:質量
:加速度
物理シミュレーションは
位置を知りたい
質量は固定なので
加速度は力
F am=
F
m
a
Fa
m=
運動方程式
速度m/sを時間tで積分→位置m
加速度m/s を時間tで積分→速度m/s
2
velocity += acceleration * dt;
position += velocity * dt;
これが物理シミュ
加速度があれば位置を出せる
力ベクトルは足せる
FixedUpdate
物理計算
物理更新
物理更新
FixedUpdate
物理計算
物理更新
FixedUpdate
物理計算
摩擦力
反発力
重力
AddForce
+)
トータル外力 足す
様々な力を
AddForceは足すだけ
void FixedUpdate() {
Vector3 f = new Vector3(0f, -9.81f, 0f); // 重力
f += new Vector3(1f, 0f, 0f); // 風の影響
f += new Vector3(0f, 1f, 0f); // 浮力
rigidbody.AddForce(f);
}
こんなふうにあらかじめ足しても良い
AddForceをそれぞれ呼ぶのと同じ
外力の応用
浮力
浮力とは
物体が押しのけた水の重さ
潜っている部分の体積
かなりたいへん
潜っている部分の重心
浮力の計算はたいへん
球で代用する
単純な式で出せる!
向きを考慮しなくていい
波の対応も容易
動画
浮力っぽい
浮力の計算はたいへん
球で代用する
V = (
t3
3
+ r2
t +
2
3
r3
)⇡
単純な式で出せる!
向きを考慮しなくていい
波の対応も容易
r
S
t x
y
力積
加速度は
微小期間の速度変化
m
F
m
:力
:質量
:加速度
F=
a
a
a dv
dt
=
運動方程式
加速度は
微小期間の速度変化
mF= a
a dv
dt
=
mF= dv
dt
これも運動方程式
加速度は
微小期間の速度変化
mF= dv
dtmF = dvdt
力積
運動量(速度)の変化
加速度は
微小期間の速度変化
mF= dv
dtmF = dvdt
力積
力積も運動量も
の変形に過ぎない
F = ma
運動量(速度)の変化
F
「力積は運動量を変化させる」
dt期間に発生した力の合算を
速度変化に適用する術
m= dvdt
瞬間的に発生する力に使う
「力積は運動量を変化させる」
dt期間に発生した力の合算を
速度変化に適用する術
m= dvdtF
爆発で吹っ飛ばしたい
爆発力が存在する期間
AddForce
を呼べばいい
が、爆発は一瞬
が大き過ぎて一瞬を作れないdt
ForceMode.Impulse
rigidbody.AddForce(force, ForceMode.Impulse);
ForceMode.Impulse
rigidbody.AddForce(force, ForceMode.Impulse);
内部的には速度の直接変更
( の右辺)
質量は考慮するが は考慮しない
mF = dvdt
dt
ForceMode.Impulse
に依存しない速度変化になる
OnCollisionEnterなどの瞬間的な力
rigidbody.AddForce(force, ForceMode.Impulse);
内部的には速度の直接変更
( の右辺)
質量は考慮するが は考慮しない
mF = dvdt
dt
dt
if (hit) {
rb.AddForce(force);
}
if (hit) {
rb.AddForce(force, ForceMode.Impulse);
}
誤
正
を変えると動作が変わってしまう
瞬間的な外力には力積を使用する
dt
Drag
Dragとは
速度に比例した抵抗力
例:空気抵抗
指定終端速度 を得るための外力
[Unity]RigidbodyのDragから終端速度を得る
https://qiita.com/yuji_yasuhara/items/1f438f0f27f5ef854a73参
考
:質量
:Drag
m
k
F =
1 dt
vmk
k
v F
〜実践的な物理入門〜
https://www.youtube.com/watch?v=FqjM9oujyNE
[Unity道場 札幌スペシャル]
プロが教える脱初心者スクリプト術!
参
考
物理シミュレーションの
ウソ
仮に物理シミュが完璧なら
こんなに設定は必要ない
なにがウソなのか?
あらゆる問題の根源
が大きすぎるdt
なにがウソなのか?
が大きすぎる
あらゆる問題の根源
物理シミュレーションの品質
あらゆる工夫dt
Wikipedia「Newton’s cradle」より
実現困難な例
https://en.wikipedia.org/wiki/Newton%27s_cradle
接触
接触していない
移動
接触!(図形的に算出)
接触=必ず潜り込みが発生
潜り込みを直す
位置と角度を調整
Solve(ソルブ)
と呼ばれる処理
接触は
物理シミュレーションの
最大の難関
多くの進化や工夫は
ここに集約される
下にも物体があったら
調整したせいで潜り込む!
何度も調整
求める完全さは
アプリの特徴次第
負荷大
慣性テンソル
「質量  」
F = m
dv
dt
m
m
F
v
外力 :速度v
質量は「動かしにくさ」
T = I
d!
dt
慣性テンソルは「回しにくさ」
回転ににおける「慣性テンソル 」
F = m
dv
dt
移動における「質量  」
m
I
m
I
T
!
F
v
外力
トルク
:速度v
:角速度!
動画
慣性テンソルを可視化
回しにくい
回しにくい 回しやすい
外積
ここからは武装が必要
行列
テンソル
トルク
外積 〜ベクトルの立体関係〜
どちらのベクトルにも
垂直なベクトル
A
B
A×B外積
B
行列 〜ベクトルに掛けるもの〜
BにPを掛けたらA
AもBもひとつの値(スカラー)なら大きさしか変えない
が、ベクトルの場合は大きさも方向も変えるのでPは行列
0
@
Ax
Ay
Az
1
A
0
@
Bx
By
Bz
1
A
0
@
P00 P01 P02
P10 P11 P12
P20 P21 P22
1
A=
PA =
ベクトル
スカラー
行列
0階テンソル
1階テンソル
2階テンソル
今回扱うのは2階テンソル
〜単なる呼びかた〜テンソル
回転には必ず回転軸がある
〜回転力の記述〜トルク
トルク(と角速度)は
軸ベクトルで表現
回転させる力をトルクと呼ぶ
速度m/sを時間tで積分→位置m
加速度m/s を時間tで積分→速度m/s
2
velocity += acceleration * dt;
position += velocity * dt;
これが物理シミュ
再掲 加速度があれば位置を出せる
加速度があれば位置を出せる
加速度があれば(速度を出せるから)位置を出せる
トルクがあれば姿勢を出せる
加速度があれば位置を出せる
加速度があれば(速度を出せるから)位置を出せる
トルクがあれば姿勢を出せる
加速度があれば位置を出せる
トルクから角速度を出せば姿勢を出せる
加速度があれば(速度を出せるから)位置を出せる
トルクがあれば姿勢を出せる
加速度があれば位置を出せる
トルクから角速度を出せば姿勢を出せる
加速度があれば(速度を出せるから)位置を出せる
トルクと角速度の関係は単純ではない
動画
衝撃的な事実
期待した動き 実際の動き
動画
重心
r
角速度ω ある点pにおける速度vは
重心からのベクトルrと
角速度ωと垂直なので
と外積で表せる
v
点p
!v = ⇥r
重心
r
速度vで移動している点pは
働いた力fの結果そうなっている
f
v
点p
角速度ω !v = ⇥r
重心
r
トルクT
トルクTは
すべての点で働いた力fを
距離を考慮して合計したもの
外積 r×f をぜんぶ足す
X
はぜんぶ足すの意味
ff
f
f
f
点p
fT r=
X
⇥
質点の分布が対称でない場合
トルクT(r×f の総計)と
角速度ω(運動の結果)は
軸が異なる
重心
r
トルクT
f
角速度ω
動画
慣性テンソルの性質
速度と角速度の関係
!v = ⇥ r
運動方程式
= m
d
dt
v
f
fT r=
X
⇥
トルクと力の関係
!T =
X
r ⇥ m
d
dt
( ⇥r)
運動方程式
= m
d
dt
v
f
速度と角速度の関係
!v = ⇥ rfT r=
X
⇥
トルクと力の関係
トルクと角速度の関係!
トルクと角速度の関係
!
=
d
dt
T IT
右辺はベクトル を線形変換している
この変換は必ず行列  とおけて
!
(計算略)
d
dt
I
T =
X
r ⇥ m
d
dt
( ⇥r)
トルクと角速度の関係!
Iこの すなわち
トルクベクトルと角速度ベクトルを関連づける
行列(2階テンソル)のこと
慣性テンソルとは
複雑な状況を行列ひとつで吸収できる驚異
!
=
d
dt
T IT
~A ⇥ ( ~B ⇥ ~C)
= ( ~A · ~C) ~B ( ~A · ~B) ~C
~r ⇥ (~! ⇥ ~r)
= (~r · ~r)~! (~r · ~!)~r
= |~r|2
~! (rx!x + ry!y + rz!z)~r
= |~r|2
~! (rx!x + ry!y + rz!z)
0
@
rx
ry
rz
1
A
= |~r|2
~!
0
@
r2
x!x + rxry!y + rzrx!z
rxry!x + r2
y!y + ryrz!z
rzrx!x + ryrz!y + r2
z!z
1
A
= (r2
x + r2
y + r2
z)~!
0
@
r2
x rxry rzrx
rxry r2
y ryrz
rzrx rxry r2
z
1
A
0
@
!x
!y
!z
1
A
=
0
@
r2
y + r2
z rxry rzrx
rxry r2
x + r2
z ryrz
rzrx ryrz r2
x + r2
y
1
A ~!
参考
密度と
物体形状で  を
積分したものが
慣性テンソル
I
0
@
r2
y + r2
z rxry rzrx
rxry r2
x + r2
z ryrz
rzrx ryrz r2
x + r2
y
1
A
x
y
z
a
b
c
m
abc
Z c
2
c
2
Z b
2
b
2
Z a
2
a
2
(y2
+ z2
)dxdydz
=
m
12
(b2
+ c2
)
I =
0
@
m
12 (b2
+ c2
) 0 0
0 m
12 (c2
+ a2
) 0
0 0 m
12 (a2
+ b2
)
1
A
例:直方体の慣性テンソル
9要素すべてを計算
Rigidbody.inertiaTensor
Rigidbody.inertiaTensorRotation
これはなんだ
さて
Rigidbody.inertiaTensor
Rigidbody.inertiaTensorRotation
どういうこっちゃ
型が…
Vector3
Quaternion
慣性テンソルは対角化できる行列
行列は二種類ある
対角化できるか、できないかだ!
0
@
A00 0 0
0 A11 0
0 0 A22
1
A
0
@
b00 b01 b02
b10 b11 b12
b20 b21 b22
1
A
対角化とは0
@
a00 a01 a02
a10 a11 a12
a20 a21 a22
1
A
対角行列に変換する術
対角行列
0
@
b00 b01 b02
b10 b11 b12
b20 b21 b22
1
A
1
=
0
@
A00 0 0
0 A11 0
0 0 A22
1
A
0
@
b00 b01 b02
b10 b11 b12
b20 b21 b22
1
A
0
@
a00 a01 a02
a10 a11 a12
a20 a21 a22
1
A
0
@
b00 b01 b02
b10 b11 b12
b20 b21 b22
1
A
1
=
IdIr
I
Ir 1
0
@
A00 0 0
0 A11 0
0 0 A22
1
A
0
@
b00 b01 b02
b10 b11 b12
b20 b21 b22
1
A
0
@
a00 a01 a02
a10 a11 a12
a20 a21 a22
1
A
0
@
b00 b01 b02
b10 b11 b12
b20 b21 b22
1
A
1
=
IdIr
I
Ir 1
I = IrIdIr 1
Rigidbody.inertiaTensor
Rigidbody.inertiaTensorRotation
対角行列なのでVector3で表現可能
回転行列なのでQuaternionで表現可能
Id
Ir
I = IrIdIr 1
動画
具体的に慣性テンソルを使う
public class SimpleRotator : MonoBehaviour {
void Update() {
if (Input.GetMouseButton(0)) {
var rb = GetComponent<Rigidbody>();
var omega = new Vector3(0f, 1f, 0f);
var R = transform.rotation;
var RI = Quaternion.Inverse(transform.rotation);
var Id = rb.inertiaTensor;
var Ir = rb.inertiaTensorRotation;
var IrI = Quaternion.Inverse(Ir);
var torque = R * Ir * Vector3.Scale(Id, IrI * RI * omega);
rb.AddTorque(torque, ForceMode.Impulse);
}
}
}
鉛直の角速度をもたらすトルクを得るサンプル
(現在の姿勢も考慮したもの)
白く塗装した2つの球。
片方は純金製、
片方は純銀製。
素材を特定する方法は?
・色、形、大きさは同じ
・質量、重心も同じ
・純金製の球は中空(上図)
・純銀製の球は稠密(下図)
転がす
答え
同じ質量なら中空の純金製のほうが
慣性テンソルが大きい。よって
回転しにくいほうが純金製
おしまい

【Unity道場】物理シミュレーション完全マスター