Box2DでActionScript物理プログラミング

第7回 円を落として星を飛び散らせる

この記事を読むのに必要な時間:およそ 3.5 分

ContactListenerクラスでの処理

クラスの全体像

では実際にサンプルプログラムがどのように書かれているかを説明します。まずはb2ContactListenerクラスを継承したContactListenerクラスからです。クラスの大まかな形は以下のようになっています。

public class ContactListener extends b2ContactListener {
  public var parent:KiraKira;
  
  public function StarContactListener() {
  }
  
  // 新しく衝突が発生したときに呼び出されるメソッド
  public override function Add(point:b2ContactPoint):void { ... }
}

メインとなるKiraKiraクラスを参照するために,parentというプロパティが定義されています。メソッドについては,コンストラクタでは何もせず,Addメソッドでは星を発生させる処理をしています。

物体が衝突した時の処理

物体が衝突したときの処理を書くAddメソッドは,以下のようになっています。

public override function Add(point:b2ContactPoint):void {
  // 衝突時の力の大きさをもとに、エフェクトを発生させる数を計算する
  var num:int = point.normalForce / 10 + 1;
  // 計算ミスで大きな力がかかることがあるので、値を10までに制限する
  if (num > 10) {
    num = 10;
  }
  for (var i:int = 0; i < num; ++i) {
    parent.addContactEffect(point);
  }
}

衝突時の力をもとに,エフェクトを発生させる数numを計算しています。実際にtraceしてみると分かりますが,normalForceの値は数十程度になることが多いので,10で割っています。ちなみにnormalForceの単位はニュートンです。

割り算の結果に1を加えているのは,最低でも1つ星を発生させるためです。また,まれにnormalForceが非常に大きくなるので,上限を10としています。

numが計算できたら,その回数だけ星を飛ばす処理を呼び出します。この処理は,KiraKiraクラスのaddContactEffectメソッドに書いてあります。

星を飛ばすには

説明の舞台をContactListener.asからKiraKira.asに移動します。この中に書かれているaddContactEffectメソッドは,星を飛ばすためのメソッドです。引数に渡されたb2ContactPointの情報から星を飛ばす位置を計算し,tweenerを使って星を飛ばします。

// 物と物が接触したときのエフェクトを発生させる
public function addContactEffect(point:b2ContactPoint):void { ... }

星の画像を読み込む

ではメソッドの中身について説明します。まずは星の画像からSpriteを作ります。ここは前回DebugDrawを使わずに物体を描画したときのパターンと似ているので,詳しい説明は省きます。

// 星の画像を読み込んで、サイズと位置を調整する
var starImage:Bitmap = new StarImage();
starImage.width = 50;
starImage.height = 50;
starImage.x = -starImage.width / 2;
starImage.y = -starImage.height / 2;

// 星の画像を表示するためのSpriteを作る
// 座標はコンタクトが発生した場所とする
var sprite:Sprite = new Sprite();
sprite.x = point.position.x * DRAW_SCALE;
sprite.y = point.position.y * DRAW_SCALE;
sprite.addChild(starImage);
addChild(sprite);

この処理の結果,sprite変数に星の画像が設定されます。場所についてはpoint.positionを参照し,物体が衝突した位置に設定します。

星を飛ばす先の座標を計算する

星を飛ばすときの原点はpoint.positionから求めることができたので,次はどこに飛ばすかを計算します。まずは飛ばす方向と距離を決めます。この値はMathクラスのrandomメソッドを使って適当な値にします。

// 星が飛んでいく方向と距離をランダムに決める
// 角度は0~360度、距離は50~150
var angle:Number = Math.random() * 360;
var length:Number = Math.random() * 100 + 50;

方向と距離が決まったら,それを使って飛んでいく先の座標を計算します。三角関数を使い,星が飛び始める点からangle度の方向にlengthだけ進んだ座標を計算しています。

// 星が飛んでいく先の座標を計算する
var dx:Number = sprite.x + length * Math.cos(angle);
var dy:Number = sprite.y + length * Math.sin(angle);

tweenerを使って星を飛ばす

座標が求まったのでいよいよ星を飛ばします。setTimeoutを使うなどして星を飛ばすプログラムを自前で書いていくと長くなってしまい,それだけでも説明を要するので,tweenerの力を借ります。星が飛んでいく先の座標dxとdyをtweenerに与えれば,そこまで移動する処理を行ってくれます。

// Tweenerで星を飛ばす
Tweener.addTween(sprite, {
  time: 3, // 3秒間
  scaleX: 0, // 消えるまで縮小する
  scaleY: 0,
  alpha: 0, // 完全に透明にする
  rotation: 200, // 少し回転させる
  x: dx,
  y: dy,
  onComplete: function():void {
    removeChild(sprite);
  }
});

単純に星を飛ばす(移動させる)だけでもいいのですが,せっかくtweenerを使うので色々な処理をしています。3秒かけて,回転しながら,どんどん小さく透明になっていくようにします。

3秒経過してアニメーションが終わるとonCompleteが呼び出され,ステージ上からspriteが取り除かれます。

KiraKiraクラスでは,この他にもクリックで円を作ったりする処理が含まれていますが,これらについては以前に触れたことなので,説明は省略します。

まとめ

コンタクトリスナを使い,物体同士が衝突したときに星が飛び散るFlashを作りました。ActionScriptの一般的なイベント処理とは異なる書き方をするので,そこだけは慣れが必要です。逆に,Javaなどの言語のイベントリスナを知っている方であれば,この方法を自然に感じるかもしれません。

コンタクトリスナの挙動が若干おかしい場合もありますが,これはBox2Dのバージョンアップで改善されていくものと思われます。また,いつかは衝突検出がイベントによって通知されるようになるかもしれません。

7回の連載を通じてBox2Dの解説をしてきましたが,いかがだったでしょうか。今回のコンタクトリスナをはじめ,少し癖のある書き方を強いられることがありますが,それだけの魅力があるライブラリだと思います。

ライブラリの全ての部分を説明することはできませんでしたが,説明できた範囲だけでも,工夫次第で面白いコンテンツが作れるのではないかと思います。そういったコンテンツが続々と出てくることを期待して,連載を締めくくらせていただきます。

著者プロフィール

木村秀敬(きむらひでたか)

茨城高専,北陸先端大を卒業後,独立系ベンチャーにあこがれてjig.jpに就職。 ActionScript好きですが,根はコテコテのC/C++プログラマです。Flash/ActionScriptに興味のある方は是非Spark Projectへ。