Processingで学ぶプログラミング入門

本授業では、Processing(プロセッシング)とArduino(アルディーノ)というプログラミング環境を使用して、プログラミングの基本を学習することを目標とする。

目次

Processingとは

絵やアニメーションの表示を簡単に作れることが特徴なプログラミング環境。Java言語をベースにしている。本家は processing.org (英語)。

準備:ダウンロードとインストール

注)授業環境ではインストール済みなので設定不要。以下は、自習のために自宅等のパソコンで利用する場合の説明。

使用するコンピュータにProcessingを導入するには、まずダウロードページから使用するコンピュータの種類に合ったProcessingをダウンロードする。Windowsの場合は64bit版と32bit版があるので、使用しているWindowsに合わせて選ぶ。ダウンロードしたファイルは一旦保存し、すべて展開する(ダウンロードしたファイルアイコンの上で右クリックし、ポップアップメニューから「すべて展開」を選択する)。必要に応じて置き場所は変更してもよい。

Windowsの場合、展開してできたフォルダを開いていくと次の図のようになっているはずである。この中の、黒い円形のアイコン(processing.exe)がProcessingの本体である。

Processingの起動

"processing.exe"という名前のアイコンをダブルクリックすると起動する。起動すると、次のようなウィンドウが開く。

「エディタ領域」はプログラムを書く領域である。ここにプログラムを書き、「実行ボタン」をクリックすると、Processingは書かれたプログラムを解釈(コンパイル)して実行する。実行しているプログラムを停止させるには「停止ボタン」をクリックする。プログラムの解釈時や実行中のエラーなどのメッセージは「メッセージ領域」に表示される。「プログラム名」は起動時に自動的に設定されるが、プログラムを保存するときに別の名前に変更することができる。

ウィンドウの大きさはWindowsと同様の操作で変更できるのでプログラムが読みやすいように変更するとよい。エディタ領域に日本語を入力すると、初期状態のままでは正しく表示されないかもしれない(初期設定のフォントが日本語に対応していないため)。その場合はファイルメニューの中の「設定…」を選択し、開いた設定ウィンドウの中の「エディタとコンソールのフォント」を変更するとよい。

最初のプログラム

次の1文をエディタ領域に入力してみよう。文字の間違いに気をつけて!! lineは線という意味の英単語である。()の中の0や100は数字である。;(セミコロン)と:(コロン)の違いに注意しよう。プログラムでは;を使うことが圧倒的に多い。

line(0, 0, 100, 100);

l(小文字のエル)と1(数字)、0(大文字のオー)と0(数字)、,(カンマ)と.(ピリオド)、; (セミコロン)と:(コロン)などはよく間違うので特に注意。また、いわゆる全角文字は使用しないように!!

lineと入力すると青色に変わる。これは、lineがProcessingで用意されている関数名だからである(関数については後述)。青くならない場合はなにか間違っているということになる。

現在の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とは違う部分があるかもしれない。例えば「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で用意されている関数の一覧は、メニューの「ヘルプ」の「リファレンス」を選択すると表示される(ただし、やっぱり英語)。沢山あるが覚える必要はない。中にはあまり使わないものや難しいものもある。ここでは簡単な図形を描く関数を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関数で変更可能。

Processingには円を描く関数は用意されていないので、円を描きたい場合はellipse関数を使って幅と高さの値が同じ楕円を描く。

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(80, 100, 150, 100);
ellipse(300, 250, 200, 250);
triangle(100, 180, 50, 350, 300, 350);

rectやellipseやtriangleは、特に指定をしなかったら縁が黒色、中が白色で塗りつぶして図形を描く。

表示を見ると図形が重なり合ってることがわかる。では試しにプログラム中の行の順番を入れ替えてみよう。エディタ領域で移動させたい範囲をマウスドラッグで選択して(選択範囲の文字の背景色がオレンジ色になる)、メニューの「編集」の「切り取り」を選択すると切り取ることができる。次に移動させたい場所をクリックしてカーソルを移動させ、メニューの「編集」の「貼り付け」を選択すると切り取った行を貼り付けることができる。

