Swift Playgroundsで学ぶiOSプログラミング 第99回
3Dオブジェクトに重力を与えるには?
「物理学体」と「物理学場」を設定して物理現象をシミュレーション
2018年08月01日 17時00分更新
SceneKitを使った仮想の3D世界の描画も、いろいろな形の物体の配置から照明やカメラの設定を経て、前回は物体を表すノードに強制的に動きを加えるアニメーションを実現しました。その際にも述べましたが、SceneKitのシーンの中に配置したノードを動かす方法には大別して2種類あります。1つは前回のように、周囲の環境や物体の性質などは無視して、外部からの操作で強制的に動かす方法でした。そしてもう1つの方法が、今回取り上げる物理現象によるシミュレーションの結果として動かす方法です。
人間にとって身近な物理現象と言えば、地球上ならどこに行っても逃れることができない重力の影響によって生じるものがあります。重力は単純に考えれば、物体を「下」に引く力です。それを模して、SceneKitのシーンにも、常に下、つまりy軸のマイナス方向にノードを引っ張る力が働いています。ただし、この力はすべてのノードに漏れなく作用するわけではありません。
ノードに物理的な力を作用させるには、個々のノードに直訳すれば「物理学体」と呼ばれるオブジェクトを設定することが必要です。この設定のないノードは、いくら物理的に強い力が働いていても微動だにしません。つまり物理学体を持たないノードは、見かけ上そこに在るように見えるだけで、実際には存在していないのも同じなのです。ノード同士が重なってもすり抜けてしまいます。
また、シーンには、重力以外にもさまざまな力を働かせることができます。それは「物理学場」というオブジェクトをノードなどに設定することで実現できます。それによって周囲のノードにも影響する力が有効になります。
まずは、シーンに最初から働いている重力の影響を確かめましょう。その後、別の力を働かせて、その場に置いたノードの動きを観察します。さらに、その場の中でノードを叩くような物理力を加えて、その結果、叩かれたノードはもちろん、それに接している周囲のノードがどう動くかも観察してみましょう。その動きが、前回のアニメーションとは、まったく異質のものであることは、一目でわかるはずです。
「物理学体」を設定して、垂直降下の動きを確認する
上でも述べたように、このところずっと扱ってきたSceneKitのシーンには、実は最初から常に一定の重力が働いていたのです。しかしこれまでのところ、それによると思われる動きは観察できませんでした。床の上に載っているいろいろな形のノードは、もし本当に床に「載って」いるのなら、床が支えになって下に落ちないかもしれません。しかし、考えてみれば床自体には支えも何もないので、重力が働けば床ごと下に落ちて行ってしまうはずです。
重力があるのに床が落ちなかったのは、ほかでもない、その床やそのほかのノードにSCNPhysicsBodyのオブジェクト、ここで言う物理学体が設定してなかったからです。つまり床もその他の物体も、ただ見えているだけで、物理学的には存在していないのも同然だったわけです。
それでは、さっそく物理学体を設定しみましょう。SCNPhysicsBodyのオブジェクトには3種類あります。一般的な物体は「ダイナミック」タイプです。これは、重力などの影響を受けて動き、物理学体を設定してあるほかのノードとぶつかって跳ね返ったり、動きや力を伝えたりします。それに対して「スタティック」タイプの物理学体は、重力や、その他の力が加わってもその場を動きません。しかし、物理学的に存在はしているので、他の物理学体とぶつかります。
その結果、ほかの物理学体を跳ね返すことはできますが、スタティックタイプの物理学体は動きません。このようなタイプは、例えば床や地面など表現するのに適しています。もう1つ、「キネマティック」タイプというものもあります。これはダイナミックとスタティックの中間的な存在です。スタティック同様、重力や衝突などの外力によってそれ自体が動くことはありません。しかし、それ自体を強制的に動かすことで、ダイナミックのように他の物理学体に力を伝えることはできます。例えば、エスカレーターとか、カタパルトのようなものの表現に適しているでしょう。
ここでは、前々回から示している床の上に様々なジオメトリから作ったノードを並べるプログラムに手を加えていきます。まず床のノードのphysicsBodyプロパティには、スタティック(.static)のタイプを指定して作成したSCNPhysicsBodyのオブジェクトを設定しています。また、床の中央に配置した四角い箱のノードには、ダイナミック(.dynamic)タイプの物理学体オブジェクトを設定しました。
また四角い箱のノードは、位置を床の上50.0まで持ち上げました。その隣のカプセルのノードは同じく30.0の位置に持ち上げましたが、こちらは物理学体は設定していません。その違いを観察しようというわけです。
なお、SCNPhysicsBodyオブジェクトを作成する際には、その物理学体の形状を指定することもできます。しかし、ここではいずれもnilを指定しています。そうすることで、何も指定しなくても、それを付加するノードと同じ形状にできるのです。
このプログラムを動かすと、初期状態で高い位置に置かれた箱は、重力に従って落ちていきますが、床にぶつかってちょっとだけ跳ね返り、少し向きを変えて止まります。一方、カプセルは、床の上の空中に止まったままです。物理学体を設定してないので、重力が働かないからです。
この状態では、もちろん床にも重力は作用していますが、タイプがスタティックなので、そこから落ちてしまうことはありません。
「物理学場」を設定して、重力よりも強い力で物体を引き付けてみる
シーンに最初から存在している重力による動きはだいたい分かりました。これは自然界の重力を模したものでもあり、それほど興味深い動きが見られるわけではありません。そこで、自然界には存在しにくい新たな力を発生させてみましょう。それを可能にするのが、SCNPhysicsFieldクラスのオブジェクト、ここで言う物理学場です。
SCNPhysicsFieldクラスでは、静電気力や磁力、重力といった自然界のどこにでもあるような力に加えて、ちょっとここでは挙げきれないようなさまざまな力を発生させることが可能です。その中で、結果がはっきりとわかりやすいものとして、バネ的な力を試してみることにします。バネそのものは見えませんし、物理学体として存在もしませんが、この力が働いている場では、物理学体を設定したノード同士が、あたかもバネでつながれたように引き合います。片側がスタティックであれば、そちらの方向に一方的に引かれるので、力の加減は違うものの、強い重力が働いているかのような動きが得られます。
このバネ的な力は、SCNPhysicsFieldのクラスメソッドspringによって生成し、ノードのphysicsFieldプロパティに設定します。ここでは、床自体のノードに、この物理学場を付加してみました。
この際、シーン全体に最初から作用している重力は切っておくことにしました。重力はこのバネ力よりずっと弱いのでそのままでも構わないのですが、デフォルトの重力がどこに付いていてどうやってオフにできるかを示すためです。それは、シーンのphysicsWorldの、さらにgravityというプロパティに3次元ベクトルとして設定してあるのでした。ここで、すべての要素がゼロのベクトルを指定すれば、デフォルトの重力をオフにすることができます。
また、床の上に配置した四角い箱以外のノードにも、すべて物理学体を設定しました。
これによって、床の上に配置されたすべてのノードは、かなり強い力で床の中央に引き寄せられるはずです。プログラムを動かして確認してみましょう。
最初から床から離れたところに配置した四角い箱とカプセルは、床の中央に引き寄せられるように落ちていき、それ以外のノードは、床の上をスライドしながら中央に寄り集まってしまいます。
この連載の記事
- 第100回 SceneKitの物理現象シミュレーションとアニメーションをARKitに持ち込む
- 第98回 SceneKitのノードに動きを加えるプログラム
- 第97回 いろいろな形のノードをシーンの中に配置する
- 第96回 SceneKitの基礎シーンビュー、シーン、ノードを理解する
- 第95回 現実世界の床にボールや自動車のモデルを配置する
- 第94回 ARKitを使って非現実世界との融合に備える
- 第93回 ARKitが使えるiPadを識別するプログラム
- 第92回 Swift Playgrounds 2.1での問題点をまとめて解消する
- 第91回 iPadの内蔵カメラで撮影した写真を認識するプログラム
- この連載の一覧へ