processing 演習 Muller H.
吉野山 こぞのしおりの道かへて まだみぬかたの 花をたずねん (西行)2008.6.28 汎用IOボードgainerについて 2008.6.28 カメラの扱いを追加 2005.6.13 ver1.3 ビデオ、シリアルポート接続の解説を追加 2005.5.13 ver1.2 2005.5.11 ver1.1 2005.5.10 ver1.0
-目次のようなもの- プログラミング言語早分かり小史・Processing ■■演習の部■■ ■第一部 基本命令 変数 print()とprintln() int と float random() 制御構造 for ループ if文 文字列のやや高度な扱い 配列 ■第2部 静止画像の読み込み 変数の有効範囲 ビデオカメラからの入力 関数 標準関数abs()の使い方 ■質問に答えて 波紋のように点が広がっていくアニメーション 回転しつつ透明度の変わる画像 シリアルポート接続 アニメーション アルファ版かベータ版か?
プログラミング言語早分かり小史・Processing
・C言語(コンパクトシンプルな言語) → C++言語(オブジェクト指向) C言語の発展形。・JAVA言語(Sun) JAVAはMacでもWinでもLinux,Sunでも動く言語として はなばなしく登場。現在、様々な分野で使われている。 反面、仕様が巨大化・複雑化。 →Processing(MIT, BenFry et.al) メディアアートを意識し,JAVAを簡略化。 2005年4月20日ベータ版リリース ・processing 公式サイト ・ 日本のサイト Q.どうしてコンピュータ言語ってたくさんあるのですか?ひとつあれば十分なのに。 A.それぞれの言語は作った人も歴史も特徴も違います。 人間の言語も英語や中国語、アラビア語、ウルドゥ語、ケルト語など様々あるのと同じです。 -------------------------------- processingはJAVA言語をベースにして 主にアートの人たちにも簡単に扱えるように、と MITで作られた言語です。 ---------------------------------
■□■□■□ 演習 by mh. ■□■□■□ 使用上の注意 ・問題のレベルの目安をつけてみました。 + 簡単 ++ ちょっと考える +++ かなり考える ++++ 高度 ・速攻で作ったので、十分練られていない部分があるかもしれません。 ・主なトピックスの頭には■記号がついています。 トピックスにもレベルの目安を設定してみました。 +の数が多いものほど初心者には 高度またはそれほど必要ではないことを表します。 例 「■++文字列の取り扱い」 ++がついているトピックスはやや瑣末な話題なので 初めて学ぶ際には飛ばしてもいいでしょう。 ・processing には、プログラマのレベルに応じて 3つのモードを設けています。 まずは第1部で初心者むけのbasicモードから じっくり学んでみることにします。 basic modeではインタラクティブ性はありませんが、 高度な機能がない分、基礎を学ぶのに適しています。 第2部でインタラクティブ性を含んだプログラムなどを扱います。 全体的に第2部はやや〜けっこう高度な内容になっています。
---第1部---
■基本命令
line(),rect(),size() etc...- 例 line(0,0,100,100); を入力し、実行。 -------------------- 線を1本引くだけのシンプルなプログラムです。 他のプログラム言語では、線を引くにも、 ウインドウの大きさを指定したり、いろいろ宣言が 必要だったりと、意外に大変です。 同じことをするのに、JAVAやC言語だと10行ぐらいは ゆうにかかりそうです。たった1行で済ませられるところが processingのすばらしい点です。 --------------------
- 問+ 縦30,横50の長方形を描く。 *rect()を使います。1行でかけます。 rectを使うには4つの数字の組が必要になります。 この数字の組がわからないときは、 help → referenceで該当する項目を 調べます。このように、簡略なマニュアルが セットになっていて簡単に調べることができる ところもprocessingの使いやすい点の一つです。
- 問+ 一辺が50の正方形を描く *rect()を使う。
- 問++ 正方形の中心が画面の中心に重なるように描く。 *rectに入れる4つの数字は、順に、左上のx,y座標、横、縦 です。ほんのちょっとしたひと手間が必要。
- 問+ 円を描く。 *ellipse()を使います。円なので、中心と半径を 指定して描きたいところですが、Java系の言語では すこし考え方が違います。 rect()の指定を思い出してください。 ellipse()の4つの数字はrectで長方形を描くのと同じです。 その長方形にちょうど 内接する円(または楕円)を描きます。
- 問++ 円の中心が画面の中央になる円を描く。 *すこし算数(小学生レベル)が必要です。 次に背景の色を変えてみます。 背景色を変更するには background(グレイスケール); または background(R,G,B); を使います。 グレイスケールは0〜255の数字 R,G,Bもそれぞれ0〜255の数字を指定します。 256段階に変化するボリュームのように 考えるといいでしょう。
- 問+ 背景を白に指定する
- 問+ 背景を赤に指定する 次に図形を塗りつぶしてみます。 fill()を使います。 fillの色の指定はbackground()と同様です。 fill(グレイスケール); または fill(R,G,B); を使います。 グレイスケールは0〜255の数字 R,G,Bもそれぞれ0〜255の数字を指定します。 256段階に変化するボリュームのように 考えるといいでしょう。
- 問+ 長方形を灰色に塗りつぶす。 *灰色なので、数字は1つで十分でしょう。
- 問+ 長方形を赤く塗りつぶす。 *RGBモードです。
- 問++ 背景色を赤にして、緑色に塗りつぶした長方形を 描いてください。 プログラミングでは命令を書く順番が重要です。 基本的に上から順に命令を実行していきます。 fill()を指定する場合、 プログラムのどの場所で指定するかが意味を 持ちます。fill()を指定した場所より下が 影響範囲です。 すこし考えてみればわかりますが、 図形を描画する前に指定しないとダメですよね。 描いた後で色を指定されてもプログラムも困るでしょう。
- 問++ 緑の長方形と赤の長方形を1つの画面に描いてください。 * 緑色指定 長方形 赤色指定 長方形(位置をずらす) という順番です。
- 問+ 画面の大きさを変える。 * size(横のピクセル数,縦のピクセル数) 以上で、直線を引く、正方形、長方形、円、楕円を描く命令、 また図形を塗りつぶすこと、塗りつぶす色を変えること、 背景色を変えることを学びました。 また画面の大きさを変えることも学びました。 それぞれの命令をもう一度確認しましょう。
■変数
「変数」は数字をひとつ収納する箱のようなものです。 よく使うタイプに 整数型のint 小数型のfloat があります。 int は整数しか扱うことができませんが、*メモリを 消費せず、処理も速くなります。 一方、floatは2.71といった小数を含む数字を 扱うことができますが、メモリの消費が2〜4倍になり、 処理も整数に比べて遅くなります。 目的に応じて、intとfloatを使い分けます。 はじめのうちはまずint型に習熟しましょう。 変数のルール 変数の扱いに関する主なルールをまとめておきます。 ・ルール1)変数はつかう前に宣言する。- 例: 変数を使って正方形を描く 一辺の長さをaとします。 int a; //宣言 a=30; //30を代入 rect(0,0,a,a); 上の例のa=30;を一箇所変えるだけで、他を 変更することなく、いろいろな 大きさの正方形に変更できます。 変数を使うと便利な点のひとつは、 プログラムの変更が簡単になり、 読みやすいプログラムになるということです。 この例では rect(0,0,30,30); としたほうが簡単に思えますが、プログラムが複雑に なるにつれ、変数は威力を発揮します。 ・ルール2)変数に使える文字はアルファベット、数字、一部の記号( _ と $ )。 先頭はかならずアルファベット(か一部の記号)。 日本語や特殊な記号は使えない。 intや*,+などの予約されている語は使えない。 変数の例 a a1 x x_p position windowSize 変数に使えない例 あ ←英数字ではない 1a ←先頭がアルファベットではない loop ←予約されている言葉なので好ましくない x-a ←マイナス記号は引き算で予約されている ・ルール3)大文字と小文字は区別される。 aとA windowとWindow はそれぞれ別の変数です。 小文字を中心に使うことをお勧めします。 ・補足ルール 変数名はa,bなど一文字だと短かくて打ち込むのに楽なので 簡単なプログラムだと短い変数名が好まれます。 でも複雑なプログラムになってくると、 aやbばかりだと何を表している 変数なのかわからなくなってきます。 そこで複雑なプログラムだと ある程度実体のわかる変数名が好まれます。 windowSize, window_size lineLength, tate,yoko,lineNagasa など。変数名には日本語文字は使えませんが、 英語が苦手な人はローマ字で表記してもいいでしょう。 あるいは、この際、英語も勉強するつもりになるのも いいと思います。 辞書を引きつつ、英語の変数名になれると ユニバーサル(普遍性のある)なプログラムに近づきます。 int型の変数にはi,j,k,n,mなどが常用されます。 int(integer,整数)の頭文字のiを採用し、アルファベット順に 選んでいるのでしょう。l(小文字のエル)は数字の 1と紛らわしいので避けられる傾向があります。 座標の値を入れる変数にはx,y,z などの文字が慣用として好まれます。
■コメント
上の例でつかった、//の記号はコメント記号です。 //より後ろの行末までをコメントにします。 コメントとは、人間がプログラムを読みやすくする ために注意書きを入れたり、 一時的に命令をプログラムからはずす時に使います。 コンピュータが命令を実行するときには、 コンピュータはコメントを読み飛ばします。 コメントにするにはもうひとつの方法があります。 /*と*/ ではさむやり方です。 //は1行だけのコメントで軽快に使えます。 /*と*/ではさむやり方は一度にたくさんの行を 一気にコメントしたいときに便利です。■print()とprintln()
変数に収納されている値などを知りたいときが あります。手軽に表示させる方法に print()とprintln()があります。 プログラムを作っている途中は特に、 テストをしたり、間違いを発見するために、 よく使います。地味ですが、しっかりマスターしてください。 まずは基本の例- 例 helloと表示する。 print("hello"); 文字をそのまま表示するには"(ダブルクオーテーション)で はさみます。" "で囲まれた文字のグループを文字列と 呼びます。
- 例 変数aの値を表示する。 int a; a=7; print(a); 変数の値を表示するには、そのままprint文に渡します。
- 問 print(a); と print("a"); の違いを説明しなさい。 ------------------------------ println()はprint()とちがって 行末に改行がつきます。 print()の場合は改行はつきません。 --------------------------------
- 例 int a; a=7; print(a); print(a); *7が2回表示されるが改行がないので 77 と表示される
- 例 int a; a=7; println(a); println(a); *改行が毎回つくので 7 7 と表示される ------------------------------
- 例 xとyの値を表示する うまくない例 int x,y; x=3; y=5; print(x); print(y); としても結果は 35 となって見にくいですね。
- まずまずうまい例 int x,y; x=3; y=5; println(x); println(y); println()を使っているので2行に分かれて表示されます。
- うまい例(おすすめ) int x,y; x=3; y=5; print(x+" "+y); +は通常は足し算を表します。 a,bが数字を収納する変数だと、 a+b という式はaとbの変数の値を 普通に足し算します。 一方、文字列に対しては + 記号は文字の連結に使います。 真ん中の" "は空白(スペース)を出力します。"と"の間に スペースがあることに注意してください。
- 問 x,yはint型として宣言しておきます。 x,yの値に適当な数字を入れたとして、 print(x+y); と print("x"+"y"); の違いを実際にプログラムして説明しなさい。
- 問 int x,y; x=3; y=5; ではじまるプログラムがあるとします。 あと1行付け加えて表示結果が x=3 y=5 となるプログラムをprint文1つで書きなさい。 ただし、print文の中では3や5という数字は「知らない」 こととします。 print("x=3 y=5");ではダメです。 * "x="とすると文字列としてそのまま出力されます。 連結は+記号を使います。 print文の基礎はだいだい以上です。 これをマスターするとプログラム作りが 楽になるでしょう。
■変数のコピーと交換
変数どおしで値を受け渡したり、交換したりできます。- 例 aの値をbにコピーする(1)。 int a,b; a=3; b=a; println("a=" + a + "b=" +b); *aの値がbにコピーされます。
- 例 aの値をbにコピーする(2)。 ------ int a,b; a=3; b=5; println("a=" + a + "b=" +b); b=a; println("copy a to b"); println("a=" + a + "b=" +b); ------ *aの値がbにコピーされます。 bの以前の値は破壊されます。
- 問+ 変数の値の交換 a=3; b=5; として aの値とbの値を交換するプログラムを考えてください。 交換前と交換後の数字がわかるように表示してください。 *単純にコピーしようとすると、データが破壊されますね。 一時的に保存する作業用の変数をもうひとつ用意します。
■int と float
int型変数は整数 float型変数は小数 を扱えます。 floatのほうが「大きい箱」のようなものです。 intのデータをfloatに収納することはできます。 逆に floatのデータをintに収納することはできません。- 問+ 次の例はエラーになります。 エラーメッセージも見てなぜか考えてください。 int a; a = 3.1415; print(a);
- 問+ 上の変数宣言をfloatに変えて、 正しく表示されることを確かめてください。
- 問+ 次の例を実行し、float型からint型へは コピーできずにエラーになること確かめてください。 int a; float x; x = 3; a = x; print(a);
- 問+ 次の例を実行し、int型からfloat型へは コピーできることを確かめてください。 int a; float x; a = 3; x = a; print(x);
■四則計算
足し算 引き算 掛け算 割り算 はぞれぞれ + - * / です。これらはoperator(オペレータ、演算子)といいます。 使うときにint型とfloat型の違いに注意が必要です。- 例 aとbの値を足してcに代入。結果を表示。 int a,b,c; a=5; b=3; c=a+b; println(c);
- 問+ 上の例をもとに、引き算、掛け算、割り算を 試してみてください。 -- 5割る3を実行すると、答えは1.66..という小数ではなく 1になりましたね。intどうしの割り算は 切り捨てになることに注意してください。 端数まで計算するためにはfloat型にする必要があります。 --
- 問+ 上の四則計算をint ではなくfloatで実行してみてください。 operatorには優先順位があります。 足し算よりも掛け算が優先されることを小学校の 算数で習ったと思います。 2+3*2 は掛け算優先なので、 2+6 となり、答えは8ですね。 ()をつかうと優先順位を変更することができます。 (2+3)*2 とすると答えは10になります。
- 問+ 次のプログラムを実行し、結果がなぜそうなるのか説明しなさい。 int a; a=1+2*3; println(a); a=(1+2)*3; println(a); ここまでで変数にはint,floatがあること、また、 変数のコピーや交換など基本的な操作を学びました。 さらに、+,-,*,/の四則計算を行いました。 割り算の場合、intどうしだと切り捨てが起こることを学びました。
■random()の使い方
さいころ1つ振ると1から6までのどれかの数が得られます。 得られた結果をくじ引きのように使ったりできます。 このさいころの目のような役割を果たしてくれるのが乱数です。 乱数を使いこなせるようになるといろいろな応用が可能です。- 例 0〜10までの乱数を表示する。 print(random(10)); *この例では描画用のウインドウは利用していません。 スケッチの下側にある黒いウインドウ領域に 数字が表示されます。再生ボタンを何度か 押して、乱数が表示される様子を観察してください。 毎回違う数が表示されると思います。 窓枠の大きさは、エディット領域との 境目のバーを、アイコンが変化した状態の時に ドラッグすると変更できます。 random()は 上の例のように、 random(乱数の最大の数) と使います。 最小の数と最大の数を指定することもできます。 以下の例で示します。
- 例 9〜10までの乱数を表示する。 print(random(9,10)); 結果は9.1059や9.8725のように 9〜10の間の小数が表示されます。 負の数も指定できます。
- 例 random()の結果をいったん変数に収納してから 表示する。 間違った例 int r; r=random(10); print(r); これはエラーになります。 どうしてでしょうか? エラーメッセージを見ると The type of the right sub-expression,"float" is not assignable to the variable, of type "int" と出ています。 変数には整数型と小数型があったことを 思い出してください。 random()の結果はこれまで試したように 結果は小数を含むものでした。 付属のreferenceでrandom()を見ると return float とあります。 これは、random()の結果はfloat型で返されるという意味です。 それを整数型の変数に無理やり収納しようとしたので エラーになったのです。 そこで変数の型をfloat型に宣言しなおします。 正しい例 float r; r=random(10); print(r); さて、実際に乱数を使う場合、 0〜10までの整数の乱数がほしい、という 場合がしばしばあります。 random()はfloat型(小数を含む数)の 乱数を返してくるので、そのままでは使えません。 これを解決する方法ももちろん用意されています。 型変換を行う手続き int(xxx); float型の数xxxを整数型に変換します。端数は 四捨五入ではなく、切り捨てられます。 float(zzz)int型の数zzzをfloat型に変換します。 ただ、このfloatをintに直す手続きは自動で行われるので、 int()ほど使用頻度はあまり高くないでしょう。 これらの型変換のことをcast(キャスト)といいます。 もう少し勉強が進むと、int,float以外にもいくつかの型が あることを学ぶでしょう。
- 例 型変換を用いて、1〜10までの整数の乱数を 表示する。 int a; a=int(random(9))+1; print(a); +1としているのは、乱数は0からはじまるためです。 a=int(random(1,10))でもいいでしょう。 この例では、実際にやってみるとわかりますが、 10が表示されることはないでしょう。 10までの乱数、といったときに、9.999.. などは含まれますが、10を含んではいないようです。 10も含めたい場合は、トリックを使ってみます。 int a; a=int(random(9.99999))+1; print(a); もちろん、random(最大の数)ですから、 a=int(random(9.99999))+1; の部分は a=int(random(10))+1; としてもいいことになりますね。 後の書き方だと、10が最大となるランダム数をいったん int()で端数を切り捨てて整数にし、その結果に1を加えています。 random(10)がぴったし10にならない限りはOKです。 もしもぴったし10が出力されることがあれば、aの値は 11となる事態が発生します。これでは、 10までの乱数という設計と違ってしまい、困ります。 referenceの記述を見ると、最大数を含むのか 含まないのかがあいまいになっているので、 9.99などとしている前者のほうが安全なプログラムになります。 いろいろ細かく議論しましたが、 a=int(random(1,11)); または a=int(random(1,10.999)); または a=int(random(10))+1; というあたりがまあまあ実用的な 案でしょうか。
- 問+ 1から6までの整数の乱数を出力する プログラムを作ってください。 *答えは1つではありません。 6も表示されるかたしかめましょう。 うっかりつくると、6が表示されない乱数になります。 何度か実行を繰り返して、1から6までの数字が まんべんなく出てくるか確かめてみてください。 1や6が出ない場合はどこかに間違いがあります。
- 問+ さいころを2個投げたときは1から6までの 2つの数字が得られます。これをまねて、 1行に2つの1から6の乱数整数を表示する プログラムを作ってください。 * randomを2回使います。 以上で乱数の作り方、float型とint型の違い、 型変換の方法を学びました。
制御構造 プログラムを作る際には、 「ある命令を100回繰り返してください」、 とか、「ある変数の値がxxx ならAの命令を 実行して、そうでなければBの命令を実行して ください」、という考え方が必要かつ重要になります。 このような仕組みのことを制御構造と一般にいいます。 制御構造の中でも特に重要なのが 繰り返しと条件分岐です。 条件分岐は、 「Pという条件が正しいなら、Aという命令を実行しなさい」 といった形をとります。 if 文がその代表形式です。 繰り返しは、「Aという命令を10回繰り返しなさい」 といった形をとります。 for文(forループともいいます)がその代表形式です。 順に簡単な例と問題を通して学んでいきましょう。
■for ループ
まずは 繰り返しの命令 for文 からはじめましょう。- 例 int i; for(i=0;i<5;i++){ 命令A; } 命令Aを5回繰り返す構文です。
- 問 上のfor文の例にならって hello と5回表示するプログラムを作ってください。
- 問 上の問の結果を書き換え、10回表示するプログラムを 作ってください。 int を宣言する場所をループの入り口にして for(int i=0;i<5;i++){ 命令A; } と書くこともしばしばあります。宣言文を int i; for(i=0;i<5;i++){ ... とやるか、 for(int i=0;i<5;i++){ とやるかは(細かい説明になるので)好みの問題としておきましょう。 さて、これでみなさんは機械的作業から人間が解放される瞬間を 味わったことになります。 「繰り返すこと」は人間にとっては退屈で苦痛な仕事ですが、 コンピュータにとっては得意な仕事です。 そのコンピュータの持つ絶大な力を発揮する仕掛けが for文です。大いに利用したいパワフルな仕掛けです。 ここまではまだ、単純な繰り返しでした。 for文を使いこなすと、少しずつ条件を変えながら 繰り返す、といった使い方ができます。 そのようにfor文をより強力に使いこなすためには なぜこれで繰り返し命令になるか、その仕組みの 理解が欠かせません。 以下ではそれを学びます。
■for文のしくみ- 問 プログラム int i; for(i=0;i<5;i++){ println("i=" + i); } を実行し、iの値がどう変化するか観察してください。 少しおさらいしましょう。 println("i = " + i); は文字列 "i = " をそのまま表示します。+記号があるので続けて 変数iの値を後ろに結合して表示する命令です。 print()ではなくprintln()を使用しているので 改行がつきます。 結果は i = 0 i = 1 i = 2 i = 3 i = 4 となったと思います。 for文の形をよく観察すると for(A ; B ; C){ 命令; } となっていますね。 A,B,Cの間は 「:」 (コロン。上下とも . )ではなく 「;」 (セミコロン。上は. 下が,)です。 このA,B,Cの3つの部品がfor文の要(かなめ)です。 Aはまずループに入る前の初期設定です。 この例では、i=0;ですから iの値に0がはじめセットされました。 次にBはループを続けるかどうかの判定条件です。 i<5 というのはiの値が5よりも小さいかどうかを 判定しています。もしもiの値が5よりも小さければ テストは合格です。ループの本体の命令を実行します。 逆にiの値が5よりも小さくなければテストは 不合格です。ループは終了します。 ここで、「5よりも小さくなければ」とまわりくどい言い方を している点が気になったと思います。 「5よりも大きければ」と素直に言えばいいじゃないか、と 思ったかもしれません。 実際は「5よりも小さくなければ」と 「5よりも大きければ」は微妙に違います。 ちょうど5に等しいときの違いがあります。 「iが5よりも大きければ」というと、iがちょうど5の時は 5より大きくはないので、該当しません。 「iが5よりも小さくなければ」というと、 iがちょうど5の時は等しいけれども、小さくはないので 該当します。 数学の記号で書くと 「iが5よりも大きければ」は i>5 「iが5よりも小さくなければ」は i>=5 というわけです。 さて、整理するとi<5 という条件は iが0,1,2,3,4 の時は正しく、 iが5以上だと正しくなくなります。 そういうわけで、iが5以上の数になると ループの判定テストに不合格となり、 ループは終了します。 for(A;B;C) の3つの要の部品の最後はCです。 Cは{}の中にあるループ本体の命令が 1度実行されるごとに実行される後処理の命令です。 ここにはi++ と書かれています。 この記号は一般的によく使われる命令で iの値をひとつ増やせ という命令です。 i = i+1; という命令と意味は同等です。 i++のほうが記述が簡潔で、(コンピュータの 機械的な構造を考えると)処理も速くなると期待できるので、 この記法を勧めます。 ただし、iが整数型であることが前提です。 int a=0; a++; は正しいプログラムで実行の結果、aの値は 1になりますが、 float x=0.0; x++; はエラーになります。 以上の説明を経た後で、改めて 問いの例を頭の中でシミュレーションして みましょう。 (蛇足:シミュレーションはsimulationのカタカナ表記ですね。 アクセントはaの位置にあります。動詞形は simulateでアクセントはiの位置にあります。 ×「シュミレーション」と誤記する人を時折見かけます。 これだと「趣味レーション」で、間違えると恥ずかしいので 気をつけましょう。) for(i=0;i<5;i++){ println("i=" + i); } ですから、 まず初期設定i=0で iを0にセットします。 次にBの判定条件は i<5 ですが、iの値は今は0なので テストはパスです。 結果、ループ本体に進むことができて、 i = 0 と表示します。 ループ本体を実行したので、後処理Cを行います。 後処理Cはi++です。iの値を1つ進めるということなので iの値は0からひとつ進めて1になります。 これで第1回のループが終了しました。 次の2回目のループ作業に入ります。 Aのi=0は初期設定なので、2回目以降は無視されます。実行 されません。いきなり判定条件Bです。 今はiの値は1になっています。 i<5 は依然として正しいので、2度目のループも実行されます。 i = 1 と表示されます。 後処理Cはi++なので iの値はまたひとつ進んで、2になります。 これで2回目のループが終了しました。 続いて3回目のループです。 これもいきなり判定、判定はパス、ループ実行、 後処理でiの値は3になって終了です。 以下同様に4回目はiの値は4になって終了します。 5回目はiの値は4で始まるので 同様に判定はパス、iの値は5になって終了します。 さて、ここまでで5回命令は実行されました。 6回目はiの値は5で始まります。 i<5で判定を行うと、 今度はiの値は5ですから、これは不合格です。 5<5という不等式は成立しませんね。 そういうわけで、第6回目のループは命令は実行 されないまま、終了します。 これでfor文の仕事は終わりました。 結果が i = 0 i = 1 i = 2 i = 3 i = 4 となる理由がよく理解できましたか? 文章で書くと長いのですが、理解してしまえば 簡単です。重要な箇所なので、 よくわからなかった人もわかるまで考えてみてください。
- 問+ int i; for(i=5; i>0; i--){ println("i=" + i); } を実行し、結果がなぜそうなるのかを 説明してください。 なお、 i--; はiの値をひとつ減らす、という命令です。
- 問+ 正方形を10個描くつもりで以下のプログラムを書いた。 int i; int a; a = 20; for(i=0;i<10;i++){ rect(0,0,a,a); } 実際に実行してみると 正方形は1つしか描けていない。 なぜだろうか?
- 問++ 上の問を参考にして 以下の指示にそったプログラムを考えてください。 ・正方形の一辺の長さをa(int 型)とします。aの値は20とします。 ・正方形を10個,for文で描きます。for(i=0;i<10;i++)を使います。 ・10個の正方形の左上の座標を(x,y)とします。x,yはint型の変数です。 ・x,yの値がiの値とつねに一致するようにします。 ヒント ・正方形を描く部分はrect(x,y,a,a);という文を使えばいいでしょう。 ・xとiの値を一致させるにはiの値を代入すればいいので x=i; とします。
- 問+ 上の問をすこしだけ変えて x,yの値がつねにiの2倍になるようにしてください。 たとえば、iが2のときは xは4,yも4です。
- 問+ 上の問をすこしだけ変えて x,yの値がつねにiの2倍になるようにしたまま、 aの値がつねにiの値の5倍になるようにしてください。 だんだん大きくなる正方形が描かれるはずです。
- 問+ for文をつかって 横の長さ99,縦の長さ10,の長方形を 10段縦に並べるプログラムを描いてください。
- 問+ 上の例をすこし変えて、 10段の長方形の色にグラデーションをつけてください。
- 問+ 上の例をすこし変えて 長方形のアウトラインを消してください。 ヒント noStroke();を使います。 ループの外に書くのがいいか、内に書くのがいいかを考えてください。
- 問+ 上の例をすこし変えて モノトーンのグラデーションの画面を作ってください。 ヒント 長方形のサイズを縦1か2にします。 色はモノクロの場合256階調ありますが、とりあえず 0から200ぐらいで変化させてグラデーションに 使えばいいでしょう。
- 問++ forループとline()を使い、 ノート罫線のように画面全体に横の罫線を 10本引いてみてください。
- 問++ forループとline()を使い、 10*10の方眼模様を作ってみてください。 *2重にする必要はなく、forループを2度つかうといいでしょう。 初めての場合は、紙に始点と終点の座標を書いて考えてみると 分かりやすいでしょう。 for 文は2重にすることができます。
- 例 2重ループの例 for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ println(i+" "+j); } } -- *iの値とjの値の変化をよく観察してください。 「内側のjのループを3回」という命令を、3回繰り返します。 内側のjのループが1周すると、iの値がひとつ増えます。 内側のjが速くまわり、外側のiがゆっくり動きます。 時計の長針と短針の関係のようなイメージでしょうか。 2重にとどまらず、3重4重、といくらでも多重にできますが、 初級レベルでは2重まで使いこなせれば十分でしょう。
■+if文
if文の簡単な例から。 はじめはトリビアル(ささいな、あたりまえ) な例が多いので、退屈かもしれませんが、 これらを組み合わせることでプログラムが出来上がって いくので、注意深く考えつつ進んでください。- 例 aの値が1ならばHeyと表示する。 int a; a =1; if(a==1){ println("Hey!"); } あたりまえすぎる例でしたね。
- 問+ 上の例でaの値を1以外にして実行すると、helloと表示されない ことを確かめてください。 ==の記号は等しいかどうかを判定する記号です。 =が1つの場合は代入のための記号でしたね。 間違えやすいので注意してください。 上の例のように条件判定のif文は if( 条件 ) { 条件が満たされたときに実行する命令; } という形をとります。 { }の内側には命令をいくつ書いてもかまいません。
- 問+ 上の例に1行付け加えて、 aが1なら Hey! Hello と表示され、 1以外のときは Hello とだけ表示されるプログラムにしてください。
if...elseの構造 if(条件){ 命令A; 命令B; ... } else{ 命令P; 命令Q; ... } という形をとります。 これは ifの条件が正しいときは 命令A,B,...を実行し、 そうでないときは 命令P,Q,...を実行する、 というプログラムになります。 else =「そうではなくて」と読み替えれば自然に 読めますね。- 問+ (重要) if(条件){ 命令A; } else{ 命令P; } というプログラムと if(条件){ 命令A; } 命令P; というプログラムの動作の違いを 説明してください。 字下げについて →口頭による説明 elseをつかったよく似た構造もあります。 else(=そうではなくて)と読み替えて考えてください。
- 問if...else if...の構造 if(条件P){ 命令A } else if(条件Q){ 命令B } else{ 命令C } という形式があります。 (1)Pだけが満たされるときに実行される命令はどれですか?(A) (2)Qだけが満たされるときに実行される命令はどれですか?(B) (3)PもQも満たされるときに実行される命令はどれですか?(A) (4)PもQも満たされないときに実行される命令はどれですか?(C) (3)の動作は直感とすこし違うかもしれませんが、 よく考えてみましょう。
- 問+ 上の解説を簡単な具体例で たしかめてみてください。 たとえば; int p,q; p=1; q=2; if(p==1){ println("A"); } else if(q==2){ println("B"); } else{ println("C"); } このプログラムの、 p=1; q=2; の部分でp,qの値を変えて結果を観察して理解を 深めてみましょう。
- 問+ if(条件P){ 命令A } else if(条件Q){ 命令B } と if(条件P){ 命令A } else { 命令B } の動作の違いを説明してください。 条件判定には == 左右が 等しいか? != 左右が等しくないか? > 左の数が大きいか? < 左の数が小さいか? >= 左の数が大きいかまたは等しいか? <= 左の数が小さいかまたは等しいか? という記号があります。
■++条件の組み合わせ 条件を組み合わせて複雑な判定を行うしくみが 用意されています。 || または && かつ- 例 float a=2.5; if( (2 < a) && (a < 3)){ //aが2より大きいかつ3より小さいなら print(a); } aが2から3の間の数のときだけ出力されます。 (2 < a) は (a > 2) と同じことです。どちらも文法的に許されます。 if(a > 2 && a < 3){ と書いても同じです。 しかしはじめの例のように if( (2 < a) && (a < 3)){ と書いたほうが aが2から3の間に挟まれてあることが 直感的に読みやすいでしょう。 ですからはじめの例のように書くことをおすすめします。 もうひとつ工夫があります。 if( (2 < a) && (a < 3)){ //aが2より大きいかつ3より小さいなら の内側が2重の括弧になっています。 2重にする必要はないのですが、 このほうが2つの条件のアンドである、というのが 見やすいのでやはりお勧めします。 さて次の例と比較してください。
- 例 if(a > 2){ if(a < 3){ print(a); } } この例は二重のif文で書かれています。 「もしもaが2より大きければ」、で 最初のif文が満たされ、さらに 「(aが2より大きいという前提で)もしもaが3より小さければ」、で 2番目のif文が満たされることになります。 見かけは先の例とかなりちがいますが、結局、 printが実行されるのは 2よりも大きく、3よりも小さい 場合しかなく、動作は先の例とまったく同じです。 どちらのスタイルで書くかは状況に応じて、 理解しやすい、読みやすいほうを選べばいいでしょう。
■+if文とfor文の組み合わせ for文とif文を組み合わせて、もうすこし 練習してみます。- 例 for文で変数iを動かし、iが偶数の時だけ表示する。 int i; for(i=0;i<10;i++){ if(i%2==0){ print(i); } } * % は割り算したときの余りを求める記号です。 モジュロ演算子といわれます。 たとえば 5%2 は5を2で割った時のあまりで、結果は1になります。
- 問+ 上の説明を実際にプログラムを作って確かめてください。 * Answer: print(5%2);
- 問+ iが偶数の時だけ表示するプログラムを変更して、 iが奇数の時だけ表示するプログラムを作ってください。 *これらの問題は、if文などの学習のためのものです。 単に偶数を表示するプログラムを効率よく書くには 次のほうがスマートです。 for(int i=0;i<5;i++){ println(i*2); } または for(int i=0;i<10;i+=2){ println(i); } 後の例は i+=2; という計算を使っています。 これは、 iの値に2を足して新しい値とする、という意味です。 i=i+2; と同じ計算です。 i+=2; は、はじめてみると分かりにくいと思うかもしれませんが、 日本語と同じ語順で、 「iに2を加える」 と読み下せばとても自然ですね。 同様に i*=2; という表現もあります。 こちらは 「iに2をかける」 という意味です。
- 問+++ チエックパターンをつくる ■□ □■ というように交互に色違いの 10*10のブロックパターンを作ってください。 頭の体操的問題です。 * 2重for文を使います。 判定はi,jの偶数、奇数で見分ければよさそうです。 0列目を考えると、 iが偶数の時は黒 奇数の時は白とすればいいでしょう。 ところが、1列目になると、白黒が逆になります。 jが偶数か奇数かで変わってきます。 ヒントとしてループ内部の雛形を書いておきます。 if(j%2==0){//jが偶数,つまり、0,2,4,6,8列目 if(i%2==0){ 黒 } else{ 白 } } else{//jが奇数、つまり1,3,5,7,9列目 if(i%2==0){ 白 } else{ 黒 } } *スマートな解決へのヒント i+jの偶数か奇数かで判定します。 こういうところはちょっと算数のセンスが あるとスマートに解決するのですね。
- 例 二重ループの中が全部で何回アクセスされたか 数えるプログラムを作ってください。 *たとえば、3x3のループなら9回なのははじめから分かっていますが、 これも将来の発展をみこみ、テクニックを学ぶための例です。 int counter=0; //counterの値をはじめ0にセットしておく必要があります。 for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ counter++; println(counter); } }
- 問+ 上の例では println(counter);がループの内側にあるので、 カウンタが動くたびに画面に書き出されます。 これを、ループが終了したときの 結果だけを書くプログラムに書き換えてください。
- 問++ 上の例を改造し、i+jが偶数の時が何回あったかを 出力するプログラムを作ってください。 *if文が必要です。
- 問++ 1〜6までの乱数を100個生成するプログラムを 作ってください。 * 乱数はrandom()のところで作った1〜6の乱数を表示する プログラムが参考になります。 ループは一重でできますね。 100個の数字を改行で並べるとスクロールが大変なので、 各数字はスペースで区切り、 10個出力するごとに改行を入れるようにしましょう。 「10個出力するごとに」は i%10の値(モジュロ、10で割ったあまり) をif文で見張っていればできますね。 改行を出力する命令はprint("\n");です。
■++文字列のやや高度な扱い
文字列の高度な扱いに慣れることで 大量のファイルを自動処理したりすることができるようになるでしょう。 たとえば、連番のイメージを読み込んでアニメーションなどに 組み込むようなことができるでしょう。 文字列を収納するための変数は int でもfloatでもなく新しい変数の型 String型を使います。 Sが大文字であることに注意してください。- 例 String a; a="hello"; print(a);
- 例++ img0.jpg 〜 img10.jpg という11の名前を連続して生成する。 imgという部分と.jpgという部分は共通ですね。 問題は、それらにはさまれている 0〜10という名前をどう生成するか、です。 ------ String name; for(int i=0;i<11;i++){ name="img"+i+".jpg"; println(name); } ------ int型の変数に整数が自由に格納できたように 文字列変数は文字列を自由に格納できます。 この例を発展させていくと、連番のファイルを一気に 読み込むといったことが可能になります。 そのほか、「文字列の頭から3文字目以降を読み出す」、 とか「文字列変数同士を比較して一致しているかどうかを調べる」、 などの操作が可能です。この演習の目的に対しては すこし高度なので、省略します。 より詳しく勉強したい人はreferenceのStringの 項目に簡潔な説明が出ています。
■++ 配列 array
配列は「データの組」を保存できます。 たとえば、x,y座標値などは組にして保存すると便利な ことがあります。- 例 2つの要素をもつ配列の使い方 int []a = new int[2]; //int型の2つの要素をもつ配列を宣言 a[0]= 7; //配列の0番目の要素に7を代入 a[1]= 5; //配列の1番目の要素に5を代入 print(a); 配列は、画像データを保存したりするときに欠かせません。 a[0]の時の「0」のように、要素の番号のことを indexインデックスと呼びます。 配列は数学でいう「ベクトルの成分表示」と同じ概念です。
- 問+ 上の例にならって10個の要素からなるint型の配列aを用意しなさい。 for文をつかってaの各要素の値がindex番号と一致するように セットし、その結果を画面に出力してください。 たとえばa[5]には5がセットされます。 画像データは、たとえばTVサイズ(VGAともいいます) なら640x480個のpixel(画素)からできていますね。 話を簡単にするため、モノクロ画面であるとしましょう。 このようなデータの場合、640x480の方眼紙を用意して、 その方眼紙のそれぞれのマス目に明るさの値(モノクロの)が 書き込まれているようなイメージを頭の中に描くといいでしょう。 このマス目の値を1画面分そのままざっくりと保存できたら 便利です。2次元の配列を使えばそれができます。
- 2次元配列の例 2x2の配列データの例です。 int [][] a = new int[2][2]; for(int i=0;i<2;i++){ for(int j=0;j<2;j++){ a[i][j]=i+j; println(a[i][j]); } }
--------第2部---------------
第1部では、静的なプログラムを中心に学習しました。 第2部ではいよいよインタラクティブ性をもったプログラム などを学びます。なお、ベータ版を念頭に記述しています。 アルファ版はdraw()ではなくloop()を使用します。 ■基本構成 void setup(){ } void draw(){ } という2つの手続きが基本になります。 setup(){}はプログラムが起動したときに 1度だけ呼び出される手続きです。 これまで第1部で学んだプログラムは 基本的には、この void setup(){ } の内部に記述するものです。 これまではvoid setup()を省略していた、という ことです。 一方、 void draw(){ } は、プログラムが動いている間、モニターしている プログラムです。たとえば、マウスやキーボード、 シリアルポートなどを見張っていて、キーが押されたら 絵が変わる、といったことを行うには、この draw(){}の内部に記述します。 アルファ版はdraw()ではなくloop()を使用します。 draw()が常にループしていることを確かめてみます- 例+ draw()が呼び出されるたびにiの値が1つ増える -- int i; setup(){ i=0; } void draw(){ println(i); i++; delay(1000); } -- delay(1000);はプログラムを一時停止します。 単位はms(ミリセック、千分の1秒)なので、 この例では1秒待ちです。 これがないと、プログラムは高速で画面に文字を 吐き出してしまい、見難いだけでなく、 画面表示用のメモリがあふれて、ハングしやすい状態になります。
- 例+ マウスに張り付く正方形 マウスの入力を受けて、正方形を描く例です。 マウスの現在位置はmouseX,mouseYという変数名で とりこむことができます。 void draw(){ int x,y; int a; a=20; x=mouseX; y=mouseY; rect(x,y,a,a); }
- 問+ 上の例では、描いた図形がどんどんたまっていきます。 これがたまらないようにプログラムを書き直してください。 *background();をつかいます。 背景色で塗りつぶします。 背景で塗りつぶす→描く→背景で塗りつぶす→描く→... というサイクルを作るわけです。 今の場合、draw()そのものがループ構造になっているので、 「背景で塗りつぶす→描く」 という記述を1度書くだけで実現できます。
■静止画像の読み込み
すでに出来上がっている画像を読み込む場合を 考えましょう。 ・予備知識 processingはプログラム本体と必要なデータが ひとつのフォルダにまとまっています。 このフォルダはプログラム名と同じ名前です。 これまでの利用で気がついていると思いますが、 ファイルは自動的にdefaultの名前がつけられますね。 たとえば、sketch_050511a.pde というように、日付けにa,b,c..の アルファベットが順につけられます。 拡張子は.pdeです。 これらはファイルと同名のフォルダに収納されています。 必要なデータはそのフォルダ内のdataフォルダに 収納します。 sketch_050511a---sketch_050511a.pde (folder) | -- data(folder)---画像ファイルなど という構成になっています。 なお、sketch_050511a.pdeは普通のテキストファイルです。 通常のエディタなどで開いて編集することも可能です。 以下では、このフォルダを 「作業フォルダ」と呼ぶことにします。- 例+ 静止画像を1枚読み込みます。 (1)取り込む画像を準備 img0.jpg という名前ですでに保存されている絵があるとします。 サイズは任意ですが、ここでは100x100の絵とします。 (2)画像データのコピーまたは移動 この画像を作業フォルダのdataフォルダに コピーまたは移動する必要があります。 processingの内部からコピーを行うには sketch→AddFile で目的のファイルを選びます。 このとき、dataフォルダがなければ自動的に作成されます。 コピーされた画像はそのdataフォルダに格納されます。 データは通常のファイル操作でコピーすることもできます。 processingのAddFile機能では複数のファイルが選べないので、 データの数が多いときは、通常のファイル操作で コピーしたほうが効率がいいでしょう。 以上で準備完了です。 画像を扱う変数の型は、PImageです(アルファ版はBImage)。
- 例+静止画像を読み込む:スタティック(静的)でシンプルな例 以下の3行のプログラム; --- PImage p; //画像を収納するPImage型の変数pを宣言 p=loadImage("img0.jpg"); //変数pに用意した画像データをロード image(p,0,0); //画像を画面に表示 --- で画像を読み込んで表示してくれます。 もうすこし遊んでみましょう。 image(p,0,0); の0,0は画像を表示する位置です。
- 問++ マウスに張り付く画像 上の例をdraw(){ }の中に書きます。このプログラムを書き換え、 「マウスに張り付く正方形」の例を参考にして さきの画像データがマウスに張り付いて動く プログラムを作ってください。
- 例 2コマパラパラマンガの例 img0.jpg img1.jpg の2枚の絵があるとします。 --- PImage p0,p1; int i=0; void setup(){ p0=loadImage("img0.jpg"); p1=loadImage("img1.jpg"); } void draw(){ if(i%2==0){ image(p0,0,0); } else{ image(p1,0,0); } i++; } --- これをもうすこし発展させた例がexampleにあります。 File→sketchbook→example→Image→Sequential をよく研究してください。 ここまでの知識を組み合わせていくと、 入力に応じて、提示する静止画の種類を 変えたり、パラパラマンガでアニメーションを つけたりすることができるようになっているはずです。
- 問+ 重要 上の例では、 p0=loadImage("img0.jpg"); p1=loadImage("img1.jpg"); をsetup(){}の中で行っています。 これをdraw(){}の中でやらない理由を説明してください。 *答え イメージファイルを「読み込む」のは一度でいいはずです。 draw()の中でやると、同じファイルを何度も読み込むというとても 無駄な動作をするプログラムになってしまいます!
- 問+ 2枚の画像を読み込んでおき、 マウスが画面の左半分にきたら1枚目の絵を表示し、 マウスが画面の右半分にきたら2枚目の絵を表示する プログラムを書いてください。 *if文を使い、マウスの位置によって表示する絵を切り替えます。
■[解説]+ 変数の有効範囲
先の例では PImage p; int i=0; が先頭に来ていました。 「変数が有効な範囲はそのブロックの中」 というルールがあります。 たとえば、 void draw(){ int i; int i; } はエラーになります。iを2度宣言しているからです。 一方 void setup(){ int i; } void draw(){ int i; } はエラーになりません。 はじめのiはsetupの中でだけ有効。 あとのiはdrawの中でだけ有効となります。 つまり、同じ文字をつかってはいますが、 今の場合はブロックが別なので、 これらは別の変数と思ってかまいません。 以下では、説明の便宜のため、 さらに簡略化してvoid setup()などを 省略します。 たとえて言えば、 { } で囲まれたブロックは家庭であるとしましょう。 ある家庭にhiroshiくんがいたとして、 同じ家庭の中にもう一人hiroshiくんという名前を つけることはありません。 一方、{}が違うと別の家庭なので、 そこには別人のhiroshiくんがいても混乱は 生じないわけです。 { int i; ... } { int i; ... } は2回int iが宣言されていますが、2つの独立した ブロック内部の話なので文法にかなっている、というわけです。 { int i; ... } { ... } の場合、後半でiを使うことはできません。 iの宣言は前半のブロックで宣言されているだけなので 後半のブロックで使う場合は宣言なし、とみなされ、 使用できません。もし必要なら、後半のブロックでも 宣言すれば使用できます。 さて、 int i; { ... } { ... } の場合は、2つのブロックの外側でiが宣言されています。 このようなiはどちらのブロックからも使用することができます。 両方のブロックに共通な変数になるわけです。 このようにプログラムのどこからでも利用可能な 変数をグローバル変数といいます。
■++ビデオカメラからの入力
カメラを利用するには 希望のあった、ビデオカメラからの入力を受ける プログラムを掲載しておきます。 カメラはfirewireでコンピュータにつながっているものと します。MacOSの場合はカメラの電源をいれて、 i-movieなどで接続を確認してから使うといいでしょう。 windowsの場合は設定がやや複雑なのでこのノートでは 触れません。- 例++シンプルに画面に表示します。 -- // Capture and add pink stripe // by muller h. 2005.05.12 // simple camera capture import processing.video.*; Capture camera; int cameraW=80,cameraH=60; //取り込む画像の大きさを決めている変数です。 //この変数は省略可能 void setup() { size(260, 220); println(Capture.list()); //camera = new Capture(this, width, height, 30); camera = new Capture(this, cameraW, cameraH, 3); // 上で決めた変数の大きさで取り込んでいます。 // 最後の3はフレームレート(1秒間のコマ数) } //以下3行はカメラデータを読み込む決まり文句。 void captureEvent(Capture camera) { camera.read(); } void draw() { image(camera, 50, 50,160,120); //50,50の位置に大きさ160x120で表示 //取り込んだ大きさは80x60なので2倍に拡大したことになります。 } --
- 例+++ より高度な例 すこし遊んでみました。 ちゃんとわかりたい人はProcessingのHELPにある reference→library→videoの解説を見てください。 #人と違うものが作りたかったら日ごろからの #地道なお勉強も大事です。 中央の小さな四角にマウスが入ると、 表示が変わります。 グリーンのラインは直接カメラデータに 上書きしています。 ---------- // Capture // 2005.05.12 by Muller H. import processing.video.*; int cameraW=80,cameraH=60; int cx,cy; Capture camera; void setup() { size(300, 300); println(Capture.list()); //camera = new Capture(this, width, height, 30); camera = new Capture(this, cameraW,cameraH, 6); noFill(); cx = width/2; cy = height/2; } void captureEvent(Capture camera) { camera.read(); } void draw() { int x,y; int x0,y0; int w,h; color c = color(100,200,50); x = mouseX; y = mouseY; // define image size w = cameraW; h = cameraH; for(int i=0;i
■++ 関数(またはメソッド)
関数については第1部で解説してもよかったのですが、 ここで行います。 あるまとまった手続きを何度もおこなうような場合、 それらを束ねて、名前をつけ、その名前を呼ぶと実行 してくれるようになれば便利です。 関数はそのような使い方ができます。 まずは簡単な例から。- 例+ void setup(){ hello(); } //以下が関数の定義部分 void hello(){ print("hello"); } *void型の関数hello()を定義して呼び出しています。 定義部分は 型 名前(){ 本体 } という形をしています。 今の場合、void型で、名前はhelloという関数を 定義しています。本体は、画面にhelloとプリントする ことです。 この関数をつかうには、使いたい場所で hello(); と書きます。 void setup(){の直後の hello(); がそうです。
- 問+ 画面に適当な2本の線をひく関数を作ってください。 型はvoid型で、名前はline2()とでもしておきましょう。 それをsetupの中で呼び出して機能するか確かめてください。 次はint型の関数の例です。
- 例+ --- void setup(){ int c; c=add(1,2); print(c); } int add(int a,int b){ return(a+b); } --- * a+bを計算し、結果をint型で返します。 値を返すときは return(返す値); という命令をつかいます。
標準関数abs()の使い方
abs()という関数が標準であります。 マイナスの数を強制的にマイナス記号をとってプラスに するという関数です。 数学言葉では「絶対値をとる」、といいます。- 例+ abs(-3) は3になります。 abs(3) も3になります。 すこし面白い応用例を示します。
- 例++ --- int v; int max = 5; for(int i=0;i < max*4;i++){ v=abs((i+max)%(max*2)-max); print(v); } --- 0からはじまり、maxまでの数字を昇順に表示したあと、 降順になり、0まで表示。このサイクルを2回くりかえします。 回数を変化させるには、 for(int i=0;i < max*4;i++){ のところで*4を変更します。 たとえば、*8にすると4回繰り返します。 すこし難しいかもしれませんが、 なぜこういう動きになるかは、自分で考えてください。 高校数学の言葉でいえば、 y=|x-a| のグラフの問題と同等です。 ・比較 ある範囲の値で数字を押さえたいとき、たとえば0から255 までに押さえたいとき、 v=i%256; とするのもよく使う手ですね。 この場合、iが0から順に増えていくとして255を 超え、256になったらゼロにリセットされます。 この周期を繰り返します。 横軸をiにしてグラフにするとノコギリの歯のように、 右側がきりたった崖の形になります。 上の例では、三角の山が上りと下りを交互にくりかえす形になります。 工学(電子音楽)用語でいえばノコギリ波と三角波の違いです。
■質問に答えて これまで学んできたことのまとめをかね、みなさんからの質問に 関連した簡単な例を作ってみました。
- 例++ 波紋のように点が広がっていくアニメーション 波紋のように点が広がっていくアニメーション。 単純に円を描くのなら標準のellipse()でいいでしょう。 ここでは、点を円にそって並べる関数 dotCircleを作ってみました。 三角関数の知識が必要です。 -- int i=0; void setup(){ size(200,200); //dotCircle(0,0,30); } void draw(){ int r; background(180); r = i%50; //半径50までいったら0にもどす dotCircle(0,0,r);//最初の円 r = (i-25)%50; if(r>0){ dotCircle(0,0,r); //2番目の円 } i++; delay(50);//時間間隔。大きくするとゆっくり } void dotCircle(int cx,int cy,int r) { float div = 36;//分割の細かさ float th; int x,y; int dotSize=2;//ドットのサイズ noStroke(); fill(250,100,100);//色指定 for(int i=0;i
- 例++ 回転しつつ透明度の変わる画像 ・適当な画像が1枚必要です。 ここではboy.jpgという名前で1枚用意しています。 ・void rotImage(int i)という関数を自分で作っています。 --- PImage p; int i=0; void setup(){ size(200,200); p=loadImage("boy.jpg"); //image(p,0,0); } void draw(){ //中心の円にマウスがあたったときだけ描くようにしています。 //描きっぱなしでいいならifの条件と、elseまるごと不要。 if(90
■シリアルポートの接続
クワクボさんのキットはベータ版でも動きます。 以下のシンプルなプログラムを参考にしてください。 アルファ版を使い続ける理由は僕はない、と 思っています。 ベータ版のほうがビデオの扱いなどもすっきり 改良されていていいですよ。 ----------- 例 シリアルからの入力を1つだけ読む import processing.serial.*; int a; Serial myPort; println(Serial.list()); myPort = new Serial(this, Serial.list()[2], 9600); a=myPort.read(); println(a); println(Serial.list()); でシリアルに結びつけられているデバイスの 一覧が表示されています。 Serial.list()[2] のところでデバイスを指定しています。 0,1はたいていモデムが割り当てられているので 2番が該当することが多いようです。 うまく動かない場合はここの数字を変えてみてください。 --
- 例 シリアルが有効な限り入力を受け画面に書き続ける --- import processing.serial.*; Serial myPort; void setup() { println(Serial.list()); myPort = new Serial(this, Serial.list()[2], 9600); } void draw() { while (myPort.available() > 0) { int a = myPort.read(); println(a); } } *while()はこれまで説明しませんでしたが、 for文とならぶ、代表的なループ構造です。 while(条件){ 命令 } で、条件が正しい間、命令を繰り返します。 for文をつかった次のプログラム for(int i=0;i<10;i++){ 命令; } は、whileをつかうと int i=0; //変数の初期化。忘れやすいので注意。 while(i<10){ 命令; i++; } と表せます。 forとwhile,どちらをつかってもいいのですが、 次の使い分けが普通です。 for文 回数がわかっているとき while文 回数は不明で、ある条件が満たされている間だけループさせるとき 上の例では、シリアルポートが正常にうごいているかどうか をチエックする関数 myPort.available()を見張っていて、それが 正の値のとき、つまり正常に動作「しているあいだ」(=while) はループを続けます。
■アニメーション
- アニメーションの例 静止画連番ファイルを読み込み、アニメーションとして 表示するシンプルな例があります。 file → sketchbook → Examples → Image →sequantial をよく研究してください。 framelate(30) とあるところを framelate(10) などと小さくするとスロー再生になります。
- アニメーションの例(2) --学生とのやりとりから-- 静止画連番ファイルを読み込み、アニメーションとして 表示する例です。 Kさんのプログラムひながたをさくっとつくって おいたので、メイルでながします。 gif画像40枚x3を読み込み、 マウスの位置で切り替えるようにしています。 415x300というちょっと半端なサイズですが、 サクサクうごいていますよ。 コメントを日本語でいれておきましたが、 実行するときはこの日本語コメントが 悪さをするかもしれません。そのときは コメント部分を削ってください。 画像は含まれていないので、用意することが必要です。 このプログラムでは、 majo0001.gif - majo0040.gif obake0001.gif - obake0040.gif usa0001.gif - usa0040.gif というファイル合計120枚を読み込んでいます。 ----- PImage []majo = new PImage[40]; PImage []obake = new PImage[40]; PImage []usa = new PImage[40]; int n=0; void setup(){ String name; // majo0001.gif - majo0040.gif を読み込み // 下の(i<9? "000" : "00") の部分はちょっとトリッキー //ですが、3項演算子というやつです。if文のようにつかえて // 条件 ? A : B //とつかいます。 // 条件が正しければAを出力、正しくなければBを出力します。 //この例では、iが一桁だと、000をつけ、2桁だと00をつけます。 for(int i=0;i<40;i++){ name="majo"+(i<9? "000" : "00") +(i+1)+".gif"; // println(name); majo[i] = loadImage(name); } //obake0001.gif - obake0040.gifの読み込み for(int i=0;i<40;i++){ name="obake"+(i<9? "000" : "00") +(i+1)+".gif"; // println(name); obake[i] = loadImage(name); } //usa0001.gif - usa0040.gifの読み込み for(int i=0;i<40;i++){ name="usa"+(i<9? "000" : "00") +(i+1)+".gif"; // println(name); usa[i] = loadImage(name); } size(415,300); //フレームレートを設定します。 //1秒に30枚の速さで表示します。 framerate(30); } //以下が描画部分。 //マウスの位置が画面を縦に3分割し、マウスの位置に //よって表示するアニメを切り替えます。 void draw(){ if(mouseX < width/3){ image(majo[n%40],0,0); } else if(mouseX < width/3*2){ image(obake[n%40],0,0); } else{ image(usa[n%40],0,0); } n++; } ------------
●アルファ版かベータ版か?
アルファ版ではこの第二部のプログラムは draw()をloop()にすればある程度動くと思いますが、 動作確認していません。 上にもすでに書いたように、アルファ版は使い続ける積極的 理由がありません。ベータ版をすすめます。
glossary
- 変数 数字、文字などを格納する器
- default なにも設定しないときの「お任せ」の状態
- default値 (ディフォルトち) お任せの値 ex. default値は0に設定されています。
- int 整数
- float 小数
- void 「からっぽ」、「無効」、という意味。 void tetsuzuki(){ } という記述は、tetsuzuki(){}という手続き全体が括弧の中で 閉じていて、外に値を返したりしない、という意味です。 もうすこし勉強がすすむと、 int tetsuzuki(){ } というような書き方もでてきます。 これは、tetsuzuki(){}という手続き全体を行うと、 最後にint(整数)の結果を返すという意味です。
三角関数
3分でわかる三角関数。 sin(サイン)とcos(コサイン)では苦しめられた人もおおいでしょう。 三角関数というよりも、「円に関係する関数」と思ったほうが理解しやすいです。 半径1の円を想像します。 円の中心を原点にします。 円の周上に点があります。この点ははじめ3時方向にあります。 そして、時計と反対方向にまわることができます。 3時方向からはかって、角度がたとえば30度の点にきました。 (2時の方向ですね)。このときの点の位置(x座標、y座標) はこの角度だけで決定されますね。 このときのx座標をcos30° このときのy座標をsin30° と呼ぶことに(というか定義)します。 その値がいくつですか、というのはまた別の問題です。 三角関数のサイン、コサインは、 半径1の円で角度を指定したとき、その座標がいくつですか、 というのを知るための装置 だと思えばいいでしょう。 点がもう少しすすんで90度のところにきました (12時の方向ですね)。 このときのx座標はcos90° このときのy座標をsin90° ということになります。 絵を描いてみればすぐわかりますが、 この90度のとき、x座標は0,y座標はもっとも大きくなって1 ですね。 つまり sin90°=1 cos90°=0 ということです。 同様に sin180°= 0 cos180°= -1 sin270°= -1 cos270°= 0 sin360°= sin0°= 0 cos360°= cos0°= 1 などがすぐわかるでしょう。 コンピュータはx,y座標が得意です。 円を描かせるときには、 x=cos(t); y=sin(t); として、tを0度から360度で順にうごかして行けば 半径1の円が描けるはずです。 半径が2の場合は全体に2倍して x = 2*cos(t); y = 2*sin(t); とすればいいですね。 一般に半径がrのときは x = r*cos(t); y = r*sin(t); とすればいいわけです。 あとひとつだけ注意することは 数学では、角度を表現するときにの1周を360度ではなく 2*πとします。180度がπと覚えておくといいでしょう。 こうすると数学ではいろいろ便利なことが多いのでこう 定義しています(なぜ便利かの理由詳細は省きます)。 30°だとπ/6, 60°だとπ/3となること は容易にわかりますね。 まとめ 角度がt(180°をπとするはかりかた)のとき、 半径rの円は x = r * cos(t) y = r * sin(t) と表せます。 tを固定すれば円の上の1点、 0から2*πの範囲でうごかしてやると円を描きます。