基本的な操作はワープロなどの文章編集ソフトと同じである。切り取りや貼り付けなどのメニューはControl+X(Controlキーを押しながらXキー)やControl+Vで操作することもできる(キーボードショートカット)。

練習:図形を描く関数の順番を入れ替えて実行してみよう。

関数の順番を入れ替えると重なり方が変わることがわかると思う。例えば次のように逆順にしたプログラムを実行すると下の絵のようになる。

size(400, 400);
ellipse(300, 250, 200, 250);
triangle(100, 180, 50, 350, 300, 350);
rect(80, 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, 200, 250);
  triangle(100, 180, 50, 350, 300, 350);
  rect(80, 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()関数は特に設定しなければ1秒間に60回、自動的に繰り返して実行される。

このプログラムの動作を見ると、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);
}

実行するとマウスカーソルが四角の真ん中に位置するようになったことが確認できる。

プログラム中で四則演算を行うには、和(足し算)と差(引き算)は数式と同じように+と-を使う。積(かけ算)と商(割り算)は、数式の×や÷記号はキーボードに無いので、代わりに*(アスタリスク)と/(スラッシュ)を使う。

練習:マウスカーソルの位置に×印を描くようにしてみよう。大きさは適当な大きさでよい。×印は2本の直線で描けばよい。

変数を作る

今度は、マウスカーソルの位置に、半径が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以外でも多くのプログラミング言語では=を代入に使用している。

変数 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まで(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;  // 移動方向を反転させる
  }
  // ???
}

練習:上のプログラムの // ??? の部分を書いてプログラムを完成させよう。

以下、暫定版です。内容を変更するかもしれません。

配列

次のプログラムは円が上下に跳ね返り続けるプログラムである。

int y = 0;  // 円の中心のy座標
int dy = 5;  // 円のy方向の速度

void setup() {
  size(400, 400);
}

void draw() {
  background(255);  // ウィンドウを消す
  ellipse(200, y, 20, 20);  // 円を描く
  y = y + dy;  // 円を移動させる
  if (y <= 0 || 400 <= y) {  // 円が上下の端を超えたら速度を反転する
    dy = -dy;
  }
}

このプログラムで動く円を3つに増やしてみよう。x座標が同じだと重なってしまうので、描く位置はずらしておく。

int y = 0;  // 円の中心のy座標
int dy = 5;  // 円のy方向の速度

void setup() {
  size(400, 400);
}

void draw() {
  background(255);  // ウィンドウを消す
  ellipse(150, y, 20, 20);  // 左の円
  ellipse(200, y, 20, 20);  // 真ん中の円
  ellipse(250, y, 20, 20);  // 右の円
  y = y + dy;  // 円を移動させる
  if (y <= 0 || 400 <= y) {  // 円が上下の端を超えたら速度を反転する
    dy = -dy;
  }
}

このプログラムでは同じyの値を使って3つの円を描いているので、3つとも同じように上下に跳ねる。3つの円の跳ね方を異なるようにするには、3つの円それぞれの位置と速度を別の変数とする必要がある。

int y1 = 0;  // 円1の中心のy座標
int dy1 = 5;  // 円1のy方向の速度
int y2 = 100;  // 円2の中心のy座標
int dy2 = 5;  // 円2のy方向の速度
int y3 = 200;  // 円3の中心のy座標
int dy3 = 5;  // 円3のy方向の速度

void setup() {
  size(400, 400);
}

void draw() {
  background(255);  // ウィンドウを消す
  ellipse(150, y1, 20, 20);  // 円1(左の円)
  ellipse(200, y2, 20, 20);  // 円2(真ん中の円)
  ellipse(250, y3, 20, 20);  // 円3(右の円)
  y1 = y1 + dy1;  // 円1を移動させる
  if (y1 <= 0 || 400 <= y1) {  // 円1が上下の端を超えたら速度を反転する
    dy1 = -dy1;
  }
  y2 = y2 + dy2;  // 円2を移動させる
  if (y2 <= 0 || 400 <= y2) {  // 円2が上下の端を超えたら速度を反転する
    dy2 = -dy2;
  }
  y3 = y3 + dy3;  // 円3を移動させる
  if (y3 <= 0 || 400 <= y3) {  // 円3が上下の端を超えたら速度を反転する
    dy3 = -dy3;
  }
}

