Edited at

メディアアート系で重要な数式・概念 & p5.jsで遊ぶ

More than 3 years have passed since last update.

高校数学がボロボロでも大丈夫です。(

もともと個人的にインプットし貯めていたものですが、書きなおして公開します。

地味ですが、派手な動きはこの地味な数式・概念がベースになってきます。

また、メディアアートとは言ったものの、ゲーム制作などにも役立つでしょう。

「ラジアンとは?」「サイン波を描く」「円軌道を描く」「弾幕(2点間の距離系&角度系)」「多角形を描く」「フラクタル」といったテーマです。

また、そのプレイグラウンドとしてp5.js(Processingのjs版)をご紹介します。2014年にリリースされたものでまだマイナーですがCodePenのような海外サイトでは人気が出つつあります。またProcessing公式プロジェクトなので安心感もありますね。ちなみに、Processing.jsとは別プロジェクトです。


ラジアンとは?

・ラジアンは単位

角度についてのもう一つの単位です。

角度とラジアンは、ドルと円みたいな違いと思っています。

・1ラジアンの定義

トリビアですが、ラジアンには弧をもとにした基準があって、

「弧の長さ = その円の半径」の時「1になる単位」だそうです。

まぁ、これ知ってもあんまり変わりません。(←)

・ラジアンの出し方

ラジアン = 角度 * π ÷ 180 です。

つまり角度ありきで出すことがほとんどでしょう。

JavaScriptだと

ラジアン = 角度 * Math.PI / 180;

var rad = 0;

var degree = 50;

rad = degree * Math.PI / 180;
console.log(rad); //0.8726646259971648


まずは、サイン波を描く

サイン波は最もプリミティブなものなので、ここでサイン波の描き方を覚えておきましょう。

深く原理は分からずとも使えるようになれば大丈夫です。

ちなみに、Sineを良く使う場面

「定期的に、ある値をふわふわ上下させたい時」
に良く使います。

ここらへんは言わずもがなかもしれません。

ここでは、うねる直線、つまりサイン波を作ります。

circlemoving3.gif

以下の動きを毎フレームするイメージです。

1 . まず、xをいくらか+します。いくらでもOK。

2 . 角度をいくらか+します。いくらでもOK。

3 . 角度をラジアンに変換します。

4 . yの座標をSin(ラジアン)で決定します。

5 . xとyを元にオブジェクトの座標をセットします。

・・・よく分かりませんよね?w

これだけでイメージすることは不可能かと思います。コードを書いて数値をいじってイメージを掴んでいきます。

コードにすると、

//xをいくらか+する

x += 1;

//角度をいくらか+する
degree += 10;

//角度をラジアンに変換
rad = degree * Math.PI / 180;

//y座標をsin(ラジアン)で決定。最後に掛ける値で振れ幅を調整
y = Math.sin(rad) * 30;

//ポジションをセット
setPosition(x,y);

+する値によってスピードや振れ幅を変えられます。

xに+する値を増やすとx方向のスピードが速くなり、

//x方向に速くする

x += 15;

circlemoving4.gif


角度に+する値を増やすとyの振れるスピードが速くなります。

//yの振れるスピードを速くする

degree += 30;

circlemoving5.gif


また、yに最後にかける値を大きくすると振れ幅が大きくなります。

//yの振れ幅を大きくする

y = Math.sin(rad) * 100;

circlemoving6.gif


円軌道を描く方法

プログラミングでどうやって円を描けばいいんだ?愕然としますよね、分かります。

そんな時に覚えておくとすぐに使える数式があります。しかも汎用性がとっても高いです。


毎フレーム、角度を+する

毎フレーム、角度をラジアンに変換する

X座標 = 円の中心のX座標 + 半径 × Cos(ラジアン)

Y座標 = 円の中心の中心Y座標 + 半径 × Sin(ラジアン)

オブジェクトを(X,Y)に移動させる


これは鵜呑みして損はありません。

JavaScriptだと、

    x = centerX + radius * Math.cos(rad);

y = centerY + radius * Math.sin(rad);

で、これをどう使うか?

毎フレームこんな処理をします。

・角度を増やします

・角度をラジアンに変換します

・X座標 = 円の中心のX座標 + 半径 × Cos(ラジアン)を出す

・Y座標 = 円の中心の中心Y座標 + 半径 × Sin(ラジアン)を出す

・X,Yをもとに座標をセット

JavaScriptだと、

//角度を増やします

degree += 3;

//角度をラジアンに変換します
rad = degree * Math.PI / 180;

//X座標 = 円の中心のX座標 + 半径 × Cos(ラジアン)を出す
x = centerX + radius * Math.cos(rad);

//Y座標 = 円の中心の中心Y座標 + 半径 × Sin(ラジアン)を出す
y = centerY + radius * Math.sin(rad);

//X,Yをもとに座標をセット
setPosition(x,y);

そうすると・・・

circlemoving10.gif

使い方としては、毎フレーム、角度が増えるほど円における位置が進んでいくことをイメージしておくと良いと思います。

また、半径を毎フレーム長くすると螺旋を描いたり、

//半径を毎フレーム長くする

radius += 1;

circlemoving16.gif


さらに・・・さっきのサイン波が活躍しだします。今後ずっと活躍します。

半径をサイン波で上下させるとウネウネします。

//半径をサイン波で上下させる

sinFactor += 0.1;
radius += Math.sin(sinFactor) * 10;

circlemoving19.gif


あとは角度をサイン波で上下させるとこんな風になったり・・・

//角度をサイン波で上下させる

sinFactor += 0.04;
rad += Math.sin(sinFactor) * 3;

circlemoving20.gif

値を色々変えてイメージを掴んでいきましょう。

足す角度を増やすと速くなる、半径を足すと螺旋になる、ここらへんは分かりやすいのでやってみましょう。

イメージが掴めたら、テキトーに値をいじってみましょう。

関係なさそうな値もいじってみましょう。

たとえば中心の座標を毎フレームずらしてみるとかですね。


そして弾幕へ・・・

サイン波、円の描き方、弾幕へ行きましょう。

circlemoving24.gif

弾幕の仕組みは↓に書いたので割愛します。

こっちでは2点間の距離の公式とかも出てきます。

弾幕の初歩(距離系と角度系)

http://qiita.com/hp0me/items/1164bf9669a825d76ffa


多角形をどうやって描くか?

先ほどの円の公式を活用します。ここでは6角形を描くとします。

まず、円の1周すなわち360度を6角形の6で割ります。 = 60度

そして、先ほどの円の周り方で60度ごとにプロットを(座標で)打っていきます。

ここでプロットを配列にpushしていきましょう。

配列にはこんな感じで値が入るはずです。

配列 = [0度の座標,60度の座標,120度の座標,180度の座標,240度の座標,300度の座標]

これを結びます。

まずは、0度の座標と60度の座標を線で結びます。

その後は60度と120度の座標を結んで・・・

どうでしょうか?

今回、長くなってしまったのでコードは割愛します。

時間が出来たら加筆しますね・・・


そしてフラクタルへ・・・

先ほどの多角形の書き方(プロットを保存して、線でむすぶ)を応用してフラクタルを書きます。

ここでは三角形の中に三角形ができるフラクタルを作ってみます。

ここからはちょっとややこしいかもしれませんが

順番はこんな感じ

・三角形を描く (できたプロットはA,B,Cとする)

・A , AとBの中点 , AとCの中点の3点を結んで三角形を作る

ひたすらこれを入れ子(ネスト)で繰り返します。

これができていればどんなコードでもOKです。

これも需要があればコードを加筆する予定・・・

応用すればアニメーションもできるでしょう。

ちなみに「動くフラクタル 8角形」っていうのを作ってみました。

http://jsdo.it/hp0me_/ycrm

circlemoving9.gif


最新のプレイグラウンド = p5.jsで遊ぶ

Processingはなんか今っぽくない、かといってCanvasはなんかちょっと足りない・めんどい、Three.jsはちょっとめんどい、といったニーズを解決するのがProcessingから派生したp5.js(Processing公式プロジェクト)です。

ここで紹介したような数式・概念を試すにはぴったりです。

ちなみにp5.jsはProcessing.jsとは別プロジェクトです。所感としてProcessing.jsはなんかイケてないなと思っていたのですが、p5.jsはイケてます。

また、マイクのインプットなど、ちょっとしたAPIも多数あります。

公式サイトのGetStartedを参考にはじめます。

http://p5js.org/get-started/

p5.jsの基本はsetup()とdraw()です。

setup()は最初に呼ばれる関数です。

よくあるinit、またはUnityのStart()にも似ています。

draw()は毎フレーム呼ばれる関数です。何かを動かしたいときはここで操作します。

JSのSetTimeOut()、UnityのUpdateに似ています。

また、height(キャンバスの高さ)やwidth(キャンバスの幅)など便利な変数がグローバルで最初からセットされています。これは何気に便利。くわしくはリファレンス(http://p5js.org/reference/)にて。

var n = 0;

function setup() {
//最初に呼ばれます
console.log("スタート");

//Canvasを作る
createCanvas(500, 500);
}

function draw() {
//毎フレーム呼ばれます。
n++;
console.log(n);
}

また、基本関数として、図形を描くために

ellipse(x,y,縦の大きさ,横の大きさ);

これで円を描けることはまず覚えておきます。

p5.jsで円軌道を描いてみましょう。先ほどの、


毎フレーム、角度を+する

毎フレーム、角度をラジアンに変換する

X座標 = 円の中心のX座標 + 半径 × Cos(ラジアン)

Y座標 = 円の中心の中心Y座標 + 半径 × Sin(ラジアン)

オブジェクトを(X,Y)に移動させる


を使います。

var circle;

var speed = 30;
var radius = 100;

//最初に呼ばれる
function setup() {
createCanvas(500, 500);
circle = new MyCircle();
}

//毎フレーム呼ばれる
function draw() {
background(255);//再描画
circle.move();
}

//円のクラス
function MyCircle(){

this.x = width/2;
this.y = height/2;
this.degree = 0;
this.rad = 0;
this.radius = radius;

this.move = function(){
//角度を増やします
this.degree += 3;

//角度をラジアンに変換します
this.rad = this.degree * Math.PI / 180;

//X座標 = 円の中心のX座標 + 半径 × Cos(ラジアン)を出す
this.x = width/2 + this.radius * Math.cos(this.rad);

//Y座標 = 円の中心の中心Y座標 + 半径 × Sin(ラジアン)を出す
this.y = height/2 + this.radius * Math.sin(this.rad);

//X,Yをもとに座標をセット
ellipse(this.x,this.y,30,30);
};
}


さらに、p5.jsで弾幕を書いてみました。クリックで打ちます。

http://jsdo.it/hp0me_/zlLi


//弾が格納される配列
var circles = [];

//弾
var cir;

//初期座標(bulletX,bulletY)からangleの角度へ移動
var bulletX = 0;
var bulletY = 0;

//弾速
var speed = 3;

function setup() {
createCanvas(500, 500);
}

function draw() {
background(255);

//弾の分だけループ
for(var i = 0;i<circles.length;i++){
circles[i].move();
}

}

function mouseClicked() {
//弾の分だけループ
for(var i = 0;i<20;i++){
cir = new MyCircle(i * 5 +45);
circles.push(cir);
}
return false;
}

//弾幕クラス
function MyCircle(anglee){

this.x = width/2;
this.y = 10;
this.angle = anglee;
this.vx = Math.cos(Math.PI / 180 * this.angle) * speed;
this.vy = Math.sin(Math.PI / 180 * this.angle) * speed;

this.move = function(){
this.x += this.vx;
this.y += this.vy;
fill(0);
ellipse(this.x,this.y,10,10);
};
}

p5.jsの最大のメリットは、面倒なCanvasの前準備をせずにいきなりCanvasのアルゴリズムに集中できるところです。余談ですが、意外にも(?)これに近いのはCocos2d-jsやenchant.jsといったゲーム系エンジンじゃないでしょうか?

そしてp5.jsで作ったコードはjsdo.itに投稿すれば面白いですね。

実際に投稿してみました。

http://jsdo.it/hp0me_


後記

これも重要かな、これは間違ってるよ、などあれば教えてください

こちらにコメント or mail : hp0isme@gmail.com


2015.11.27追記

p5.jsが3Dに対応したようです(10月)。軽くいじってまとめました。

「p5.jsがWebGL&3D対応。軽くいじってみます」

http://qiita.com/hp0me/items/1570694d9f61368f3ae6


p5.jsでグローバル汚染を回避するには(2015.12.22追記)

Processingスタイルのグローバル汚染が気になる場合、インスタンスモードで回避できるようです。

「p5.js を instance mode で使う」

http://qiita.com/turusuke/items/c92a13602523d6378cc0