Help us understand the problem. What is going on with this article?

メディアアート系で重要な数式・概念 & 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

hp0me
スマートフォンゲームがメインですが、最近は雑食になってきました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした