Processingで学ぶプログラミング入門
本授業では、Processing(プロセッシング)というプログラミング環境を使用して、プログラミングの基本を学習することを目標とする。
Processingとは
絵やアニメーションの表示を簡単に作れることが特徴なプログラミング環境。Java言語をベースにしている。本家は processing.org (英語)。
準備:ダウンロードとインストール
注)授業環境ではインストール済みなので設定不要。以下は、自習のために自宅等のパソコンで利用する場合の説明。
使用するコンピュータにProcessingを導入するには、まずダウロードページから使用するコンピュータの種類に合ったProcessingをダウンロードする。Windowsの場合は[Without Java]と書かれていない方を使うとよい。ダウンロードしたファイルは一旦保存し、すべて展開する(ダウンロードしたファイルアイコンの上で右クリックし、ポップアップメニューから「すべて展開」を選択する)。必要に応じて置き場所は変更してもよい。
Windowsの場合、展開してできたフォルダを開いていくと次の図のようになっているはずである。
Processingの起動
"processing"という名前のアイコンをダブルクリックすると起動する。起動すると、次のようなウィンドウが開く。
「エディタ領域」はプログラムを書く領域である。ここにプログラムを書き、「実行ボタン」をクリックすると、Processingは書かれたプログラムを解釈(コンパイル)して実行する。実行しているプログラムを停止させるには「停止ボタン」をクリックする。プログラムの解釈時や実行中のエラーなどのメッセージは「メッセージ領域」に表示される。「プログラム名」は起動時に自動的に設定されるが、プログラムを保存するときに別の名前に変更することができる。
最初のプログラム
次の1文をエディタ領域に入力してみよう。文字の間違いに気をつけて!!
line(0, 0, 100, 100);
l(小文字のエル)と1(数字)、0(大文字のオー)と0(数字)、,(カンマ)と.(ピリオド)、; (セミコロン)と:(コロン)などはよく間違うので特に注意。また、いわゆる全角文字は使用しないように!!
lineと入力すると赤色に変わる。これは、lineがProcessingで用意されている関数名だからである(関数については後述)。赤くならない場合はなにか間違っているということになる。
入力できたら「実行ボタン」をクリックして実行すると、プログラムが実行されて次のようなウィンドウが表示される。プログラムを終了するには「停止ボタン」をクリックするか、表示されたウィンドウの「x」ボタンをクリックしてウィンドウを閉じればよい。
もし入力間違いをしていたらメッセージ領域の上が赤くなってエラーメッセージが表示されるので、よく見て直そう。
さて、"line"という単語は「線」という意味だから、このプログラムは線を描くものだ、というのはなんとなく想像できると思う。では、その後の数字はなんだろうか?試しに数字を次のように変えて実行してみよう。
line(0, 0, 100, 50);
すると今度は次のような表示になる。
どうやら数字は座標を表しているようだ。実は次のようになっている。
line(x1, y1, x2, y2); 直線を描く
x1: 始点のx座標
y1: 始点のy座標
x2: 終点のx座標
y2: 終点のy座標
注)水色背景の箇所の説明書きは、機能や、x1, x2, ...がどのような意味を表すかを説明するための書き方で、この通りに書けばよいのではないことに注意。
詳しい説明はリファレンス(説明書)がある(ただし英語)。"line"と書かれている部分をダブルクリックして選択し(背景色がオレンジに変わる)、メニューの"Help"の中の"Find inReference"を選択する(下の図)。するとWebブラウザに説明が表示される。
残念ながらProcessingの公式説明書は英語しかないが、有志による日本語訳がWeb上にある。例えば「Processing リファレンス 日本語」で検索するとよいだろう。
練習:数値をいろいろ変えてみて描かれる線が変化することを確認しよう。
値を変更してみると、原点(0, 0)はウィンドウの左上にあり、x軸は右向き、y軸は下向きになっていることがわかると思う。
注:数学ではy軸は上向きになっているのが一般的であるが、コンピュータではこのように下向きになっていることもある。
関数を使う
値を変更してみると思った通りに表示されないことがある。例えば次のプログラムを実行しても、最初のプログラムと同じ表示になる。
line(0, 0, 200, 200);
これは、表示できる範囲が(0, 0)から(100, 100)までになっているからである。では、表示できる範囲を広くしてみよう。このプログラムにもう1行追加してみる。
size(400, 400); line(0, 0, 200, 200);
するとウィンドウが大きくなって、線がウィンドウの左上から中央まで描かれるようになる。
このようにsizeを使うとウィンドウの大きさ(表示範囲)を指定することができる。説明書的に書くと次のようになる。
size(width, height); ウィンドウの大きさを指定する
width: 幅
height: 高さ
さて、これらのlineやsizeというのはProcessingに予め用意されている機能であり、プログラムに書くことで呼び出すことができる、ということが分かったかと思う。プログラム用語では「関数」と呼ぶ。一般的に、関数は次のようにプログラム中で書いて使用する。
【関数名】(【引数1】, 【引数2】, ...)
【関数名】はlineやsizeのような名前である。関数名の後ろには()(丸かっこ)を書く。かっこの間には【引数】(ひきすう)と呼ばれる、関数を呼ぶときのパラメータを書く。引数の個数は関数によって決まっている。
関数によっては値を返すものもある。このように、値を渡すと何かを実行する、あるいはなんらかの値を返すことから、関数と呼ばれている。
もう一つ、重要なプログラムの約束がある。1行の最後には;(セミコロン)を付けなければならない。
厳密には「一つの実行文の最後には;を付けなければならない」であるが、今のところは理解しやすいように1行の最後としておく。
Processingで用意されている関数の一覧は、メニューの"Help"の"Reference"を選択すると表示される(ただし、やっぱり英語)。沢山あるが覚える必要はない。中にはあまり使わないものや難しいものもある。ここでは簡単な図形を描く関数を3つほど紹介しておこう。
rect(x, y, width, height); 長方形を描く
x: 左上のx座標※
y: 左上のy座標※
width: 長方形の幅
height: 長方形の高さ
※初期状態の場合。rectMode関数で変更可能。
ellipse(x, y, width, height); 楕円を描く
x: 中心のx座標※
y: 中心のy座標※
width: 長方形の幅
height: 長方形の高さ
※初期状態の場合。ellipseMode関数で変更可能。
triangle(x1, y1, x2, y2, x3, y3); 三角形を描く
x1: 1点目のx座標
y1: 1点目のy座標
x2: 2点目のx座標
y2: 2点目のy座標
x3: 3点目のx座標
y3: 3点目のy座標
練習:これらの関数を使って絵を描いてみよう。
順次実行
次のプログラムを実行してみよう。表示は下の図のようになる。
size(400, 400); line(0, 0, 200, 200); rect(100, 100, 150, 100); ellipse(300, 250, 150, 250); triangle(100, 250, 50, 350, 300, 350);
rectやellipseやtriangleは、特に指定をしなかったら縁が黒色、中が白色で塗りつぶして図形を描く。
表示を見ると図形が重なり合ってることがわかる。では試しにプログラム中の行の順番を入れ替えてみよう。エディタ領域で移動させたい範囲をマウスドラッグで選択して(選択範囲の文字の背景色がオレンジ色になる)、メニューのEditのCutを選択すると切り取ることができる。次に移動させたい場所をクリックしてカーソルを移動させ、メニューのEditのPasteを選択すると切り取った行を貼り付けることができる。
基本的な操作はワープロなどの文章編集ソフトと同じである。CutやPasteなどのメニューはControl+X(Controlキーを押しながらXキー)やControl+Vで操作することもできる(キーボードショートカット)。
練習:図形を描く関数の順番を入れ替えて実行してみよう。
関数の順番を入れ替えると重なり方が変わることがわかると思う。例えば次のように逆順にしたプログラムを実行すると下の絵のようになる。
size(400, 400); ellipse(300, 250, 150, 250); triangle(100, 250, 50, 350, 300, 350); rect(100, 100, 150, 100); line(0, 0, 200, 200);
実は描く順番はプログラムで書いた順番になっている。
size関数を途中に移動させると、size関数より前の図形が描かれないのは、既に描いた後にウィンドウの大きさを指定して開き直すからである。
このように、プログラムは上から順に実行する(順次実行)。当たり前のように聞こえるし、実際当たり前なのだが、基本的かつ重要な約束である。
関数を書いてみる
ここまで、Processingで用意されている関数を使ってみたが、自分で関数を新たに作ることもできる。プログラムを作る人が自由に作る関数の他に、Processingには予め役割が決められている関数がある。ここでは次の2つの役割が決まっている関数を使って、先ほどのプログラムを書き直してみよう。
プログラムを実行すると最初に1回だけ呼ばれる関数
void setup() {
(この間に実行文を書く)
}
絵を描く関数。
void draw() {
(この間に実行文を書く)
}
「実行文」というのは、関数を呼ぶ以外に、計算式などの他の書き方も含むからである。次回以降に説明予定。
いずれの関数も最初にvoidと書いてある。これは関数を呼び終わったときに、この関数が返す値の型(種類)を決めておくためのものであるが、voidは「この関数は何も返さない」ことを表している。他の種類についてはここでは省略する。
次に来る、setup()やdraw()は関数名である。これらの関数は引数を必要としない(というか、これらの関数はProcessingで引数を受け取らないと決められている)ので、()の中は何も書かれていない。
その後の{と}の間が関数の本体である。つまり、この関数が呼ばれたら、{と}の間に書かれているプログラムを実行する。{と}で囲む範囲をブロックと呼ぶ。ブロックは関数以外でも使用する。
先ほどのプログラムではウィンドウの大きさを指定は最初に1回だけやればよいのでsetup関数の中に入れ、残りはdraw関数の中に入れてみる。
void setup() { size(400, 400); } void draw() { ellipse(300, 250, 150, 250); triangle(100, 250, 50, 350, 300, 350); rect(100, 100, 150, 100); line(0, 0, 200, 200); }
実行させても前のプログラムと変わらない。
変数
ここまでのプログラムは図形の座標をそのままプログラム中で指定している。これでは予め決まっていることをコンピュータにやらせるだけで面白くないので、ちょっと違うプログラムを作ってみよう。
void setup() { size(400, 400); } void draw() { ellipse(mouseX, mouseY, 50, 50); }
このプログラムを実行し、開いたウィンドウの上にマウスカーソルを移動させると、マウスカーソルの位置に円が描かれていく(下図)。
このプログラム中、楕円を描くellipse関数の中でmouseXとmouseYという文字列があるが、ellpse関数ではこれらの位置には楕円の中心座標を書くことになっている。実際、マウスカーソルの位置に楕円の中心が来ていることが確認できると思う。
マウスカーソルを動かすたびに円が描かれていくのは、draw()関数を作っているからである。draw()関数が繰り返し実行されるような仕組みがProcessingでは用意されている。
つまりmouseXとmouseYはマウスカーソルのx座標とy座標であることがわかる。このような、何か値を覚えている箱のようなものを変数と呼ぶ。数学ではxやyなどを変数と呼ぶが、プログラムの場合は任意の文字列で区別する。mouseXとmouseYはProcessingで予め用意されている変数で、その値は自動的にマウスカーソルの座標になっている。
式と演算子
先ほどのプログラムでは円を描いていたが、同じようにして四角を描いてみよう。四角を描くにはellipse関数の代わりにrect関数を使えばよい。
void setup() { size(400, 400); } void draw() { rect(mouseX, mouseY, 50, 50); }
実行させてみると、同じようにマウスカーソルについて四角が描かれていくが、位置が真ん中ではない。これはrect関数の最初の2つの引数は描く四角形の左上の座標になっているからである。
実はrectMode関数で、rect関数の最初の2つの引数の意味を設定変更可能であるが、ここでは説明のため使わないことにする。
では、マウスカーソルが真ん中に来るように調整してみよう。描く正方形の大きさは縦横50となっているから、その半分の25ずつだけ縦横ずらせばよい。つまり、マウスカーソルの位置から、縦横25だけ小さい座標から描けばよいことになる。
プログラム中でこのような計算をさせるには、数式と同じように書けばよい。
void setup() { size(400, 400); } void draw() { rect(mouseX - 25, mouseY - 25, 50, 50); }
実行するとマウスカーソルが四角の真ん中に位置するようになったことが確認できる。
プログラム中で四則演算を行うには、和(足し算)と差(引き算)は数式と同じように+と-を使う。積(かけ算)と商(割り算)は、数式の×や÷記号はキーボードに無いので、代わりに*(アスタリスク)と/(スラッシュ)を使う。
練習:マウスカーソルの位置に×印を描くようにしてみよう。大きさは適当な大きさでよい。
変数を作る
今度は、マウスカーソルの位置に、半径が10の円と30の円と50の円の3つを描いてみよう。
void setup() { size(400, 400); } void draw() { ellipse(mouseX, mouseY, 50, 50); ellipse(mouseX, mouseY, 30, 30); ellipse(mouseX, mouseY, 10, 10); }
描く順番に気をつけよう。大きい円を後から描くと先に描いた円を上から消してしまう。
マウスカーソルが円を隠してしまうので、3つの円の中心を(-25, -25)だけずらして描くようにしてみよう。
void setup() { size(400, 400); } void draw() { ellipse(mouseX - 25, mouseY - 25, 50, 50); ellipse(mouseX - 25, mouseY - 25, 30, 30); ellipse(mouseX - 25, mouseY - 25, 10, 10); }
さて、このプログラムを見ると、mouseX - 25 と mouseY - 25 という計算式が3行とも出てきている。例えば、ずらす量を(-25, -25)から(-30, -25)に変更することにしたとしよう。すると、mouseX - 25 を mouseX - 30 に書き直さなければならないが、3カ所もあって面倒だし、間違えて mouseY - 25 の方を書き換えてしまうかもしれない。
そこで、同じ値を使い回せるように変数に値を格納してみよう。
まず、変数を準備する。mouseXやmouseYのように最初から用意されている変数とは別に、プログラムの中で変数を宣言することで使えるようになる。変数を宣言するには、変数の型(変数が覚えることのできる値の種類)と名前を決めなければならない。宣言は次のように書く。
変数の型 変数名;
例:
int x;
float y;
int a, b;
intは整数のみを扱う型、floatは実数(小数)を扱う型である。他にも扱える値の範囲や種類の違う型があるが、ここでは省略する。
逆に言うと、宣言していない変数は使うことができない(実行させるとエラーが出る)。宣言は使う前に行う必要がある。もう一つ、変数の有効範囲に注意する必要がある。変数は、その変数を宣言した位置の前後の { と } の間(ブロック)内のみで使うことができる。ここではdraw関数の中で使いたいので、draw関数の最初で宣言してみる。
void setup() { size(400, 400); } void draw() { int x; // 円の中心のx座標を覚えておく変数 int y; // 円の中心のy座標を覚えておく変数 ellipse(mouseX - 25, mouseY - 25, 50, 50); ellipse(mouseX - 25, mouseY - 25, 30, 30); ellipse(mouseX - 25, mouseY - 25, 10, 10); }
プログラム中で // で始まる部分から右端までは無いものとして扱われるので(コメントと呼ぶ)、プログラムが分かりやすくなるように書くとよい。また、/* と */ で囲まれている範囲もコメントとなる。なお、Processingの標準エディタで日本語を入力しようとするとエディタ内でかな漢字変換できないので少々不便。
変数は宣言しただけでは値が決まっていないので、値を設定する必要がある。そのためには = (代入演算子)を使う。
変数 = 計算式;
右辺の値(計算式の結果)を左辺の変数に代入(格納)する
数学では = (等号)は右辺と左辺の値が等しいことを表すが、プログラムでは右辺の値を左辺の変数に代入する記号であることに注意。
Processing以外でも多くのプログラミング言語では=を代入に使用している。
変数 x には mouseX - 25 の値を、変数 y には mouseY - 25 の値をそれぞれ代入したいので、次のようになる。
void setup() { size(400, 400); } void draw() { int x; // 円の中心のx座標を覚えておく変数 int y; // 円の中心のy座標を覚えておく変数 x = mouseX - 25; // x に mouseX - 25 の計算結果を格納する y = mouseY - 25; // y に mouseY - 25 の計算結果を格納する ellipse(mouseX - 25, mouseY - 25, 50, 50); ellipse(mouseX - 25, mouseY - 25, 30, 30); ellipse(mouseX - 25, mouseY - 25, 10, 10); }
最後に、3つのellipse関数の中で使用している mouseX - 25 と mouseY - 25 の部分をそれぞれ x と y に置き換える。
void setup() { size(400, 400); } void draw() { int x; // 円の中心のx座標を覚えておく変数 int y; // 円の中心のy座標を覚えておく変数 x = mouseX - 25; // x に mouseX - 25 の計算結果を格納する y = mouseY - 25; // y に mouseY - 25 の計算結果を格納する ellipse(x, y, 50, 50); ellipse(x, y, 30, 30); ellipse(x, y, 10, 10); }
最初のプログラムに比べると行数が増えていて面倒なように見えるが、実は内容はすっきりしている。例えば mouseX - 25 や mouseY - 25 の部分を変更してみると分かると思う。
条件分岐
前回のプログラムではマウスカーソルがウィンドウ内を動くと図形が描かれていたが、何か絵を描こうとすると不便である。そこで、マウスのボタンを押している間だけ描くように修正してみよう。
void setup() { size(400, 400); } void draw() { if (mousePressed == true) { ellipse(mouseX, mouseY, 10, 10); } }
6行目の、if (mousePressed == true) がこのプログラムのミソである。
if は英語で「もし……ならば」と訳すとおり、プログラムでも条件が成り立っていれば指定された手順を実行する機能を持っている。書き方としては次のようになる。
if (条件式) {.....}
if の次には()で囲って条件式を書く。条件式は、真(正しい)か偽(正しくない)かのどちらかが決まる式で、左辺と右辺の値を比較する比較演算子がよく用いられる。上のプログラムでは == という、左辺と右辺の値が等しい場合に真、等しくない場合に偽となる比較演算子を使っている。
== の役割は数学の = と同じである。前に説明したようにプログラムでは = は代入演算子であるので、混同しないように注意。Processingでは == が書かれるべき箇所で = を書いてしまった場合、あるいはその逆の場合、実行するとエラーとなることが多いので間違うことは少ないと思う。
条件式の次に来る { と } の間には、条件式が成立した場合に実行するプログラムを書く。{ と }の間は何行あってもよい。
マウスのボタンが押されているかどうかは、mousePressed という変数に格納されている。変数 mousePressed は boolean 型(論理型)の変数である。boolean型は true(真)かfalse(偽)のどちらかの値だけ取り得る。mousePressed ではマウスのボタンが押されていたら true、押されていなかったら false となる。
つまり上のプログラムは、条件式 mousePressed == true によってマウスのボタンが押されていたら { } 内を実行する(円を描く)、となっている。マウスのボタンが押されていなかったら条件式が成立しないので円は描かれない。
色々な比較演算子
== 以外にも、次のような比較演算子を使うことができる。
比較演算子
== 左辺と右辺の値が等しいか
!= 左辺と右辺の値が異なるか
> 左辺が右辺より大きいか
>= 左辺が右辺より大きいか等しいか
< 左辺が右辺より小さいか
<= 左辺が右辺より小さいか等しいか
練習:マウスカーソルのx座標がウィンドウの右半分にあるときだけ、マウスカーソルの位置に円を描くようにしてみよう。マウスボタンは押していなくても描くものとする。
組み合わせ条件
「さもなければ」
if文は条件式が成立すれば、その後ろの { から } までを実行する。では条件式が成り立った場合と成り立ってない場合で違う処理を実行したい場合はどうすればよいか。例えば、マウスボタンを押しているときは黒丸、押していないときは白丸を描くようにしてみよう。
塗りつぶす色を変更するにはfill関数を使う。fill関数を呼んだ後で描く図形の塗りつぶし色は、fill関数で設定した色になる。
fill(r, g, b); 塗りつぶし色を設定する
r: 赤色成分の強さ。0〜255の値。
g: 緑色成分の強さ。0〜255の値。
b: 青色成分の強さ。0〜255の値。
コンピュータでの色は光の三原色(加法混色)、つまり赤と緑と青を混ぜて色を表現する。
fill関数には他の色の指定方法も用意されているが、ここでは省略。
これを使って、マウスボタンを押しているときは赤、押していないときは青の円をマウスカーソルの位置に描くようにしてみよう。赤はfill(255, 0, 0);、青はfill(0, 0, 255);とすればよい。
if文だけを使うと次のように書ける。
void setup() { size(400, 400); } void draw() { if (mousePressed == true) { // マウスボタンが押されていたら fill(255, 0, 0); } if (mousePressed == false) { // マウスボタンが押されていなかったら fill(0, 0, 255); } ellipse(mouseX, mouseY, 10, 10); }
ここで、変数mousePressed(マウスボタンが押されているかいないか)は、true(ボタンが押されている)かfalse(押されていない)のどちらかの状態しか無いことに注意して見よう。if文には、「もし……ならば……、さもなければ……」という書き方が用意されている。
if (条件式) {.....} else {.....}
これを使うと先ほどのプログラムは次のように書ける。
void setup() { size(400, 400); } void draw() { if (mousePressed == true) { // マウスボタンが押されていたら fill(255, 0, 0); } else { // マウスボタンが押されていなかったら fill(0, 0, 255); } ellipse(mouseX, mouseY, 10, 10); }
実行してみても動作は変わらない。プログラムは1行減っただけであるが、このように書く方が意図が理解しやすく、間違いを起こしにくい。
○○かつ××
前に練習問題でマウスカーソルのx座標がウィンドウの右半分にあるときだけマウスカーソルの位置に円を描くプログラムを作った。今度は、さらにマウスボタンが押されているときだけ描くようにしてみよう。
前に作ったプログラムに、マウスボタンが押されている条件判断を付け加えてみると次のようになる。
void setup() { size(400, 400); } void draw() { if (mouseX >= 200) { // マウスカーソルがウィンドウの右半分ならば if (mousePressed == true) { // マウスボタンが押されていたら ellipse(mouseX, mouseY, 10, 10); } } }
このプログラムでは、「マウスカーソルがウィンドウの右半分にあるか」という条件が成立した場合に、さらに「マウスボタンが押されているか」という条件を判断するようにしている。
このように、2つ以上の条件が同時に成立した場合に所定の処理を行いたい場合は、if文の中にif文を書けばよい。
また、&&という記号(AND演算子)を使って条件判定をつなげることもできる。&&は、その左右に書かれた条件式の両方が成立しているかどうかを判断する。上のプログラムを&&を使って書くと次のようになる。
void setup() { size(400, 400); } void draw() { if (mouseX >= 200 && mousePressed == true) { // マウスカーソルがウィンドウの右半分で、マウスボタンが押されてるならば ellipse(mouseX, mouseY, 10, 10); } }
○○または××
さきほどのプログラムは、「マウスカーソルがウィンドウの右半分にあり、かつ、マウスボタンが押されている」時に円を描いた。では、「マウスカーソルがウィンドウの右半分にあるか、または、マウスボタンが押されている」時に描くにはどうすればよいか?
単純に、これらの2つの条件を別々に判定すると次のようになる。
void setup() { size(400, 400); } void draw() { if (mouseX >= 200) { // マウスカーソルがウィンドウの右半分ならば ellipse(mouseX, mouseY, 10, 10); } if (mousePressed == true) { // マウスボタンが押されていたら ellipse(mouseX, mouseY, 10, 10); } }
このプログラムでは、「マウスカーソルがウィンドウの右半分にある」場合も「マウスボタンが押されている」場合も円を描くので一見正しくできているように見える。しかし、「マウスカーソルがウィンドウの右半分にあり、かつ、マウスボタンが押されている」場合には両方で円を描くので2つ描いてしまっている。
そこで、後半の「マウスボタンが押されている」場合に描くのは「マウスカーソルがウィンドウの右半分にない」場合のみにしてみよう。これは、前に説明したelse(さもなければ)を使って実現できる。
void setup() { size(400, 400); } void draw() { if (mouseX >= 200) { // マウスカーソルがウィンドウの右半分ならば ellipse(mouseX, mouseY, 10, 10); // 円1(下の表参照) } else { if (mousePressed == true) { // マウスボタンが押されていたら ellipse(mouseX, mouseY, 10, 10); // 円2(下の表参照) } } }
条件の組み合わせを表にすると次のようになる。
このように、条件を整理して考えることでif文の組み合わせだけでも複雑な条件の組み合わせを判定することができるが、この場合、||(OR演算子)を使うと簡単に書くことができる。||は、その左右に書かれた条件式のどちらかが成立しているかどうかを判断する。
void setup() { size(400, 400); } void draw() { if (mouseX >= 200 || mousePressed == true) { // マウスカーソルがウィンドウの右半分、または、マウスボタンが押されていたら ellipse(mouseX, mouseY, 10, 10); // 円1(下の表参照) } }
複雑な条件
以上説明した条件の書き方を組み合わせて、ちょっと複雑な条件判定をしてみよう。
ウィンドウの中心に原点とするx軸とy軸をとり、第1象限か第3象限にマウスカーソルが位置しているときに円を描くようにしてみる(下図)。
まず第1象限の条件をxとyの値で表すと、「x > 0 かつ y > 0」となる。
練習:第3象限の条件もxとyの値で表してみよう。
この2つの不等式が同時に成立することを表すには「かつ」&&を使って、
x > 0 && y > 0
のように書けばよい。
「第1象限か第3象限にマウスカーソルが位置している」は「第1象限にマウスカーソルが位置している、または、第3象限にマウスカーソルが位置している」となるから、||を使ってこれらの条件をつなげばよい。
練習:このプログラムを書いてみよう。
繰り返し
マウスを1回クリックすると、ウィンドウ内のランダムな位置に丸を描くプログラムは次のようになる。
boolean mouseClicked = false; // マウスがクリックされたか覚えておく変数(trueの時クリックされた) void setup() { size(400, 400); } void draw() { if (mouseClicked == true) { // マウスがクリックされたら int x, y; x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); mouseClicked = false; // マウスがクリックされたことを忘れる } } void mouseClicked() { mouseClicked = true; // マウスがクリックされたことを覚えておく }
ちょっと複雑だが順番に見ていこう。
このプログラムは3つの関数でできている。setup()とdraw()はこれまでにも出てきたものである。setup()はプログラムを実行すると最初の1回だけ呼ばれる関数である。draw()はウィンドウ内の表示を行うための関数で、自動的に繰り返し呼ばれる。
mouseClicked()は新しい関数である。この関数は、マウスがクリックされた時に自動的に呼ばれる。上のプログラムでは、マウスがクリックされたことを覚えておく変数mouseClickedを用意しておき、マウスがクリックされた時に値をtrueにしている。図形を描く処理はdraw()関数の中に書かなければ表示されないので、このような仕掛けにしている。
マウスボタンは「クリックされた」「クリックされていない」の2つの状態しかないので、変数mouseClickedはboolean型(trueかfalseのどちらかの値のみとることのできる型)としている。
draw()関数の中では、最初の条件文でマウスがクリックされたかどうかをmouseClickedの値で判別している。クリックされていたら、x座標とy座標の値を表す変数xとyを用意し、それぞれの座標を乱数で決めている。乱数はrandom()関数を使って発生させる。
float random(high) 0からhighまでの乱数を返す
なお、(int)は整数型に変換することを指示する書き方である。random()関数がfloat型(実数型、小数点以下のある値)を返すのに対し、代入する側の変数xやyがint型(整数型)なので、型を変換する必要がある。整数型に変換する際、小数点以下は切り捨てられる。
ランダムに決めた座標に円を描いた後、mouseClickedの値をfalseに設定する。これがないと、いつまでもmouseClickedの値がtrueのままになってしまい、マウスをクリックしていなくても円を描き続けてしまう。
さて、このプログラムはマウスをクリックするたびに1個円を描くが、1回クリックすると5個円を描くようにしてみよう。
単純に円を描く部分を5回書けばとりあえずできる。
boolean mouseClicked = false; // マウスがクリックされたか覚えておく変数(trueの時クリックされた) void setup() { size(400, 400); } void draw() { if (mouseClicked == true) { // マウスがクリックされたら int x, y; // 1個目 x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); // 2個目 x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); // 3個目 x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); // 4個目 x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); // 5個目 x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); mouseClicked = false; // マウスがクリックされたことを忘れる } } void mouseClicked() { mouseClicked = true; // マウスがクリックされたことを覚えておく }
ちょっとめんどくさい。5個ならまだしも100個とかになると無理である。
同じプログラムを繰り返すために、次のような『繰り返し」を指定する構文が用意されている。
while(条件式) {.....}
条件式が成立している間、{.....}の中身を繰り返す。
上のプログラムの場合、描いた回数を数える変数を用意して5回描くまで繰り返すようにすればよい。例えば次のようになる。
boolean mouseClicked = false; // マウスがクリックされたか覚えておく変数(trueの時クリックされた) void setup() { size(400, 400); } void draw() { if (mouseClicked == true) { // マウスがクリックされたら int x, y; int count = 0; // 描いた回数を数える変数 while (count < 5) { // 5回描くまで繰り返す x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); count = count + 1; // 描いた回数を1回増やす } mouseClicked = false; // マウスがクリックされたことを忘れる } } void mouseClicked() { mouseClicked = true; // マウスがクリックされたことを覚えておく }
while文の条件式 count < 5 によって、countの値が5になるまでwhile文の中身(後ろの{}の間の処理)が繰り返される。while文の{}の最後の行、count = count + 1;は、countの値を1増やしている。=は等号ではなく代入文であることに注意!右辺の式count + 1を計算した結果をcountの新しい値とする、という処理になる。
count = count + 1; のように1値を増やす処理はよく使われるので、count++; のように、++演算子を使って書くこともできる。
このように回数を数える繰り返しはよく使われる基本パタンであるので、次のような書き方も用意されている。
for(初期化式; 条件式; 反復式) {.....}
初期化式:最初に1回だけ実行される式。通常はカウンタ変数の値を初期化する。
条件式:繰り返しを継続するか判定する式。trueの場合継続する。
反復式:{.....}の処理を1回実行した後で、実行される式。通常はカウンタ変数の値を増減させる。
forを使うと次のように書ける。
..... int count; // 描いた回数を数える変数 for(count = 0; count < 5; count = count + 1) { // 5回描くまで繰り返す x = (int)random(400); // x座標をランダムに決める y = (int)random(400); // y座標をランダムに決める ellipse(x, y, 10, 10); } .....
練習:次の図のように、400x400のウィンドウに、ウィンドウの中央を中心として直径が400, 350, 300, 250, 200の同心円を描くプログラムを考えてみよう。マウスの操作は考えなくてよい。描く順番に気をつけて!
アニメーション
draw()関数は自動的に繰り返し呼び出されるので、次のプログラムのように描く座標を変化させるとアニメーション表示ができる。
int x = 0; // 円の中心のx座標 void setup() { size(400, 400); } void draw() { background(255); ellipse(x, 200, 20, 20); x = x + 5; // 円の中心のx座標を5増やす }
background()関数はウィンドウの背景色を設定する関数である。これを呼ぶことで画面をクリアすることができる。
background(gray); 白黒モードで背景色を設定する。0の場合黒、255の場合白になる(0〜255の値)。
background(r, g, b); カラーモードで背景色を設定する。それぞれ0〜255の値。
変数xを宣言している位置(このプログラム例では一番最初)に注意。draw()が繰り返し呼ばれる際、xの値を忘れてしまわないように、draw()の外で変数を宣言する必要がある。draw()の中で宣言すると、draw()が呼ばれるたびに別の変数を用意することになってしまい、値を忘れてしまう。
draw()関数が呼ばれるたびに円の中心のx座標を5ずつ増やしているので、右に動いていくように見える。この増やす量を大きくすると速く、小さくすると遅く動いているように見える。
練習:xの変化量を変えてみて速度が変わることを確認しよう。
壁で跳ね返るようにする
このプログラムでは、drawが呼ばれ続けてxの値が際限なく大きくなっていくので、円が右端から外に出てそのまま何も表示しなくなってしまう。そこで、ウィンドウの端まで円が移動したら反対向きに移動するようにしてみる。
ウィンドウの右端のx座標は400なので、円の中心座標が400に達したら左向きに移動させることにする。上のプログラムのようにx座標の値を大きくしていけば右向き(正の方向)に移動するのだから、左向き(負の方向)に移動させるには値が小さくなるようにすればよい。
右向きに移動しているか左向きに移動しているかを覚えておく変数を追加すると次のようなプログラムになる。
int x = 0; // 円の中心のx座標 boolean xDirection = true; // x方向の移動方向。trueの時右向き、falseの時左向き void setup() { size(400, 400); } void draw() { background(255); ellipse(x, 200, 20, 20); if (xDirection == true) { // 右向き x = x + 5; // 円の中心のx座標を5増やす } else { x = x - 5; // 円の中心のx座標を5減らす } if (x >= 400) { // 右端に達した? xDirection = false; } }
練習:上のプログラムに、円が左端に達したときにも移動する向きが反対になるように処理を追加してみよう。
移動方向を速度で扱うようにする
このプログラムでは動く向きを変数で覚えていて、向きに応じて次のx座標の計算を切り替えていた。が、向きと移動量をまとめて一つの変数で扱うとプログラムがより簡単になる。
int x = 0; // 円の中心のx座標 int dx = 5; // x方向の1回あたりの移動量。正の場合右向き、負の場合左向きに移動する void setup() { size(400, 400); } void draw() { background(255); ellipse(x, 200, 20, 20); x = x + dx; if (x <= 0 || x >= 400) { // 左端か右端に達した? dx = -dx; // 移動方向を反転させる } }
変数dxは速度と考えればよい。符号が向きを表している。1回の画面書き換えごとに速度の分だけx座標が変化する。
練習:壁に達したときの条件判定と向きの反転のプログラムの意味を考えてみよう。
2次元方向に移動させる
次は、1次元(x軸方向)だけに動くだけでなく、2次元的に動くようにしてみよう。2次元的な動きは、x軸方向の動きとy軸方向の動きに分けることができる。つまり、それぞれの方向に対して、速度を設定しておき、座標を計算すればよい。
int x = 0; // 円の中心のx座標 int y = 0; // 円の中心のy座標 int dx = 5; // x方向の1回あたりの移動量。正の場合右向き、負の場合左向きに移動する int dy = 3; // y方向の1回あたりの移動量。正の場合下向き、負の場合上向きに移動する void setup() { size(400, 400); } void draw() { background(255); ellipse(x, y, 20, 20); x = x + dx; // ??? if (x <= 0 || x >= 400) { // 左端か右端に達した? dx = -dx; // 移動方向を反転させる } // ??? }
練習:上のプログラムの // ??? の部分を書いてプログラムを完成させよう。
自由課題
課題:自由にプログラムを作ってみよう。説明した以外の関数を使ってもよい。
参考
Processingクイックリファレンス 主な機能、関数の説明。
Processingリファレンス すべての機能、関数の説明の日本語訳。少々分量が多い。