今回は複数の値をまとめて扱うことで、簡単なデータ分析をしたり、多くの物体をアニメーションさせたりする方法を学びましょう。
変数のように1つ1つのデータに名前を付けるのではなく、たくさんのデータをまとめて扱える方法として 配列 (array) という仕組みが用意されています。
配列は次のような文法で作成します。括弧は角括弧 [ ]
です。
[データ, データ, データ, ...]
配列にも名前がないと不便なので変数や定数に代入して使います。
let 配列名 = [データ, データ, データ, ...];
let 配列名 = []; // 空の配列を作ることもできる
配列内の個別データには何番目のデータかを表す番号 (index) が振られ、その番号を指定することで配列内の各データを扱うことができます。 また、末尾・先頭にデータを追加することができます。 作成した配列を扱う文法は次の通りです。indexは0から始まることに注意しましょう。
配列名[N] // N番目の値を参照する
配列名[N] = 値; // N番目に値を上書き代入する
配列名.push(値); // 末尾に値を追加する
配列名.pop() // 末尾の値を削除する
配列名.unshift(値); // 先頭に値を追加する(元々あったデータの番号が1ずつ後ろにずれる)
配列名.shift(); // 先頭の値を削除する
配列も本格的に使用する前にコンソールで触ってみましょう。
push と pop は「後に入れたデータを先に出す」という特徴を持った構造「スタック (Stack)」に対する操作の呼び方としてよく使われるものです。 データを押し込む・取り出すイメージです。
こういうのが Stack ですね。配列の末尾への追加・削除はまさしくこれらを抽象化したものというわけです。
ちなみに「先に入ったデータが先に出る」ような構造は「キュー (Queue)」と呼ばれます。先に並んだ人が先にお店に入れる行列のイメージです。
日常に例えると Stack はダメ人間で、 Queue はきちんとしているイメージが付きまといますが、情報科学的には Stack も大変活躍することがあるものです。 みなさんの部屋や脳内は Stack ですか? Queue ですか?
配列のすべての値について順番に繰り返しで処理するのは定番パターンのプログラムです。
次の例は、配列に含まれる値の合計値を求めるプログラムです。
配列名.length
でデータの個数がわかるのでそれを繰り返しの条件に使います。
let scores = [88, 80, 76];
let sum = 0;
for(let i = 0; i < scores.length; i++){
sum += scores[i];
}
console.log(sum);
値を1つずつ足し合わせていくことになりますので、途中結果を保持しておくために変数 n を使用しています。
続いて、配列を使ってたくさんのものを描画する例を見ていきますが、まずはその下準備としてたくさんの値が入った配列を用意します。
たくさんの値を手作業で用意するのは面倒なので、min 以上、max 未満の無作為な値を用意してくれる random(min, max)
を使いましょう。
繰り返しと組み合わせることで一気にたくさんの値を用意することができます。
let scores = [];
for(let i = 0; i < 10; i++){
scores.push(random(60, 100)); // 60以上100未満のランダムな数を追加
}
ランダムに決めた値を点数だと思ってそれを表す棒グラフを描いてみましょう。
function setup(){
createCanvas(400, 400);
// 点数を乱数で用意する
let scores = [];
for(let i = 0; i < 10; i++){
scores[i] = random(60, 100); // 60以上100未満のランダムな数を代入
}
// 棒を描く
for(let i = 0; i < scores.length; i++){
const dx = width / scores.length; // 棒の幅(固定)
const h = height * scores[i] / 100; // 棒の高さ(点数に比例)
fill(128);
rect(i * dx, height - h, dx, h);
}
}
次は、複数の物体をアニメーションさせるプログラムを作ります。
配列はたくさんの同じ種類のデータを扱うにはとても便利でした。 それ以外にも複数の異なる種類のデータをまとめて扱いたいときがあります。 たとえば、複数の物体がアニメーションするプログラムを作るときには、各物体の位置・速度・形状・色などをセットで扱うことができると便利です。 そのようなときに利用できるのが オブジェクト です。 オブジェクトではデータに番号ではなく キー と呼ばれる文字列を付けて区別します。
具体的な例を見てみましょう。位置・速度・大きさを持ったボールを二つ作成して配列に追加するプログラムです。
let balls = []; // 配列を用意して
let b1 = { x: 50, y: 50, vx: 3, vy: 0, size: 10 }; // ボール1のオブジェクトを作って
balls.push(b1); // 配列に追加
let b2 = { x: 40, y: 40, vx: 0, vy: 3, size: 20 }; // ボール2のオブジェクトを作って
balls.push(b2); // またもや配列に追加
ここまで実行するとデータは以下のような状態になります。ここでは x, y, vx, vy, size がキーです。
オブジェクト作成の文法は以下の通りです。キーと値をコロン :
でつないだものが一組で、それをカンマ ,
でつなぎます。
長くなる時はカンマで改行して整形すると読みやすいでしょう。
let オブジェクト名 = { キー1: 値1, キー2: 値2, ... };
// 長い場合は複数行にまたがって書くのもわかりやすい
let オブジェクト名 = {
キー1: 値1,
キー2: 値2,
...
};
オブジェクトの値を参照するときは配列と同じように [ ]
を使うか、ドット .
を使います。
オブジェクト名["キー"] // 値を参照する方法その1
オブジェクト名.キー // 値を参照する方法その2
オブジェクト名["キー"] = 新しい値; // 値を上書きする方法その1
オブジェクト名.キー = 新しい値; // 値を上書きする方法その2
それではお待ちかね、たくさんの物体のアニメーションです。 section4-2
let balls = [];
function setup(){
createCanvas(windowWidth, windowHeight);
}
function draw(){
background(160, 192, 255);
for(let i = 0; i < balls.length; i++){ // すべてのボールを描画し、速度の分だけ移動させる
let b = balls[i];
ellipse(b.x, b.y, b.size);
b.x += b.vx;
b.y += b.vy;
}
}
function mouseDragged(){ // ドラッグされたらボールを増やす
const dx = mouseX - pmouseX;
const dy = mouseY - pmouseY;
if(mag(dx, dy) > 5){ // mag(x,y) はベクトル(x,y)の長さ
const b = { x: mouseX, y: mouseY, size: 20, vx: dx, vy: dy };
balls.push(b);
}
}
マウスをドラッグすると、その動きの方向に飛んでいくボールが追加されるように作っています。
際限なく物体が増えていくのはよろしくないので画面外に出ていったら配列から削除するなどした方がよいです。