3つの円に対して変数や実行文を書いているので、最初のプログラムの3倍になってしまっている。しかし、位置は異なるもののやっていることは同じなので何とかならないだろうか?

前に習った繰り返しを使えば楽になりそうな気がするが、変数の名前が違うのでこのままでは無理である。

そこで配列というものを使う。配列とは、複数の変数を同じ名前でひとまとまりにして、番号で区別する仕組みである。

配列の宣言と使い方

配列を使うには、変数と同様に、まず宣言する必要がある。配列宣言の文法は次の通り。

変数型[] 配列名 = new 変数型[要素数];

具体的には、例えば次のように書く。

int[] array = new int[5];

この例は、整数型(int型)の変数を5個まとめた、arreyという名前の配列を宣言している。配列が管理している変数のことを要素と呼ぶ。要素数は要素の個数のことなので、この例の場合は要素数は5となる。

配列の各要素は番号を指定して区別することができる。番号を指定するには配列名の後ろに[]を書き、[]の中に番号を指定する。この[]で指定する要素番号のことを添字(そえじ)と呼ぶこともある。上の例のarrayならば、array[0]、array[1]、array[2]、array[3]、array[4]のようにして指定できる。要素番号は0から始まり、要素数-1までを指定することができる。

配列は変数のアパートと考えるとよい。配列名がアパートの名前で、要素番号は部屋番号、というイメージである。

配列の要素番号を指定すると、通常の変数と同じように扱うことができる。例えば次のように、代入や計算を変数と同様に書くことができる。

int[] array = new int[5]; array[0] = 2; // arrayの0番目の要素の値に2を代入 array[1] = array[0] * 2; // array[0]の値を2倍してarray[1]に代入

配列で宣言しているのに添字を指定せずに使おうとすると文法エラーになったり、予想外の実行結果になったりする。理由は少々ややこしいのでここでは説明を省略する。

配列はこのようにして使うことができるが、この使い方ではあまり意味がない。[]を付けるだけ面倒な気もする。配列が便利なのは、要素番号を変数や計算式で指定できることである。例えば次のような書き方ができる。

int[] array = new int[5]; int i; i = 0; array[i] = 2; array[i + 1] = array[i] * 2;

このプログラムの意味を考えてみよう。

もう少し複雑にしてみよう。繰り返しを使って、例えば次のようなプログラムを書いてみる。

int[] array = new int[5]; array[0] = 2; for (int i = 0; i < 4; i++) { array[i + 1] = array[i] * 2; }

Processing(Java)では変数宣言をfor文の()の中に入れることができる。このようにすると、この変数iはfor文の{}の中だけで有効になる。

このプログラムの意味を考えてみよう。

やってみよう

最初に説明した、3つの円が上下に跳ねるプログラムを配列を使って書き直してみよう。

考え方としては、例えば次のとおり。

円の数を7つに増やしてみよう。

練習問題

マウスカーソルの軌跡を10個の円が残像のように追いかけてくるプログラムを作成しよう。起動直後の表示は厳密に正確でなくてもよい。

ヒント:

関数を作る

プログラムを作成していくと、同じような処理(手続き)を何度も使う必要が出てくる。また、プログラムが大規模になってくると、プログラムのどの部分が何をやっているのか見通しが悪くなってくる。関数は、プログラムを簡潔に、かつ理解しやすく書くための仕組みである。

ProcessingはJava言語をベースにしているので、Java言語にならって呼ぶのならメソッドとなるが、公式リファレンスではfunction(関数)と呼んでいるので、ここでも関数と呼ぶことにする。Processingはオブジェクト指向を意識しなくても扱えるようになっているので、用語としてもfunctionとしているのだろう。

準備:マウスの位置に顔を描く

