Swift Playgroundsで学ぶiOSプログラミング

SpriteKitを使って万有引力の法則をシミュレートする

文●柴田文彦 編集●吉田ヒロ

2017年07月24日 17時00分

重力のある場に「看板」を放り出す?

 今回のプログラムは、前回の最後に作った「ASCII倶楽部」のロゴがぐるぐる回ったりしながら消えていくものに手を加えるところから始めることにします。しかし、手を加えるといっても、プログラムとしてはむしろ単純になります。前回のプログラムから、強制的にアニメーションを加えるアクションに関するものを取り除き、そこに物理的な特性の設定を加えるといった感じです。

 前回と異なるのは、SpriteKitのシーンSKSceneから派生させたクラス、PGSceneを作る部分だけです。

前回のロゴが回転して消えていくアニメーションを表示するプログラムのシーン(SKScene)を定義する部分をこのように変更します

 まず、シーンがビューに張り付けられたタイミングで実行されるdidMoveメソッドの中で、前回は単にシーンの背景色を薄いグレーに設定しただけでした。今回は、そこにシーン全体の物理的な性質を決定するための2行を追加しています。まず、シーンオブジェクトのphysicsBodyプロパティを設定しています。これは、簡単に言えば物体の大きさを指定するものです。ここでは、シーンという場が、ちょうど画面の大きさと一致するように、四辺に壁を作るような処理をしていると考えてください。これで中に置いた物体は画面からはみ出すことがなくなり、画面の端に衝突して、はね返ったりするようになります。

 もう1つのphysicsWorldプロパティのgravityは、このシーンの中で働く重力を設定しています。水平方向を表すdxはゼロ、垂直方向は-9.8、つまり下方向に地球上とほぼ同じ重力加速度が作用するような設定です。

 もう片方のメソッド、ユーザーが画面にタッチした際に呼ばれるtouchesBeganの中では、シーンの中に「ASCII倶楽部」のロゴをスプライトとして配置しています。タッチの検出、スプライトの作成、タッチした場所へのスプライトの配置までは前回とまったく同じです。

 そのあとは、ひたすらスプライトオブジェクトのプロパティを設定しています。まず、今回の目的のためにはちょっとロゴ画像が大きすぎるので、縦横とも半分のサイズにスケーリングしています。そしてその大きさに対してphysicsBodyプロパティを設定し、ロゴ画像の外枠が、そのまま物体としての大きさとなるようにしています。

 あとは、そのロゴのphysicsBodyに対して物理的な特性を設定していきます。massは質量(単位はkg)、restitutionは反発係数、linearDampingは線方向(回転でない)の減衰を表す係数、そしてallowsRotationは、この物体が回転(自転)することを許可するかどうかを表します。最後に物理的な特性を設定したスプライトを、addChildメソッドによってシーンに張り付ければ完了です。

 このプログラムを動かすと、前回同様薄いグレーの画面が表示されるので、どこかにタッチしてみましょう。その位置に前回の半分の大きさの「ASCII倶楽部」ロゴが表示され、そこから画面の下に向かって「落ちて」いきます。

上のプログラムを動かすと、今回も薄いグレーの背景の空のシーンが表示されます。その中のどこかにタッチすると、そこに「ASCII倶楽部」のロゴが表示され、そのまま下に落ちていきます

 下まで落ちると、画面の下辺で少し跳ね返りますが、やがて落ち着きます。一度シーンに追加したロゴは落ちてもそのまま残るので、ロゴを追加していくと、シーンの中にどんどん溜まっていきます。すでに落ちているロゴに対してずらして落とせば、打ち所によって回転したりするので、縦になったり、逆さまになったり、場合によっては斜めになったりすることが観察できるでしょう。

ロゴをどんどん落としていくと、そのまま積み重なったり、衝突する位置によっては回転して向きが変わったりしながら、シーンという場の中に溜まっていきます

絵文字から作ったボールを転がす

 「ASCII倶楽部」のロゴは、四角くて回転しにくいので、何か丸い物体を使って、シーンの中で転がる様子を観察してみましょう。

 転がる丸い物体と言えば、真っ先に思いつくのはボールですね。ロゴ画像の代わりにボールの画像を用意すればいいのですが、もっと簡単な方法があります。それは絵文字のボールを利用することです。前回の最初に、文字列からラベルノードを作ったのを憶えているでしょうか。ラベルノードもスプライトと同様に機能します。

 ただし、今回は丸い物体として振舞ってくれるように、丸いphysicsBodyを設定する必要があります。実は半径と中心の座標を指定してphysicsBodyの形状を円に指定することも可能です。それを利用すれば絵文字のように、文字としての枠(長方形)と、目に見える形が一致していない場合でも丸い物体に仕立てることができます。

 この例では、絵文字からバスケットボールを選び、フォントサイズを指定し、その文字から丸いボール部分だけを切り出してphysicsBodyに設定してみました。

