enchantMOONで障害物描いてボイドを飛ばしてみた

また面白い JavaScript+HTML5 のコードを発見!

ALとかAIっぽい話は好きなので、早速ボイドを enchantMOON でも飛ばしてみた。
と言っても、JavaScript そのまま動かすのでは特にすることもないので、今回はペンで障害物を描いて、更に軌跡を描くとそれに沿ってリーダーが飛んで群れが障害物を避けながら飛ぶという形にしてみた。

ま、障害物を避けるところは全然ダメダメなんだけど…。
その代わり、障害物に当たるとパーティクルが弾けるようにしてみたよ!

 ボイドを enchantMOON で動くようにする

正直、画面周りの HTML5(なの?)の div とか canvas とか良くわからないので、下記のページのコードを参考にして div と canvas を用意してみた。

// enchantMOON
var backing = MOON.getPaperJSON(MOON.getCurrentPage().backing),
	tracks = backing.strokes.pop(),
	div = window.document.createElement("div"),
	canvas = window.document.createElement("canvas");
canvas.width = backing.width;
canvas.height = backing.height; 
window.document.body.appendChild(div);
var ctx = canvas.getContext('2d');
div.appendChild(canvas);

とりあえず動いているけど、こういうことで良いのかな?

後はボイドのコードをまんま持ってくれば、まずはボイドの enchantMOON への移植(!)は完了。

 リーダーを追随するように変更する

オリジナルは群れの中心に向かって動くようになっているけど、これをリーダーに向かって動くようにするだけ。
ルール1をガッツリ変更。

/**
 * ルール1: ボイドは近くに存在する群れの中心に向かおうとする
 */
var rule1 = function(index) {
	boids[index].vx += (leader.x-boids[index].x) / 100;		// リーダーに向かう
	boids[index].vy += (leader.y-boids[index].y) / 100;		// リーダーに向かう
};

本当に単純にリーダーと自分の位置の差分を取って x,y それぞれの移動速度を決めています。

 リーダーがストロークをなぞるようにする

次にリーダーがストロークをなぞって移動するようにすれば、群れは自然とリーダーに追いかけてくれる。
リーダーは単純にストロークの x,y をそのまま使って移動するようにしています。

var moveLeader = function() {
	if (pos<data.length) {
		leader.x = data[pos++];
		leader.y = data[pos++];
		pos++;
		// リーダーのスピードが遅いので1つ飛ばしにする
		pos++;
		pos++;
		pos++;
	}
	else {
		pos = 0;
		leader.x = data[pos++];
		leader.y = data[pos++];
		pos++;
	}
}

pos はリーダーが何番目のストロークにいるのか、data は軌跡のストロークが入っている配列。
pos++ が余分に3つあるのは、単純にリーダーの移動速度が遅かったから。
ストロークを1つ飛ばしでリーダーは移動しています。

 障害物を配置する

障害物は、軌跡に使うストローク以外のストローク全部。
ストローク1つ1つが1つの障害物になります。
障害物は各ストロークを Rect にしているだけなので、丸とか三角とかにしても境界線は Rect です。

var makeObstacle = function(data) {
	var minX = SCREEN_WIDTH,
		minY = SCREEN_HEIGHT,
		maxX = 0,
		maxY = 0;

	for (var i=0;i<data.length;) {
		var x = data[i++];
		var y = data[i++];
		i++;

		if (x<minX) {
			minX = x;
		}
		if (x>maxX) {
			maxX = x;
		}
		if (y<minY) {
			minY = y;
		}
		if (y>maxY) {
			maxY = y;
		}
	}

	var obstacle = {
		x: minX,
		y: minY,
		width: maxX - minX,
		height: maxY - minY
	}
	return obstacle;
}

var getObstacles = function(strokes) {
	while (strokes.length>0) {
		var stroke = backing.strokes.pop();
		var data = stroke.data;
		obstacles.push(makeObstacle(data));
	}
}

だらだらと長いけど、ストロークの data から、x,yの最大最小値を取り出して rect を作って障害物にしています。

 障害物を避ける

正直、ちゃんと避けてそれっぽくリーダーに向かって行くようにはできていません。

var dodge = function(index, center) {
	var b = boids[index];
	if (crash(b)) {
		var life = Math.sqrt(b.vx*b.vx + b.vy*b.vy) * 2;
		particles.push(b.x,b.y,b.vx*-0.05,b.vy*-0.05,life);
	}
	while (crash(b)) {
		b.x -= b.vx;
		b.y -= b.vy;
		var dx = center.x - b.x;
		var dy = center.y - b.y;
		if (Math.abs(dx)>Math.abs(dy)) {
			b.y -= b.vy + Math.random()*b.vy;
		}
		else {
			b.x -= b.vx + Math.random()*b.vx;
		}
	}
}

x,y軸の、リーダーに近い軸だけ乱数で適当に動いているだけです。
進行方向の左右にずらして移動を試みるとか考えてみたのだけど、計算が…ということで乱数で適当にお茶を濁しました。
あ、後はぶつかった場合にはパーティクル出してます。

というわけで、以上の修正で enchantMOON で障害物を描いて、軌跡を描いてボイドを飛ばして遊べるようになりました。

 ダウンロードリンク
 使い方

画面に障害物を描いて、最後に軌跡を1本描いたら準備完了です。
シールをタップするとボイドが軌跡に沿って障害物を避けながら(ぶつかりながら)飛びます。
障害物は何個でも描くことができます。

 最後に

今回は前に「enchantMOONで簡易版ぷよぷよを遊べるようにしてみた」でお世話になったサイトのコードをまた使わせてもらいました。

このサイトのコード、JavaScript が良くわからない自分にもわかるように書かれているので、enchantMOON であれこれやって遊べるから非常に助かってます!

おかげでなんとなく JavaScript と HTML5 がわかってきたような気が…。

v.2.6.0 で enchantMOON がサクサク動くようになったら、もっと色々と遊んでみたいのだけど、さてどうだろう?

Leave a Reply

Your email address will not be published. Required fields are marked *

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)