まずはシンプルな練習問題を通して、可変長配列の復習をしておきましょう。
キャンバスの中央から、ランダムウォークする粒が湧き出るスケッチを作ってみましょう。
このスケッチに手を加えて、粒のランダムウォークを一旦やめ、重力を加えてみましょう。重力について忘れてしまったら、「落ちる・加速する」を参照してください。
これまでの配列の使いかたをおさらいしておくと、粒のもつデータ(X・Y位置、X・Y速度)に対してひとつずつ、計4つの配列を割り当てていました。
粒が持つ情報が増えていくほど、配列の数も増えていくのがわかるでしょうか。例えば、ここからさらに色情報を持たせようとなると…さらに増えるわけです。
より複雑なスケッチに進化させていく過程で配列だらけになってしまい、スケッチの見通しがどんどん悪くなっていきます。
ここで「クラス」という概念を導入して、データ構造をすっきりと整理してみましょう。
クラスとは、複数のデータをひとかたまりにできる便利な機能です。…と言っても何のことやらなので、パーティクルを例に考えていきましょう。
まずは1粒に着目して、粒のもつ情報について考えていきます。
Processingの世界では、クラスはこのように書きます。
class クラス名 {
}
クラス名は変数と同様に、ほぼ自由に決められます。今回の例ではパーティクルシステムの「粒」を表現するので Particle
としましょうか。クラス名は大文字で始めるのが慣習です。
// 粒を表現する Particle クラス
class Particle {
}
クラスの波括弧の中に、そのクラスに属するデータが入ります。さて、粒がもつデータとは何でしょうか?
…ですね。これをクラスで表現するとこんな感じ。
// 粒を表現する Particle クラス
class Particle {
// 粒のもつデータ
// 粒は x, y, vx, vy をもつ
float x;
float y;
float vx;
float vy;
}
これで、「粒子 Particle
というクラスは、 x
, y
, vx
, vy
をもつデータ構造である」、と定められます。
身近な例で考えると…これはたいやきの型を作るのに似ています。「こういう形のたいやきを作る」というルールを定めるのが型、つまりクラスなのですね。
さて、たいやきの型を作ったところで安心するのはまだ早いです。せっかく用意した型を使って、肝心の中身・たいやきを作っていきます。ちなみに、型で作る実体をプログラミングの世界では「インスタンス」と呼びます。
使いかたは変数と同じ。変数の型を書いていた場所にクラス名が入り、変数名はこれまでどおり自由です。
// Particle という型で p を宣言(まだ空っぽ)
Particle p;
配列などと同じように、クラスのインスタンス(実体)も宣言したままではまだ空っぽです。 new
を使って実体を生成することで、はじめて使えるようになります。
void setup() {
// インスタンス(実体)を生成
p = new Particle();
}
いちどクラス(たいやきの型)を作ってしまえば、インスタンス・実体(たいやき)は作り放題です。
// こんなこともできる(今回はしないけど)
Particle p1 = new Particle();
Particle p2 = new Particle();
Particle p3 = new Particle();
Particle p4 = new Particle();
Particle p5 = new Particle();
...
さて、インスタンスのもつデータ(ここでは x
, y
, vx
, vy
)には、 インスタンス名 . データの名前
とドットでつなぐことでアクセスできます。基本的な使いかたは変数と同じです。
void setup() {
size(600, 600);
fill(0);
// インスタンス(実体)を生成
p = new Particle();
// インスタンスのデータに代入(変数と同じように利用できる)
p.x = 300;
p.y = 300;
p.vx = 0;
p.vy = 0;
}
試しに、このまま draw()
ブロックまで書いて、1粒動かして描画するところまで仕上げてみましょう。
// 粒を表現するクラス Particle を定義
class Particle {
// 粒のもつデータ
// 粒は x, y, vx, vy をもつ
float x;
float y;
float vx;
float vy;
}
// Particle という型で p を宣言
Particle p;
void setup() {
size(600, 600);
fill(0);
// インスタンス(実体)を生成
p = new Particle();
// インスタンスのデータに代入(変数と同じように利用できる)
p.x = 300;
p.y = 300;
p.vx = 0;
p.vy = 0;
}
void draw() {
background(255);
// インスタンスのデータに代入(変数と同じように利用できる)
p.x += p.vx;
p.y += p.vy;
p.vx += random(-0.1, +0.1);
p.vy += random(-0.1, +0.1);
// インスタンスのデータを参照(変数と同じように利用できる)
ellipse(p.x, p.y, 10, 10);
}
さて、クラスを使って1粒動かすところまでできるようになりました。今度は100粒同時に動かせるように拡張してみましょう。
数があらかじめ決まっている場合は、固定長の配列が使えます。書きかたは float
など基本的な型の配列と同様です。
// Particle の配列
Particle[] ps;
void setup() {
size(600, 600);
fill(0);
// 配列を初期化
ps = new Particle[100];
for (int i = 0; i < 100; i++) {
// i 番目の粒の実体を生成
ps[i] = new Particle();
// i 番目の粒のデータを初期化
ps[i].x = 300;
ps[i].y = 300;
ps[i].vx = 0;
ps[i].vy = 0;
}
}
// ...
ひとつだけ、配列の初期化が終わったあとは、1粒ずつ実体を生成する必要がある点に気をつけてください。
このまま draw()
の中身も書き換えて、スケッチが動作するように仕上げてみましょう。
float
の可変長配列が FloatList
だったように、独自のクラスも可変長配列にできます。これを実現してくれるのが ArrayList
です。書きかたがやや複雑になりますが、我慢…!
ArrayList< 配列にしたいクラス名 > 変数名 ;
先の Particle
を例にするとこんな感じ。
// Particle クラスの可変長配列
ArrayList<Particle> ps;
操作はほぼ、これままで触れてきた可変長配列 FloatList
と同じですが、要素の追加 .append()
が .add()
になっている点だけ気をつけましょう。
先の100粒のスケッチについて、配列を可変長に書き換えて、粒が湧き出すようにしてみましょう。
// Particle の可変長配列
ArrayList<Particle> ps;
void setup() {
size(600, 600);
fill(0);
// 配列を初期化(可変長なので配列の個数は指定しなくてよい)
ps = new ArrayList<Particle>();
for (int i = 0; i < 100; i++) {
// 粒の実体を生成
Particle p = new Particle();
// 粒のデータを初期化
p.x = 300;
p.y = 300;
p.vx = 0;
p.vy = 0;
// 生成・初期化した粒を可変長配列に追加
ps.add(p);
}
}
// ...
完成したら寿命を加えたり、粒のサイズを加えたりしてみてください。
粒の機能拡張が簡単・簡潔でわかりやすいと思いませんか?これがクラスのもつ一側面、データ集約の威力です。
次回はクラスが持つ別の側面、「機能の集約」について見ていきましょう。
知らないとプログラムが書けないようなものではありませんが、複雑なプログラムを作ったり、他のエンジニア・デザイナーと協業したりする際には欠かせない概念です。