次のプログラムに追加・修正して、マウスカーソルの位置に顔のような図形を描くプログラムを作成しよう。顔の例は下の図のとおり(この顔でなくても構わない)。

void setup() {
  size(400, 400);
}

void draw() {
  background(255);
  ellipse(mouseX, mouseY, 50, 50);
}

顔を描く処理を関数化する

上の絵程度の単純な図形の組み合わせであればさほどややこしくない(4行程度)が、凝った絵を描こうとすると処理が多くなって、どの処理が何をしようとしているのか分かりにくくなってくる。そこで、ひとかたまりの処理を関数という単位でまとめてみる。

下のプログラムの指定箇所に、先ほど書いた顔を描く処理を入れてみよう。書けたら実行して動作を確認すること。

void setup() { size(400, 400); } void draw() { background(255); drawFace(); } void drawFace() { /* ここに顔を描く処理を書く */ }

上のプログラムの解説

このプログラムのポイントは、drawFace()である。drawFace()と書かれている箇所は、7行目(draw()の{}の中)と、10行目(void drawFace())の2箇所ある。

7行目のdrawFace();は、形式的にはsize(400, 400);やbackground(255);などと似ているが、Processingのリファレンスを探してもdrawFace()という関数は存在しない。つまり、sizeやbackground関数はProcessingが最初から用意しているのに対し、drawFaceは独自に作った関数である。

drawFace関数を作っているのが10行目以下である。関数は次の形式で定義する(作る)。

