お絵かきプログラミングProcessingサンプルコード解説の第12弾です!
ProcessingをまだPCに導入されていない方は、こちらの記事を参考に導入してみてください!
今回は Button というサンプルコードを見てみましょう。
難易度は ★★★☆☆ くらい。
クリックに反応する部分の処理の流れを追うのは、慣れないと少し難しいかもしれませんが、汎用性が高く便利です。
わからない箇所があればコメントをください。
Table of Contents
もくじ
とりあえず動かしてみよう
Processnigを起動し、サンプルコードを開きましょう。
ファイル → サンプル と選択し現れたウィンドウで Topics → GUI → Button と選択してください。
このようなスケッチが現れます。ウィンドウの左上にある再生ボタンをクリックして実行してみましょう。
クリックすると色を変化させるボタンが現れます。
コードを読む
とりあえずコードの全貌を確認しましょう。
すこし長いですね。歯応えがありそうです。
コメントから始まっていますね。全体の理解のためのヒントがありそうなので、ちゃんと読んでみましょう。
「画像の中央にある色のついた図形をクリックすると、背景の色が変化します。」とのことです。実行時の動作をそのまま説明してくれていますね。
特に引っかかる点はないと思うので先に進みましょう。
次にグローバル変数を定義していますね。
今回のサンプルでは変数を定義する行の一部にコメントを書いてくれていますね。コメントがあるものは参考にさせてもらい、ないものは変数名から想像だけしましょう。
変数 | 意味 |
---|---|
rectX, rectY | 「正方形ボタンの位置」 |
circleX, circleY | 「円形ボタンの位置」 |
rectSize | 「四角形の大きさ」 |
circleSize | 「円形の大きさ」 |
rectColor, circleColor, baseColor | 四角形・円・ベース(?)の色? |
rectHighlight, circleColor, circleHighlight | 四角形と円形のハイライトの色? |
currentColor | 現在の色? |
rectOver | 四角形のオーバー(??) |
circleOver | 円形のオーバー(??) |
コメントを書いてくれている変数については、当然ですが意味も分かりますね。
実行結果を見た後なので、残りの変数のいくつかも、名前から意味の想像がつきます。
…が、それでもわかりにくいものがまだまだありますね。(overがどういう意味での命名かは、イメージがつきにくいですね…)
現時点でわかることはもうなさそうなので、先に進みましょう。まずはsetup()です。
setup
size()はウィンドウサイズを指定する関数です。サンプルコードによく出てくる思うので、もう憶えていらっしゃる方も多いと思います。
次に変数に順に値を代入していますね。
よく見るとこれらの変数には型の記載がないことから宣言済みであることがわかります。つまり、これらは先ほど定義したグローバル変数であることがわかります。
color()は引数1つで呼ばれているので、グレースケールの何かしらの値が入っていることがわかります。rectColorにはcolor(0)=黒、circleColorにはcolor(255)=白、がそれぞれ入っています。
circleX, circleY, rectX, rectYの式にはwidth, heightが使われていますが、これらは自分で定義せずに使用できる特殊な変数で、widthはウィンドウの幅, heightはウィンドウの高さにそれぞれ対応します。便利なので憶えておきましょう。
これでsetup()はおしまいですね。次にdraw()を読みましょう。
draw
初めにupdate()を呼んでいますが、これは独自に定義している関数です。
(ProcessingのIDEでは、processingが用意している関数はこのような紺色で表示されるのですが、update()は黒字なので独自に定義していることがわかります。)
現時点ではupdate()の内容は不明なので、言葉の意味から「何かを更新するんだろう」と思いながら、一旦飛ばします。
background()は毎度おなじみですね。描画ウィンドウ全体を塗りつぶす関数です。今回の引数はcurrentColorですが、「現在の色」という変数名である以上、固定値ではなく変化する値である可能性は高そうです。
setup()でbaseColor (=color(102)=灰色)を入れていたので、一旦、currentColorは灰色と思っておきましょう。
次に分岐がありますね。
rectOverがtrue/falseのいずれであるかによって、fill()で指定する色がrectHighlightなのかrectColorなのかが決定されています。
fill()で指定した色は、その後のrect()で使われます。
stroke()は毎度おなじみですね。図形の縁などの線の色を指定します。今回は引数が255なので白ですね。
次にもう1つ分岐がありますが、ついさきほど見たものと同じ構造になっていますね。
circleOverがtrue/falseのいずれであるかによって、fillで指定する色がcircleHighlightなのかciecleColorなのかが決定されています。
fill()で指定した色は、その後のellipse()で使われます。stroke()の引数は0なので、線の色は黒になります。
これでdraw()が終わってしまいましたね。
まだ不明な点がいくつかあったかと思いますが、一旦先に進みましょう!
update
draw()の中で呼ばれていたupdate()ですが、確かに独自に定義されていますね。
大きな分岐が見えますが、どうやら分岐先は関数overCircle(), overRect()によって決定されているようです。(グローバル変数として定義していたrectOver・circleOverとは別物ですね。ややこしいです。)
これらの関数は両方とも黒字です。つまり自分で定義しているものであり、まだ何をする関数なのかわかりません。
現時点でわかるのは、update()はcircleOver・rectOverの値を更新する関数であるということです。
63行目の記法は見慣れないかもしれませんが、circleOver・rectOverの両方にfalseを代入しています。
ところで、update()の引数x, yは使われていません。
このためupdate()はそもそも引数を取らない関数として定義できます(筆者はそちらの方が適切と思います)。その場合はdraw()内でupdate()を呼ぶ時に渡しているmouseX, mouseYを渡さないようにすればOKです。興味のある方は試してみてください。修正前と同じ動作になるはずです。)
update()もまだ不明点が多いですが、一旦次にいきましょう。
mousePressed
IDEでmousePressed()の文字色を見てみてください。紺色になっていますね。これはProcessingが用意している関数を表す色です。
mousePressed()で定義した処理は、マウスボタンが押されるたびに一度実行されます。
分岐をしていますが、やっていることはcurrentColorへの代入のようです。
circleOverがtrueならcurrentColorを、rectOverがtrueならrectColorを代入します。
どちらでもない場合は何も行いません。
currentColorは「灰色」を代入された後の動きが不明でしたが、circleOver, rectOverによって決定されるようですね。
最後にoverRect(), overCircle()をみましょう。
overRect, overCircle
overRect()から見ます。
返り値はbooleanと定義されているのでtrue/falseのいずれかが返されます。
分岐がありますが、条件式が複雑ですね。
また、ここのwidthの文字色はIDE上ではピンクで表示されていて、ウィンドウサイズを表す特殊な変数であるように見えますが、実際は引数のwidthです。
mouseX, mouseYは引数などで定義されたものではないので、マウスの位置のx座標、y座標を表す特殊な変数です。
問題の条件式ですが、どういう時にtrue/falseになるのか、図を描いて考えてみましょう。
図中の灰色の長方形はプログラム実行時に表示される描画ウィンドウを表しています。
座標軸を描いていますが、Processingではxy座標系の原点はウィンドウの左上角の点になり、ここから右方向にx軸、下方向にy軸が伸びます。
変数mouseX, mouseYで取得できる値は、マウスカーソルの、この座標上での位置になります(単位はピクセルです)。
条件式は4つの不等式が&&で結合された形になっていますが、よく見ると前半2つはx座標の条件、後半2つはy座標の条件になっていることがわかります。まずは前半2つから見てみましょう。
下図で薄いオレンジで塗った領域が、前半部分がtrueになる領域になります(不等式と見比べながら考えてみてください)。
後半部分がtrueになる領域は、下図で薄い水色で塗った領域になります。
以上から、前後半の結果を合わせると、条件式全体の結果がtrueになるのは、最後の図の緑色の領域になります。
すなわち、
マウスカーソルが指定した長方形に重なっていればtrue、そうでなければfalseになります。
この関数の名前はoverRect()でしたが、この結果からオーバーという言葉は、ものが重なっている状態を表していたことがわかります。(そういえば、PCでマウスを物体に重ねることをマウスオーバーといいますね。)
overCircle()も見てみましょう。(overRect()の結果から、overCircle()はマウスカーソルと円との位置関係に関わる関数と想像されますね。)
分岐がありますが、条件式は、数学の授業で見覚えがある式かもしれません。
こちらも図で考えましょう。
マウスカーソルの座標はやはり(mouseX, mouseY)です。いま引数のx, yを成分にもつ点(x, y)を考えます。
disX, disYが定義されていますが、これらは2点(mouseX, mouseY), (x, y)のx座標の差とy座標の差にあたります。(変数名のdisはdistance(距離)の先頭部分と思われます)
そこで、2点間の距離を三平方の定理で計算してみると、結果はさっきの条件式の左辺に一致します。
(disX, disYは負の値になる場合もあるので、図中では絶対値記号をつけています)
では条件式の右辺はなんでしょう?
diameterは(円の)直径という意味なので、diameter/2は半径の値に一致します。
これらからoverCircle()は、中心が(x, y)で直径がdiameterである円を考えた場合に、マウスカーソルが円に重なっていればtrue、そうでなければfalseを返却することがわかります。
図で表すと以下のようになります(図の場合はtrueを返します)。
overRect()、overCircle()がわかったところで、これらを呼んでいたupdate()に戻りましょう。
今見ると、マウスカーソルが円や長方形に重なっているかどうかでrectOver・circleOverの値をtrue/falseで更新している、ということがわかりますね。
update()はdraw()内で呼ばれているので、コードの実行中はupdate()が短い間隔で実行され続け、rectOver・circleOverの値はリアルタイムに更新され続けることになります。
おさらい
最後のまとめとして、draw()を見直して、Buttonの処理の流れをおさらいしましょう。
update()でマウスカーソルが円・長方形に重なっているかどうかを表す変数(rectOver, circleOver)を更新し、それらの値に応じて、円・長方形の塗りの色をfill()で更新しています。
またdraw()内では、background(currentColor)が実行されているため背景全体が何度も塗りなおされていますが、円/長方形のいずれかをマウスでクリックするとcurrentColorが更新され、背景が別の色で塗りなおされる。
という流れになっていたことがわかります。
これがButtonの全貌になります。
長かったですが、お疲れ様でした!
おまけ アレンジ
Buttonのサンプルコードでは、
1. ある領域を塗りつぶし、
2. その領域内でマウスをクリックした場合に特定の処理を行う
という流れで、「ボタン」を実現していましたが、見た目や挙動をすこし変えてみましょう。
ある領域を塗りつぶさず、その領域内でマウスボタンを押した場合に背景色を塗り替える「ボタン」を作成してください。
つまり次のような動作をさせてください。(クリックした瞬間に背景の色が変わっています)
いずれもButtonのサンプルコードを少し修正することで作成できます。興味があればチャレンジしてみてください。
解答例は、こちらにおいておきます。
問題1の解答例
長方形・円を描画する行だけ消すと、期待した動作になります。
問題2の解答例
mousePressed()内で行っていた処理をupdate()内で行うようにすると、期待した動作になります。
さいごに
今回は、ボタン(GUI)の作り方を学びました。
draw()以外に処理の起点があるパターンは理解が難し買ったかと思いますが、
GUIなどインタラクティブな作品を作るには必須の知識になります!
慣れて使いこなしましょう!
ではまた次回!