北海道を落とすとどう跳ねるのか?の裏側

おかげさまで大好評の 北海道を落とすとどう跳ねるのか? ですが、どのように作ったか、製作過程を紹介することにします。

1. 地図の素材を取ってくる

まずは地図の素材が必要です。以下のサイトから拝借しました。

pdf や eps 形式の地図データを無料で配布してくれているありがたいサイトです。

2. 都道府県ごとに分割する

上記の素材は県境もベクター形式で提供されていて大変ありがたかったのですが、島がどの都道府県に属しているかの情報がありませんでした。

そこで、Google Maps と見比べながら、島を都道府県ごとに分類していきました。無事、全ての島を分類し終わって、こんな感じになりました。

とても地味な作業でした…。

3. 都道府県ごとに SVG で出力する

次に、Illustrator 内で分類したデータをプログラムで扱える形式にしなければなりません。ここでは XML 形式でプログラムからも扱いやすい SVG で出力することにしました。

このように、都道府県ごとに SVG で出力していきます。

4. SVG を座標データに変換する

ActionScript で座標を扱いやすくするために、SVG を座標データに変換しました。

例えば、北海道の座標では、SVG には次のようなデータが含まれています。

<path i:knockout="Off" fill="#FFFFFF" d="
  M122.377, 57.787
  c     0,      0, -5.191, -2.143, -8.04,  -3.73
  c-2.848, -1.588, -3.957, -2.445, -4.058, -2.672
  c-0.102, -0.227,  0.024, -1.008, -0.353, -1.234
  ... 以下略

この記号の羅列が座標を知る鍵です。最初の、M122.377, 57.787 は (122.377, 57.787) まで移動しろということで、次の c が3次ベジェ曲線を(-8.04, -3.73) まで描け、ということです。

こんな感じで、SVG の中身をパースして、図形を描画するところまでできました。ベジェ曲線は途中のカーブは無視して始点と終点だけを結んで描画してます。それなりに再現できてたので曲線の描画は省略しました。

ところで、物理エンジン(Box2d)では図形は出っ張ってないといけません。直線を結んだだけでは凹んだ箇所が出てくるのでシミュレーションできません。

そこで、このプログラムでは図形の凸包を求めています。上の図では水色の線が凸包です。凸包は図形の外側を布で覆ったところをイメージすると分かりやすいでしょう。

凸包を求めるアルゴリズムは 凸包の計算 (PDF) が分かりやすかったです。今回は計算時間は重要ではなかったので、一番シンプルで泥臭い方法で求めています。

そんなこんなで SVG を座標の集合に変換することができました。変換をしているのが EasySvgConvert.as で、変換結果が Prefs.as です。

5. Box2dFlashAS3 で落とす

先ほど作った凸包の形で物体を作成して、物理シミュレーションを開始します。シミュレーション結果の位置に応じて、地形を移動回転させています。

Box2dFlashAS3 の DebugDraw 機能を ON にしてみるとこのような見た目になります。

物理エンジンは複雑な地形は考えていなくて、凸包だけを認識しているわけです。

ちなみに、北海道は本島のみを落としていて、そのほかの島々は登場しません。それには理由があって、北海道がこのようないびつな形をしているからです。もし、小さな島も一緒に落としてしまうと、島との当たり判定が変に見えてしまったことでしょう。

6. 頂点の数を減らす

SVG からそのまま凸包を求めてシミュレーションすると、どうしても丸みを帯びた角が地面に接触するときに計算数が増えてしまい、コマ落ちしてしまいました。また、素の状態だと頂点のデータだけで 150KB 程度の容量を食っていたので、データのダイエットが必須でした。

そこで頂点数を適度に間引くためのツールを Flex で作りました。

真ん中がオリジナル、右が間引いたあとです。この図は間引きすぎですが、違和感がない程度まで間引くよう調整しました。シリアライズして GZIP 圧縮した結果、150KB あったデータを 60KB まで削減でき、描画速度も向上しました。

ソースコードは LightenPrefs.mxmlLightenPrefsImpl.as あたりです。

7. 反発係数や摩擦を調整する

ただ落とすだけでも十分面白かったのですが、都道府県ごとに個性を持たせるようにしました。例えば、「岩手県は名前も堅そうだし形もごつごつしているのでほとんど跳ねないようにしよう」とか「栃木県は丸いからスーパーボールみたいに弾みそうだよね」とか「石川県が立ったら面白いに違いない」とかそんな感じです。

完成したパラメータは こんな感じ になりました。

package{
public var PrefBox2d:Array = [
  {name: '北海道',   zoom: 1.0, r: 0.71, f: .12},
  {name: '青森県',   zoom: 2.5},
  {name: '岩手県',   zoom: 2.0, r: 0.25, f: .4},
  {name: '秋田県',   zoom: 1.6, r: 0.55, f: .1},
  {name: '宮城県',   zoom: 2.0, r: 0.66, f: .6},
  {name: '山形県',   zoom: 2.2, r: 0.88, f: .2},
// ...以下略

r が反発係数、f が摩擦係数です。全ての都道府県について何か見所があるようにパラメータを調整したつもりです。

8. 細かな調整

完成度を上げるところが一番地味ですが重要なところです。表示のタイミングを調整したり、都道府県を選ぶ UI を作ったり、選んだあとはドラッグできるようにしたり…。状態遷移が複雑になってきたので、State パターンみたいな感じで状態を定義しました。

まとめ

駆け足でみていきましたが、完成までの流れがだいたい分かっていただけたと思います。実際にはいろいろと試行錯誤を繰り返しながら、時にはつまずいて諦めかけながら完成に持っていきました。

興味ある人は ソースコード をじっくり読んで研究してみてください。