p5.jsのベクトルで加速度や引力を扱ってみる


2018年04月15日
With
p5.jsのベクトルで加速度や引力を扱ってみる はコメントを受け付けていません

昨年の今ごろからp5.jsでのスケッチを始め、約1年の間にいろんなラクガキをしてきたのですが、これまでオブジェクトの「動き」を作る際には、単純な等速直線運動かnoise関数くらいしか使ってきませんでした。

そろそろ引き出しを増やしてみたいと思い、改めて基礎的な物理シミュレーションの初歩を身につけておこうと、最近、Shiffman先生のこちらの本をちまちま読み直しています。

■ Nature of Code – Processingではじめる自然現象のシミュレーション

こちらの第1章で扱われているのが「ベクトル」です。これまでも、2次元や3次元のスケッチで複数のインスタンスを同時に動かしたいときに、それぞれの座標を管理するクラスを独自に作ったりはしていたのですが、p5.js作り付けのベクトルクラスを使うと、座標に加えて、引力や重力による加速度、慣性などを加味した動きが、自分で一からやるよりだいぶラクに作れます。

p5.jsのベクトルクラス「p5.Vector」

「Nature of Code」に掲載されているサンプルコードは、副題にもあるとおり「Processing」向けのものになっています。Processingには「PVector」というベクトルクラスがありますが、p5.jsにも同様に使える「p5.Vector」というクラスが用意されています。

・p5.Vector - p5.js | reference

また、p5.jsで「p5.Vector」からインスタンスを作る際には「createVector()」という関数を使います。

・createVector() - p5.js | reference

p5.Vectorは、staticメソッドを持っており、例えば、vとuという2つのベクトルがある場合に「p5.Vector.add(v,u)」と書けば、戻り値としてベクトル加算の結果を得られます。

createVectorで作ったベクトルもp5.Vectorと同じメソッドを非staticで持っており、例えば「v.add(u)」と書くと、現在の「v」に「u」を足したものを、新たなvのベクトル値にできます。

とりあえずこのくらい把握しておけば、「PVector」を使ったProcessingのスケッチを、p5.jsに移植できそうです。

今回は「Nature of Code」第1章の最後のエクササイズとして出てくる「距離によって変化する加速度」を取り入れたこんなスケッチを作ってみました。

●マウスポインタに引き寄せられるツブツブ-600x600px版(クリックすると別タブが開きます)

●マウスポインタに引き寄せられるツブツブ-ブラウザサイズ対応版(クリックすると別タブが開きます。カンバスがブラウザ一杯のサイズに拡大されます)

スケッチを開いたら、とりあえずカンバス上のどこかにマウスポインタを置いて1回クリックしてください。画面上にバラまかれた水色のツブが、じんわりと軌跡を残しながらポインタに寄ってきます。

ツブにはベクトルによる加速度が設定されていて、ポインタの座標によって逐次加速度を変化させながら動きます。ポインタと各ツブの間に引力が働いているような感じで、距離が近いほど、強く引っ張られるようになっています。速度には上限があり、引っ張られる力と、その時点での速度が釣り合うと、ポインタのまわりを円がクルクルと回る衛星のような動きになるのが面白いですね。

コードは以下のとおりです。

var movers = new Array(1000);

function setup() {
	createCanvas(600, 600);
	background(0);
	for (var i = 0; i < movers.length; i++) {
		movers[i] = new Mover();
	}
}

function draw() {
	fill(0, 40);
	rect(0, 0, width, height);
	for (var i = 0; i < movers.length; i++) {
		movers[i].update();
		movers[i].checkEdges();
		movers[i].display();
	}
}

class Mover {
	constructor() {
		this.accelaration;
		this.location = createVector(random(width), random(height));
		this.velocity = createVector(0, 0);
		this.topspeed = 4.6;
	}

	update() {
		var mouse = createVector(mouseX, mouseY);
		var dir = p5.Vector.sub(mouse, this.location);
		var magn = p5.Vector.mag(dir);
		dir.normalize();
		dir.mult(13 / magn);
		this.accelaration = dir;
		this.velocity.add(this.accelaration);
		this.velocity.limit(this.topspeed);
		this.location.add(this.velocity);
	}

	display() {
		noStroke();
		fill(191, 207, 255);
		ellipse(this.location.x, this.location.y, 8, 8);
	}

	checkEdges() {
		if (this.location.x > width) {
			this.location.x = 0;
		} else if (this.location.x < 0) {
			this.location.x = width;
		}
		if (this.location.y > height) {
			this.location.y = 0;
		} else if (this.location.y < 0) {
			this.location.y = height;
		}
	}
}

このスケッチでは一気に1000個のツブツブを描いているのですが、ポインタを動かしながら動きを見ていると、なんか弾幕シューティングの敵弾による花火大会のようにも見えてきます。

「…てことは、これゲームっぽくできるんじゃね?」

というわけで、実際に、今回のスケッチをミニゲーム風にアレンジしたものも作ってみました。結構楽しく仕上がりましたので、よろしければ次のエントリから遊んでみてください