「アニメーション」の回では、 draw()
ブロックの中で消しては描いてを繰り返すことで、ものの動きが表現できることを学びました。
Processingの世界では、 background()
で背景を消去するとまっさらになってしまいます。
このような世界において、例えば「だんだん膨らんでいく風船」や「自由に動き回るボール」のようなものはどのように表現すればよいのでしょうか?
アニメーションとは少しずつ変化していくもの。大きさが変わるものであれば「先ほどまでどれくらいの大きさだったか」を、移動するものであれば「先ほどまでどの場所にいたか」を覚えておいて、少しずつ変えていく必要があるのです。
つまり、ものを存在させて動かすためには、大きさや位置など、そのものに関する情報を覚えておく仕組みが必要なのです。
今回は、プログラミングの世界で覚えておくための仕組みである「変数」を学んでいきます。
シンプルな例として、クリックで膨らむ風船を作ってみましょう。
まずは draw()
ブロック内で ellipse()
を実行し、風船を画面中央に表示するスケッチを作成してください。
void setup() {
size(600, 600);
strokeWeight(2);
fill(255, 200, 100);
}
void draw() {
background(255, 255, 255);
ellipse(300, 300, 200, 200);
}
マウスボタンを押したときのブロックは mousePressed()
でしたね。
// マウスボタンを押したとき実行されるブロック
void mousePressed() {
// ここで風船を膨らませたい
}
さて、この風船を膨らませるにはどうすればよいでしょうか?
クリックしたとき、風船の大きさを前より少し大きくすればよいのですが…いま手持ちの知識ではこれを実現する術がありません。
このスケッチを実現するためには、風船の大きさを何かしらの方法で覚えておく必要があります。
このメモしておく・覚えておくために必要なのが、今回のテーマ「変数」です。
「変数 (variable) 」とは、値を覚えておける仕組みです。覚えた値は、取り出したり入れ直したりできます。
そのままだと何の値かわからなくなってしまうので、変数には名前がつけられます。先の例では「風船の大きさ」に使いたいので、その目的がわかるような名前をつけておきたいですね。
変数を新たに作成することを「宣言」といいます。風船の大きさ用の変数を宣言するには、以下のように文を書きます。
float 変数名 ;
変数の名前は、Processingが用意しているキーワードと被らなければ基本的には何でもOKです1。意味が理解しやすい名前をつけましょう。
// ballSize という名前で変数を宣言
// 風船 (balloon) の大きさ (size) に使うので
float ballSize;
void setup() {
size(600, 600);
…
}
これで、 ballSize
という名前で数値の変数が使えるようになりました。
宣言した変数は、これまで出てきた mouseX
や frameCount
などと同じく数値のように扱え、計算式に組み入れることもできます。
void draw() {
// 変数 ballSize を大きさとして円を描く
ellipse(300, 300, ballSize, ballSize);
}
さて、このままスケッチを実行しても何も表示されません。というのも、変数 ballSize
は宣言するのみでは空っぽのままです。覚えておく準備ができたら、中身に数値を入れていきましょう。
変数に値を入れることを「代入」といい、等号 =
を使って文を書きます。
// 変数 ballSize に 100 を入れる
ballSize = 100;
少しややこしいのですが、慣れ親しんだ等号を使うものの「等しい」という意味はなく別物2です。「=
の右側にあるものを左側にコピーする」という意味であることを意識しましょう。
本筋に戻って、風船の最初の大きさ(初期値)を設定しましょう。初期値を設定するのはプログラム開始直後の最初の1回だけでよいので、 setup()
ブロックが適していますね。
// 最初の1回だけ実行されるブロック
void setup() {
size(600, 600);
// 変数 ballSize に 100 を入れる
// 風船の最初の大きさは 100
ballSize = 100;
}
無事に風船は表示されましたか?
さて、ようやくここから変数が活きてくるところです。
これまで書いたコードによって、風船は変数 ballSize
にもとづいて描かれるようになりました。つまり、 ballSize
の数値を大きくすれば風船も大きくなるはずです。
マウスボタンを押したときに実行されるブロック mousePressed()
で、 ballSize
の数値を大きくしましょう。
void mousePressed() {
ballSize = ballSize + 10;
}
=
を「等しい」と読んでしまうと何だか奇妙な式に見えてしまいますが… =
は「代入」、右側の計算結果を左側に入れるという意味でしたね。つまりこの場合、「 ballSize
に 10
足した結果を ballSize
に入れ直す(再代入する)」という意味になります。
ここまで書けたら実行してみてください。クリックするたびに風船が大きくなるスケッチができました!
(→mouse_balloon
)
もう少し複雑な例で練習してみましょう。キーボードの方向キーでボールを動かすスケッチを作ってみます。
まずは円でボールを描くところまで準備しましょう。
void setup() {
size(600, 600);
strokeWeight(2);
fill(100, 200, 255);
}
void draw() {
background(255, 255, 255);
ellipse(300, 300, 50, 50);
}
複雑なプログラムを作るときは、このようにもっともシンプルな形から作りはじめて、目指す形に向けて少しずつ機能を足していきます。焦らず一歩ずつ、着実に進めていきましょう。
今回の例ではボールが移動します。ボールが移動するためには、その位置を覚えて維持する必要がありますね。ボールの位置(X座標、Y座標)をそれぞれ変数にしましょう。
// ボールの位置として使う変数を宣言する
float ballX;
float ballY;
宣言するだけでは変数たちは空っぽなので、 setup()
ブロックで初期化してボールの初期位置を決めましょう。キャンバスの中央がよいので、サイズ 600
の半分にしましょうか。
void setup() {
size(600, 600);
strokeWeight(2);
fill(100, 200, 255);
// それぞれに 300 を代入して初期化
ballX = 600 / 2;
ballY = 600 / 2;
}
ボールの位置が固定のままなので、変数にもとづいた位置に描かれるようにしましょう。円の中心座標を ballX
, ballY
にすればよいですね。
void draw() {
…
ellipse(ballX, ballY, 50, 50);
}
keyPressed()
ブロックマウスボタンを押したときに実行されるブロック mousePressed()
があるように、キーを押したときに実行されるブロック keyPressed()
も用意されています。
// キーを押したときに実行されるブロック
void keyPressed() {
}
どのキーが押されたかは、 keyCode
という特別なキーワードが用意されているのでこれを利用します。方向キーのキーコードはそれぞれ LEFT
, RIGHT
, UP
, DOWN
となります。これらをif文で比較すれば、特定のキーを押しているかどうかで処理を分岐できます。
// キーを押したときに実行されるブロック
void keyPressed() {
// 押しているキーが左方向キーのとき
if (keyCode == LEFT) {
…
}
// 押しているキーが右方向キーのとき
else if (keyCode == RIGHT) {
…
}
}
現状では、変数は初期値のままなのでボールは動きません。先ほど書きかけた keyPressed
の中身を埋めて、 ballX
, ballY
の値を更新してみましょう。答えを見ずに埋められるかチャレンジしてみてください。
void keyPressed() {
// もし押したキーが左矢印( LEFT )のとき
if (keyCode == LEFT) {
// ボールの位置として使っている変数 ballX を 10 減らす
ballX = ballX - 10;
}
// もし押したキーが右矢印( RIGHT )のとき
else if (keyCode == RIGHT) {
// ボールの位置として使っている変数 ballX を 10 増やす
ballX = ballX + 10;
}
// もし押したキーが上矢印のとき
…
}
無事に動いたでしょうか?
(→key_ball
)
変数について、もう少し一般的な話をしておきましょう。
変数を宣言する際、これまではおまじないのように float
と書いてきました。
float 変数名 ;
この float
、実は変数の種類(「型」と呼ぶ)を表しており、「この変数は小数です」という意味を持っています。Processingの世界では、変数に入るものの種類はあらかじめ決めておく必要があるのです。
本来、変数の宣言文は以下のような構造になっています。
変数の型 変数名 ;
Processingの公式リファレンス(Data→Primitive)によると、基本的な型は全部で8種類。ここではよく利用する4種類を紹介します。
型 | 意味 | 例 |
---|---|---|
float | 数値(小数) | 3.1416 , 100.0 |
int | 数値(整数) | 100 |
boolean | 2値(ON/OFF) | true , false |
color | 色 | #0088ff , #7890ab |
float
今回、風船の大きさやボールの位置に利用してきた float
は小数です3。位置や大きさに関するほとんどのものはこれでまかなえるので、もっともよくお世話になる型になるでしょう。
float ballSize;
…
void setup() {
ballSize = 3.14;
// 整数を入れているように見えるが、内部的に 100.0… に変換されている
ballSize = 100;
…
}
int
個数や回数など、端数が発生すべきでない場面では、整数用の int
を利用します。詳細については、後ほどの章で出てくるのでそのときに解説します。
int count;
…
void setup() {
count = 20;
…
}
boolean
照明のスイッチやモード切り替えなど、ONとOFFの2通りしかない場合は boolean
(ブーリアン4)が最適です。 true
がON、 false
がOFFを表します。
boolean light;
boolean sleepy;
…
void setup() {
// ON
light = true;
// OFF
sleepy = false;
…
}
color
Processingには、色を表現するための型も用意されています。 fill()
や stroke()
, background()
など、色を必要とするものに使えます。詳細については、また後日触れることにしましょう。
color col;
…
void setup() {
// 赤色を代入
// (Processingでは # からはじまる16進数表記を色として使える)
col = #ff0000;
// そのまま背景や塗りなどに設定できる
background(col);
fill(col);
stroke(col);
…
}
変数の宣言と初期化の代入は別々の文で書いていましたが、まとめて書くこともできます。
// 宣言
float hoge;
void setup() {
…
// 代入
hoge = 3.14;
…
}
// 宣言と同時に代入
float hoge = 3.14;
慣れてきたら使ってみるとよいでしょう。
これまで、「Processingの特別なキーワード」として説明してきたものがいくつかありましたね。 ()
なしで数値と同様に扱えたものたち、例えば frameCount
, mouseButton
, mouseX
, mouseY
, keyCode
, …。これらは実は変数なのです。
Processingが用意してくれた、宣言なしで使え、勝手に値が更新される特別な変数たち。これらを「システム変数 (system variable)」といいます。
上に挙げた以外にも複数ありますが、よく使う便利なものを紹介しておきましょう。
システム変数 | 型 | 意味 |
---|---|---|
width | int | キャンバスの幅 |
height | int | キャンバスの高さ |
変数の代入は値のコピーです。といってもよくわかりませんよね。ひとつ例をみてみましょう。
最後にクリックした地点から、現在のカーソル位置まで線を引くスケッチです。
(→line_preview
)
クリックした地点を覚えておくために変数を利用しています。
// クリックした位置を覚えておくための変数を宣言
int px;
int py;
void setup() {
size(600, 600);
stroke(0);
fill(0);
}
// マウスを押したとき実行されるブロック
void mousePressed() {
// カーソル位置を変数に代入
px = mouseX;
py = mouseY;
}
void draw() {
background(255);
// クリック位置に円を描く
ellipse(px, py, 10, 10);
// クリックした位置から現在のカーソル位置まで線を描く
line(px, py, mouseX, mouseY);
}
mousePressed()
ブロックでは、用意しておいた変数にカーソル位置 mouseX
, mouseY
を代入していますね。
void mousePressed() {
// カーソル位置を変数に代入
px = mouseX;
py = mouseY;
}
例えばこの時点でのカーソル位置 mouseX
が 123
だった場合、 px
には 123
が代入されます。このとき数値はコピーされるので、あとでカーソル位置がどこに移動しようとも、変数の中身は変わりません。
px
, py
にはそのときクリックした位置の座標が保持されるので、 draw()
ブロック内では最新のカーソル位置 mouseX
, mouseY
まで線を引けているのですね。
void draw() {
…
// クリックした位置から現在のカーソル位置まで線を描く
line(px, py, mouseX, mouseY);
}
変数に慣れるために、さらにいくつかスケッチを作ってみましょう。
クリックするたびに照明のON/OFFを切り替えるスケッチを作ってみましょう。
boolean
が最適)(→bool_light
)
ランダムに動き回るボールを描いてみましょう。 draw()
ブロックを実行するたびに、ボールのX/Y位置をランダムに増減することで実現できます。
draw()
の中)で、位置の変数をランダムに増減させる(→random_walk
)
右クリックするたびに複数のペンを切り替えるスケッチを作ってみましょう。
mouseDragged()
)int
が最適)0
なら〜、 1
なら〜)mouseButton
)(→multi_pen
)
ここまで説明してきたように、変数そのものはデータを覚えておくための仕組みですが、それ以外にもうれしい効果があります。
変数に計算結果を入れておけば、何度も同じ計算をする必要がなくなります。計算自体が複雑だったり、ものすごい回数繰り返されたりする場合は、計算結果を再利用することで負荷軽減にもつながります。
ellipse(width / 2, height / 2, 300, 300);
ellipse(width / 2, height / 2, 200, 200);
ellipse(width / 2, height / 2, 100, 100);
// 変数に代入する際に計算するだけ
float centerX = width / 2;
float centerY = height / 2;
// 計算結果の再利用!
ellipse(centerX, centerY, 300, 300);
ellipse(centerX, centerY, 200, 200);
ellipse(centerX, centerY, 100, 100);
変数は、データに名前をつけて意味づけできる仕組みと捉えることもできます。
わかりやすい名前をつけられれば、他の人や数ヵ月後の自分(≒他人)がコードを読むとき、どんな処理を書いているかを探る手がかりになります。
// 600 って何だっけ?なんで 2 で割ってるんだっけ?
ellipse(600 / 2, 600 / 2, 300, 300);
ellipse(600 / 2, 600 / 2, 200, 200);
ellipse(600 / 2, 600 / 2, 100, 100);
// 「center」って名前がついてるから、たぶんキャンバスの中央のことだな
float centerX = width / 2;
float centerY = height / 2;
ellipse(centerX, centerY, 300, 300);
ellipse(centerX, centerY, 200, 200);
ellipse(centerX, centerY, 100, 100);
=
は「等しい」ではなく「代入」