ロゴの画像から作るスプライトノードの代わりに絵文字のバスケットボールからラベルノードを作ります。physicsBodyは、半径と中心のズレを指定してボールを囲う円形にします。画面にタッチした位置には、バスケットボールが出現し、下に落ちていきます。ボール通しをぶつけると、回転しながら飛び跳ねたりします

 ほかの部分は上の例と同じです。これを動かして画面に触れると、その位置にバスケットボールが出現して、やはり下に落ちていきます。そのままでは「床」で垂直に跳ね返るだけで回転しませんが、すでに落ちているボールの上にちょっとずらして落とすと回転しながら跳ね返り、その後で床に落ちてから転がる様子が観察できます。

落ちたボールを弾いてみる

 ここまでのプログラムでは、いったんシーンの中に追加したボールは、重力で下に落ちていくだけで、ほかのボールをぶつけたりすることを除くと、あとから直接「触る」ことはできませんでした。次に、すでにシーンの中に落ちているボールに力を加えて動かしたり、回転させたりする方法を考えてみましょう。

 SpriteKitでは、ユーザーがタッチした場所にある物体(ラベルノードやスプライトノード)を検出することができます。シーンの中でタッチされた位置を指定して、そのシーンのatPointメソッドを呼び出すと、その位置にあるノードそのものが返ってきます。ここでは、ボールを配置する際に、あらかじめ「ball」というnameを設定しておき、タッチしたものがボールであるかどうかは、そのnameを調べて判断します。すべてのボールが同じ名前でいいのかという疑問もあるかもしれませんが、これは何もないところにタッチした場合を除外したいだけなので、それで十分です。どのボールにタッチしたかは、atPointメソッドが返すオブジェクトが、個々のボールそのものなので、自明のことなのです。

 ここでは、すでに落ちているボールにタッチした場合には、そのボールのphysicsBodyに対してapplyImpulseメソッドで、打撃的な力を水平、垂直方向、それぞれランダムに加え、applyAngularImpulseメソッドで、回転力をやはりランダムに与えています。

すでにシーンに追加したボールを検出できるよう、ボールにはあらかじめ「ball」という名前を設定しておきます。ユーザーがタッチしたノードがボールであれば、そのボールにランダムな力と回転を与えます

 すでに配置されているボールにタッチすると、そのボールが再び動き出します。

何もないところにタッチすると、そこにボールが出現するのは上の例と同じです。ここでは、すでに配置されているボールにタッチしてみましょう。そのボールが再び動き出します

 何もないところに触れると新しいボールが現れて落ちていき、すでにあるボールに触れると、そのボールに新たな動きが加わることを確かめてください。

違う絵柄のボールをランダムに発生させる

 今回の最後の例は、ちょっとした余興のようなものです。ここまではひたすらただ1種類のバスケットボールを発生させていました。それだけではちょっと構図的に寂しいので、いろいろな絵柄のボールをランダムに発生させてみることにします。

 まずPGSceneの定義の先頭で、とりあえず5種類のボールの絵文字を配列として用意します。あとは、touchesBeganメソッドの中でラベルノードを作成する際に、ボールの絵文字の配列から、どれか1つをランダムに選びます。

やはり絵文字を使って5種類のボールの絵柄をあらかじめ用意しておきます。ユーザーが画面にタッチしてボールを出現させる際には、乱数を使って5種類のボールの中から1つの絵柄を選んでいます

 このプログラムを動かしてみると、これまでよりはだいぶバラエティー感のある画面が表示されます。

機能としては上の例とまったく同じですが、画面にタッチするたびに異なる絵柄のボールがランダムに現れるので、なんとなく楽しさが増します

 動きとしては、上の例とまったく変わりませんが、だいぶ賑やかになりました。ボールの絵柄だけでなく、大きさや質量、反発係数などもランダムに変化するようにしてみると、物理シミュレーションとして、さらに興味深いものになるでしょう。

次回の予定

 今回は、SpriteKitの重要な機能の1つ、physicsBodyを使った物理シミュレーションを試してみました。今回のプログラムでは、シミュレーションとして何かが足りないと感じる人もいるでしょう。それは、もしかしたら重力加速度が9.8に固定されていることから来るのかもしれません。次回はその重力加速度が縦横に変化するようにしてみましょう。ただし、ランダムに変化させるのではなく、このプログラムが動いているデバイス、つまりiPadの動きに応じて変化するようにします。そのためには、新たなフレームワークCoreMotionの助けを借りることになります。

■今回作ったプログラム

Swift_48-1
Swift_48-2
Swift_48-3
Swift_48-4

mobileASCII.jp TOPページへ

mobile ASCII

Access Rankingアクセスランキング