こんにちは。アメーバ事業本部でピグの開発を担当している堀江優(@yu_horie)と申します。
この度、ピグに新たに誕生しました「昭和の町」エリア内で遊べる「めんこゲーム」の開発を担当させていただきましたので、その内部的な仕組を紹介させてください。





自作物理演算エンジン


ピグめんこゲームではリアルかつ直感的なインタラクションを実現するために、
めんこが叩かれてめんこがひっくり返るまでを計算する物理演算エンジンを自作しました。
実際の動きは上の動画を見ていただくとして、めんこが叩かれてからどのような計算をしているのかを簡単に解説します。
$1 pixel|サイバーエージェント公式クリエイターズブログ-めんこ解説図

ステージ上のめんこは(薄い立方体として)3D描画しています。
(ちなみにピグはFlashPlayer9向けにビルドされていますので、Matrix3D、Point3DやdrawTrianglesといったクラスやメソッドは今回FP9用に自作しています。)
各めんこにかかる力(回転量・移動量)を叩きつけられためんこの侵入スピード÷距離(E/d、図参照)というシンプルな方法で導き出し、さらにベクトルv(図参照)によって移動方向・X軸に回転する角度とY軸に回転する角度を導き出しています。
最終的に導きだした回転量を元にX軸・Y軸方向に回転する行列を作成、与えられた行列に応じて座標変換を行ってめんこの回転を描画しています。
流れを図式化すると下記のようになります。
$1 pixel|サイバーエージェント公式クリエイターズブログ-めんこ解説図2


計算式


自作した物理エンジンですが、サーバサイド(言語:java)に移植するために、
例えばglobalToLocalメソッド(グローバル座標からローカル座標に変換するメソッド)AS3独自のメソッドなどは使えませんでした。
使用した計算式を羅列していきます。主に座標計算系です。


2点間の距離を計る(ASでいうPoint.distanceメソッド)


$1 pixel|サイバーエージェント公式クリエイターズブログ-サンプル01
private function getDistance(x1:Number, y1:Number, x2:Number, y2:Number):Number
{
var d:Number = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
return d;
}


4点間の交点を計る


$1 pixel|サイバーエージェント公式クリエイターズブログ-サンプル02
private function getCrossPoint(p1:Point, p2:Point, p3:Point, p4:Point):Point
{
var s1:Number = ((p4.x-p2.x)*(p1.y-p2.y)-(p4.y-p2.y)*(p1.x-p2.x))*0.5;
var s2:Number = ((p4.x-p2.x)*(p2.y-p3.y)-(p4.y-p2.y)*(p2.x-p3.x))*0.5;

var cp1:Number = p1.x + (p3.x-p1.x)*(s1/(s1 + s2));
var cp2:Number = p1.y + (p3.y-p1.y)*(s1/(s1 + s2));

return new Point(cp1, cp2);
}


ローカル座標を計る(ASでいうglobalToLocalメソッド)


$1 pixel|サイバーエージェント公式クリエイターズブログ-サンプル03
var radian:Number;
if (localStage.rotation > 0) radian = Math.PI/180 * localStage.rotation;
else if (localStage.rotation < 0) radian = Math.PI/180 * (360 + localStage.rotation);
else if (localStage.rotation == 0) radian = 0;
//ローカルx
var localX:Number = Math.cos(radian)*(p.x-localStage.x)+Math.sin(radian)*(p.y-localStage.y);
//ローカルy
var localY:Number = -Math.sin(radian)*(p.x-localStage.x)+Math.cos(radian)*(p.y-localStage.y);


回転後の座標を計る


$1 pixel|サイバーエージェント公式クリエイターズブログ-サンプル04
private function getRotatePoint(xx:Number, yy:Number, cx:Number, cy:Number, rot:Number):Point
{
var cos:Number = Math.cos(rot*Math.PI/180);
var sin:Number = Math.sin(rot*Math.PI/180);
var _x:Number = cx+cos*(xx-cx)-sin*(yy-cy);
var _y:Number = cy+sin*(xx-cx)+cos*(yy-cy);

return new Point(_x, _y);
}

スマートフォンの普及も手伝ってゲーム開発のシーンは今後も増え続けていくと思います。
あらゆる数式を理解する必要はありませんが、このようにメモしておくと便利かもしれません。


気になった方はめんこゲームで遊んでみてください!
タイムトラベルエリア内の「昭和の町」エリアで遊べます!

アメーバピグはこちら
http://pigg.ameba.jp