返値の型 関数名(引数1の型 引数1の名前, 引数2の型 引数2の名前, ...) { // {}の間に関数で行う処理を書く }

なお、返値は「かえりち」、引数は「ひきすう」と読む。

10行目のdrawFace()を当てはめてみよう。

まず、最初の「返値の型」に相当しているのはvoidである。voidとは「何もない」ことを表す型である。この場合、この関数は何も返さない、ということを宣言している(返す例については後述)。

次の「関数名」に相当するのがdrawFaceである。関数名は変数名と同様のルールで自由に名前を付けることができるが、どのような意味の関数か名前だけで分かるようにしておくのがよい。この場合、顔を描くのだからdrawとfaceを組み合わせている。最初は小文字で始め、2つめ以降の単語は先頭を大文字にしておくのが慣例である。また、少々長くても意味の分かりやすい名前を付けておくことが流行である。

()の中の引数は、今回は使っていないので何も指定していない。引数の例は次に説明する。

引数で値を関数に渡す

先ほどのプログラムでは顔を描く位置は変数mouseXとmouseYで決めていた。一方、ellipse関数では関数を呼ぶ時に座標値(と大きさ)を指定している。このように、関数を呼び出す側で指定するように変えてみよう。

関数が値を受け取るには引数を使う。関数を定義する際に、受け取りたい値の型と、受け取るための入れ物になる変数を、必要な個数だけ指定する(上の水色の枠内を参照のこと)。

例えば、x座標とy座標をそれぞれ受け取ることにする。どちらの座標値も整数(int型)としておこう。変数名は分かりやすくxとyにしておく。これを関数の定義に加えると次のようになる。

void drawFace(int x, int y)

変数名は意味が想像しやすい名前をつけるのが原則であるので、一文字だけの変数名は通常は推奨できないが、この場合は間違うこともないだろう。

関数が引数を受け取ることを宣言すると、関数を呼ぶ側はそれに合わせて引数を渡してやらなければならない。ここまでのプログラムの例では、マウスカーソルの位置に顔を描きたいので、mouseXとmouseYの値を引数で渡せばよい。

drawFace(mouseX, mouseY);

Processingでは関数名が同じでも引数の型や個数が異なれば別物として扱われる。

練習:ここまでの変更を反映させてみよう。drawFace関数の中の処理は、mouseXとmouseYの代わりにxとyを使うように書き換えておくこと。

引数で関数に値を渡すか、共通の変数で値を渡すか?

先ほどの例では、引数で関数に値を渡すように修正したが、わざわざ面倒なことをやっているようにも見える。では、なぜ引数を使うのか。違いを整理してみよう。それぞれの方法の特徴を簡単にまとめると次のようになる。

共通の変数で値を渡す

※ C言語などのプログラミング言語ではグローバル変数と呼ばれることもある。ProcessingはJava言語ベースなのでこの名称は適切ではないし、公式の呼び名は無いようであるが、考え方はグローバル変数と共通している。

引数で値を渡す

それぞれの方法のメリット・デメリットを考えてみよう。

どちらの方法を使うべきかはケースバイケースであるが、特に必要が無い限り引数で値を渡す、と覚えておこう。このように関数を設計することで、関数を使い回すことが容易になる、プログラムをテストしやすくなり間違いが入る可能性を下げられる、などの効果が得られる。
例えば次の例題を、先ほど作成したdrawFace関数をコピー&ペーストすることでやってみよう。

次のプログラムはマウスカーソルの軌跡を10個の円が残像のように追いかけてくるプログラムである。このプログラムに先ほど作成したdrawFace関数を追加し、これを使って円の代わりに顔が残像として表示されるように修正せよ。

int[] x = new int[10]; int[] y = new int[10]; void setup() { size(400, 400); } void draw() { background(255); // ウィンドウ内を消す // 最新の残像の位置をx[0], y[0]に入れる x[0] = mouseX; y[0] = mouseY; // 残像を描く for (int i = 0; i < 10; i++) { // 10個分繰り返す ellipse(x[i], y[i], 50, 50); // 円を描く } // 残像の座標をずらしておく for (int i = 9; i > 0; i--) { x[i] = x[i - 1]; y[i] = y[i - 1]; } }

値を返す

ここまでの例のdrawFace関数では返値の型にvoid型を指定することで、何も返さないことを宣言している。void以外の型を指定すると、その型の値を返さなければならなくなる。例えば、次の関数は1から引数で与えられた値までの自然数を合計した値を返す。

int sum(int val) { int sum = 0; // 合計を計算するための変数 for (int i = 1; i <= val; i++) { // 1からvalまで足す sum += i; } return sum; // 計算しおわった値(合計)を返す }

この関数を試してみよう。例えばsetup関数の中で、sum関数に適当な値を引数で指定する。println関数を使うとウィンドウ下半分のコンソールと書かれている領域に文字で表示される。例えばprintln(sum(10));で、1から10までを足した結果が表示される。

関数を終了して値を返すにはreturn文を使う。returnの後ろには、関数の返値で指定した型と同じ型の変数あるいは計算式を書く必要がある。なお、返値にvoid型を指定した場合はreturn;だけでよい(関数を途中で終了することができる)。

関数が返すことができる値は1つだけである。2つ以上の値を返したい場合、そのままで返す方法はないので、共通の変数を使うか、複数の変数をまとめたデータ構造を使う(Javaの場合クラスと呼ぶ)必要がある。

1からnまでの自然数の合計は(1+n)*n/2で計算できる。上のsum関数をこの計算方法に書き換えてみよう。

その他の関数やライブラリ

以下、基本的で、最終課題作品を作るのに便利と思われる関数を紹介する。なお、Processingでは同じ関数でも複数の引数のパタンが用意されていたり、所定の関数を呼ぶことで設定を変更することができる。以下は代表的な(かつ分かりやすい)使い方を掲載しているが、他の引数や設定については公式リファレンスを参照のこと。

図形の色や線の太さを変更する

stroke(gray); 線の色を白黒で設定する
gray: 0〜255の値。0は黒、255は白、中間の値は小さいほど黒い灰色。

stroke(r, g, b); 線の色を赤・緑・青の成分で設定する
r: 赤色成分。0〜255の値。大きいほど赤い。
g: 緑色成分。0〜255の値。大きいほど緑。
b: 青色成分。0〜255の値。大きいほど青い。

noStroke(); 線(図形の縁)を描かないことを指定する

fill(gray); 図形の塗りつぶし色を白黒で設定する
gray: 0〜255の値。0は黒、255は白、中間の値は小さいほど黒い灰色。

fill(r, g, b); 図形の塗りつぶし色を赤・緑・青の成分で設定する
r: 赤色成分。0〜255の値。大きいほど赤い。
g: 緑色成分。0〜255の値。大きいほど緑。
b: 青色成分。0〜255の値。大きいほど青い。

noFill(); 図形の中を描かないことを指定する

strokeWeight(weight); 線の太さを設定する
weight: 線の太さ(ピクセル単位)

図形の表示属性(色や線の太さなどの見栄え)を設定する関数は、図形を描くよりも前で呼ぶ必要がある。また、一度設定すれば別の設定をするまでは有効になる

なお、色の指定方法はcolorMode関数を使って変更することができるが、詳細は省略する。

文字を描く

text(str, x, y); 文字列を描く
str: 文字列。""で囲んだ文字列、String型の変数などで指定する。
x: x座標(初期設定では文字列の左下)
y: y座標(初期設定では文字列の左下)

textSize(size); 描く文字の大きさを指定する
size: 文字の大きさ(ピクセル単位)

textAlign(alignX, alignY); 文字を描く座標の指定方法を設定する
alignX: LEFT, CENTER, RIGHTのいずれか(左寄せ、中央揃え、右寄せ)
alignY: TOP, BOTTOM, CENTER, BASELINEのいずれか(上、下、中央、ベースライン)
alignYは省略可能

文字列の色を指定するにはfill関数を使う(strokeではないことに注意)。

画像ファイルを表示する

loadImage(filename); 画像ファイルを読み込む
filename: 画像ファイル名。形式はGIF、JPG、PNG(とTGA)が読み込み可能。

返値: PImage型のオブジェクト

image(img, x, y); 読み込んだ画像を描く
img: 表示するPImageオブジェクト
x: 表示位置のx座標
y: 表示位置のy座標

image(img, x, y, width, height); 読み込んだ画像を指定した大きさで描く
img: 表示するPImageオブジェクト
x: 表示位置のx座標
y: 表示位置のy座標
width: 描く幅
height: 描く高さ

画像ファイルを読み込んで描くには、まずloadImage関数で読み込むと、読み込んだ画像データを表すPImage型のオブジェクト(データと思えばよい)が得られるので、これを変数に格納しておいて、描きたいときにimage関数で描く。読み込むのは一度だけでよいが、変数の有効範囲に注意。setup関数で最初に読み込んでプログラム全体で使える変数に格納しておけばよい。

PImage img1; // 一つ目の画像 PImage img2; // 二つ目の画像 setup() { size(400, 400); img1 = loadImage("imagefile1.jpg"); img2 = loadImage("imagefile2.png"); } draw() { image(img1, 0, 0); // img1を左上に原寸大で描く image(img2, 100, 100, 160, 120); // img2を(100,100)の位置に幅160、高さ120で描く }

数学関数

他にもたくさんあるが、一部だけ紹介。

sin(angle); サイン
angle: 角度(ラジアン)
返値: サイン値

cos(angle); コサイン
angle: 角度(ラジアン)
返値: コサイン値

tan(angle); タンジェント
angle: 角度(ラジアン)
返値: タンジェント値

sqrt(n); 平方根
n: 元の値
返値: 平方根値。nが負の時はNaN(不正な値であることを表す値)を返す。

random(low, high); 乱数を返す
low: 下限値
high: 上限値
返値: low以上high未満の乱数値、float型。

float angle = 0; // 角度 void setup() { size(400, 400); } void draw() { background(255); // ウィンドウ中央を中心にくるくる回す。y軸が下向きなので時計回りになる。 float x = 200 + 100 * cos(PI * angle / 180.0); // 角度をラジアンに変換 float y = 200 + 100 * sin(PI * angle / 180.0); // PIは円周率の定数 ellipse(x, y, 50, 50); angle += 5.0; // 角度を5度増やす if (angle > 360) { angle -= 360; // 360度を超えたら修正しておく } }

音声を再生する

素の状態のProcessingでは音を扱う機能がないので、ライブラリを追加する必要がある。ここではProcessingの標準であるSoundライブラリを紹介する。音を扱うライブラリは他にもあり、例えばMinimは高度な音の処理を行うことができる(使い方はSoundライブラリとかなり異なるので注意)。

ライブラリを追加するには、まずProcessingのメニューの「スケッチ/ライブラリをインポート…/ライブラリを追加…」を選択する。ライブラリを管理するウィンドウが開くので、Librariesの中からSoundを探して選択し(検索を使うとよい)、右下のInstallボタンをクリックする(インターネットに接続されている必要がある)。

SoundFile(theParent, path); 音声ファイルを読み込みSoundFileオブジェクトを生成する。対応する音声ファイルフォーマットはwav、aif/aiff、mp3。実際の使い方は下記の例を参照。
theParent: このSoundFileオブジェクトの親オブジェクト。よく分からなければthisでよい。
path: 読み込む音声ファイルのファイル名

SoundFile.play(); SoundFileオブジェクトの音声を1回だけ再生する。実際の使い方は下記の例を参照。

SoundFile.loop(); SoundFileオブジェクトの音声を繰り返し再生する。実際の使い方は下記の例を参照。

SoundFile.stop(); SoundFileオブジェクトの音声を止める。実際の使い方は下記の例を参照。

import processing.sound.*; // Soundライブラリを使うことを指定 SoundFile sound; // 音声データを覚えておく変数 setup() { size(400, 400); sound = new SoundFile("soundfile.mp3"); // 以下の3行は使い方を説明しているだけ。このプログラムの通りに書いても再生してすぐに止めているので音はほとんど鳴らない。 sound.play(); // 1回再生する sound.loop(); // 繰り返し再生する sound.stop(); // マウスボタンが押されたら再生を止める } void draw() { }

描画設定

プログラムを実行する際の見栄えに関する設定

frameRate(fps); 1秒間にdraw関数を呼ぶ回数を指定する。ただし、コンピュータの性能によっては指定した値の回数呼ばれるとは限らない。
fps: 1秒間にdraw関数を呼ぶ回数

noLoop(); draw()関数を自動的に呼ぶのを止める。

loop(); draw()関数を自動的に呼ぶ。

noSmooth(); 図形を粗く描く(かわりに描画が速くなる)。setup関数の中など、最初に1回だけ呼ぶことができる。

イベント処理

mousePressed変数の値を参照すればマウスボタンが押されているか押されていないかの状態を知ることができるが、クリックされた時に1回だけ処理を実行したい場合には向いていない。このような場合、マウスがクリックされたら呼ばれる関数mouseClicked()を作るとよい。

例えば、次のプログラムはマウスがクリックされると円の回転をオン・オフする。

float angle = 0; boolean flag = true; // 動いているか止まっているかを管理する変数(フラグ) void setup() { size(400, 400); } void draw() { background(255); float x = 200 + 100 * cos(PI * angle / 180.0); float y = 200 + 100 * sin(PI * angle / 180.0); ellipse(x, y, 50, 50); angle += 5.0; if (angle > 360) { angle -= 360; } } void mouseClicked() { if (flag) { noLoop(); } else { loop(); } flag = !flag; // フラグを反転させる }

mouseClick以外に、次のような関数がProcessingで準備されている。

void mousePressed() マウスボタンが押し下げられたら呼ばれる関数

void mouseReleased() マウスボタンが放されたら呼ばれる関数

void mouseMoved() マウスカーソルが動いたら呼ばれる関数

void mouseDragged() マウスボタンが押されながらマウスカーソルが動いたら呼ばれる関数

void keyTyped() キーボードのキーが押して放されたら呼ばれる関数

void keyPressed() キーボードのキーが押されたら呼ばれる関数

void keyReleased() キーボードのキーが放されたら呼ばれる関数

キーの場合、どのキーが押されたかは変数keyまたはkeyCodeの値を参照する。通常の文字に対応するキーはkeyに、カーソルキーなどの文字ではないキーはkeyCodeに値が入る。詳しくはProcessingのリファレンスのkeyおよびkeyCodeの説明とサンプルプログラムを参照。

自由課題

課題:自由にプログラムを作ってみよう。説明した以外の関数を使ってもよい。