はじめに
この本について
『Ruby で描く、動かす、遊ぶ — rbCanvas/p5 ではじめるクリエイティブコーディング』は、プログラミングを初めて試す人が、コードで絵、動き、音、遊べる作品を作るための本です。rbCanvas/p5 はブラウザで動くため、開発環境をインストールせずに始められます。
最初は、丸を一つ描く短いコードから始めます。数字を一つ変えると丸が大きくなり、もう一つ変えると画面の外へ飛び出します。そこから色、模様、アニメーション、マウスやキーボードへの反応、文字、画像、音、3D、ゲームへ少しずつ広げます。最後には、気に入った仕組みを選び、自分の作品として保存します。
この本では、Ruby の文法を覚える前にコードを動かします。プログラミングで遊び、好きになり、次も試したくなることが目標です。初めて出る言葉や命令は、使う場所で一つずつ説明するため、一人でも進められます。
クリエイティブコーディングとは
クリエイティブコーディングは、プログラムを使って絵や動き、音、遊べる仕組みなどを作る活動です。
たとえば、画面に丸を一つ描きます。その丸を動かすと、生き物のように見えるかもしれません。たくさん並べると模様になり、半透明にして重ねると、思いがけない色が生まれます。マウスの動きへ反応させれば、見るだけだった絵が、触って変えられる作品になります。
コードを使うと、形を描き、動かし、人の操作へ反応させられます。
紙に引いた線はそのまま残ります。コードで描いた線には、「少しずつ右へ動く」「毎回違う色になる」「クリックされたら増える」といった振る舞いを加えられます。形、時間の流れ、人の操作を一つの作品へ組み合わせられます。
クリエイティブコーディングでは、同じコードから始めても、変える数字や色によって違う作品ができます。形がはみ出した画面や、速すぎる動きが気に入ったら、その版も保存して次の変更に使えます。
この本では、次の小さな繰り返しを大切にします。
- まず動かす
- 一つだけ変える
- 何が起きたか見る
- 面白ければ、もう一度変える
まずコードを動かします。疑問が出たところで説明を読み、もう一度コードへ戻ります。
この本で使う rbCanvas/p5
rbCanvas/p5 は、ブラウザで Ruby を実行し、絵やアニメーションを作れる環境です。インストールせずに始められます。画面へ描画する部分には、クリエイティブコーディングのための JavaScript ライブラリである p5.js の機能が使われています。
本書では、公式の操作説明と API リファレンスがそろっている 0.5.1 の Opal 版を使います。Opal 版では、数値をすべて小数も扱える数として処理し、:title のようなシンボルを文字列として扱います。通常の Ruby と違う結果になる箇所では、その都度説明します。別の版では、同じコードでも動きが異なる場合があります。
Ruby は、人が読み書きするために作られた
Ruby は、まつもとゆきひろさんが作り、1995 年に公開したプログラミング言語です。まつもとさんは、いくつもの好きな言語の特徴を組み合わせ、人にとって自然に読み書きできる言語を目指しました。
コードを書く人が読みやすく、楽しめることを大切にしている点は、この本で Ruby を使う理由の一つです。
p5.js は、コードを表現の道具にする流れから生まれた
p5.js は、アーティストでプログラマーの Lauren Lee McCarthy(ローレン・リー・マッカーシー)さんが 2013 年に作り始め、Evelyn Eastmond(エブリン・イーストモンド)さんが立ち上げに加わりました。無料で利用、改良、再配布できるオープンソースの道具で、現在は世界中の人が開発へ参加しています。
その前には、Casey Reas(ケイシー・リース)さんと Ben Fry(ベン・フライ)さんが 2001 年に始めた Processing があります。Processing は、アーティストやデザイナーもコードを視覚表現に使いやすくする環境です。p5.js はその考え方を受け継ぎ、ウェブブラウザで作品を作れるようにした JavaScript のライブラリとして始まりました。
p5.js は公開後も、利用者からの問題報告、説明の追加、機能の修正によって更新されています。現在の機能には、最初の作者だけでなく、開発に参加した人たちの仕事も含まれます。
rbCanvas/p5 は、Ruby から p5.js の機能を使えるようにした
p5.js のサンプルは、本来 JavaScript で書きます。北村幸紀さんが開発した rbCanvas/p5 では、その多くの機能を Ruby の書き方で使えます。仕組みと対応機能はrbCanvas/p5 0.5.1 概要に掲載されています。
本書で使う 0.5.1 では、Opal(オパール)が Ruby のコードを JavaScript へ変換し、rbCanvas/p5 が p5.js の描画機能へ渡します。
あなたが書く Ruby
↓
Opal が JavaScript へ変換
↓
rbCanvas/p5 の橋渡し
↓
p5.js とブラウザのキャンバス
作品から形や動きを一つ見つける
実際の作品を見ると、クリエイティブコーディングがどのように使われてきたかを具体的に知ることができます。
Casey Reas さんの「Process」シリーズでは、形が動くための短い規則を決めると、実行するたびに異なる画面が生まれます。作品を見たら、気になった形や動きを一つ選びましょう。
Lauren Lee McCarthy さんの「SOMEONE」では、人がスマートホームの音声アシスタント役になり、離れた家の中を見守って話しかけます。作品を見ると、便利さと同時に、見られることや人を信頼することについても考えられます。作品を使う人がどのように操作し、画面や音がどう応じるかを一つ探します。
作品全体を再現する必要はありません。気になった形、色、動きのどれか一つを選び、あとで短いスケッチとして試します。
最初はコードをコピーして動かす
この本は、年齢を問わず、コードを一度も書いたことがない人を対象にしています。def、end、丸括弧、カンマといった記号は、最初に使う場所で説明します。
数学を使う場面では、公式より先に具体的な数字を入れます。コードを動かし、数字を一つ変え、画面で何が起きるかを見てから仕組みを説明します。
最初は掲載コードをコピーして実行し、画面が変わることを確認します。そのあと、指定された 1 か所を変えます。変えた行と画面の変化を見比べることから始めます。
エラーが表示されても、壊してしまったわけではありません。直前に変更した 1 か所を元へ戻せば、動いていた状態へ帰れます。戻し方が分からないときは、困ったときを使えます。
始める前に エディタの使い方
このページでは、rbCanvas/p5 のエディタを開いてから、作品を保存するまでを一度だけ通します。最初に使うボタンは、「書く」「実行する」「保存する」の 3 つです。ほかのボタンは、必要になった章で説明します。
まず、rbCanvas/p5 0.5.1 エディタを開きます。この本を今のタブへ残し、エディタを別のタブで開くと、二つの画面を行き来できます。図 0-1 と同じように、上部へ Help、ツール、下向き矢印、三角形のボタンが並んでいれば、基準にしている版の画面です。
本書の基本章はキーボードを使えるパソコンで進めやすく、実機確認には Chrome を使っています。タブレットなど、ファイルのドラッグ&ドロップが難しい端末では、第11章の画像と第16章の音を図と説明で確認し、その次の章へ進めます。図形、動き、入力、Ruby の章は素材を取り込まずに試せます。
コード欄には「全体」と「変更部分」がある
def setup から始まり、必要な draw なども入っている長いコード欄は、原則としてエディタのコード全体を置き換えて実行できます。一方、数行だけの短いコード欄は、直前のコードへ足したり、一部分と置き換えたりする変更部分です。短い変更部分だけを新しいエディタへ貼ると、そこで使う変数やキャンバスがまだ用意されておらず、エラーになることがあります。
変更部分の前には、「draw の中へ足す」「直前の if と置き換える」のように場所を書きます。場所が分からなくなったときは、その節の最初にある長いコードへ戻りましょう。動いている状態を保存してから、一か所ずつ変更すると違いを追えます。
この本のコードは、rbCanvas/p5 0.5.1 の Opal 版を基準にします。公式の使い方ページも同じ版に対応しています。ブラウザや画面幅によって、実行結果の表示場所が変わることはあります。
0.1 エディタの画面を見る
エディタにはコードを書く場所があります。実行結果は標準設定では新しいタブに開き、設定を変えるとコードの右側へ表示できます。
左側に最初から入っているコードは、そのまま使うことも、消して書き直すこともできます。本書のコードは、コードブロック右上のコピーボタンでコピーし、エディタへ貼り付けられます。
0.2 実行して、止めて、もう一度実行する
三角形の印が付いた実行ボタンを押すと、コードが読み取られます。0.5.1 の標準設定では、新しいタブが開いて結果が表示されます。図 0-1 のように同じ画面でコードと結果を見たい場合は、ツールメニューの「実行画面」を有効にします。
コードを直したあと、画面が自動で変わらないときは、もう一度実行ボタンを押します。右側に実行結果がある場合は、もう一度三角形の実行ボタンを押すと、新しい結果へ入れ替わります。実行結果が別タブで開いた場合は、そのタブを閉じます。
最初は次の小さな往復を繰り返します。
- コードを 1 か所だけ変える
- 実行ボタンを押す
- 画面のどこが変わったか見る
- 気に入らなければ元へ戻す
一度に一つだけ変えると、コードと結果のつながりを見つけやすくなります。
0.3 新しく始める前に保存する
保存ボタンを押すと、作品が HTML ファイルとしてダウンロードされます。HTML はウェブページに使われるファイル形式です。ここでは、コードと実行に必要なものが入った作品ファイルとして扱います。
画像や音声を取り込んだ作品では、それらも一つの HTML ファイルに含めて保存されます。保存先はブラウザの設定で変わります。見つからないときは、まずブラウザの「ダウンロード」一覧を開いてください。
新規作成やブラウザの再読み込みをすると、保存していない変更は失われることがあります。新しく始める前に、動いている版を保存しておくと安心です。
今の絵を画像として残す
HTML の保存は、コードと取り込んだ素材を残す操作です。マウスで描いた軌跡や、実行時に乱数で決まった模様まで、そのまま残すものではありません。今見えている絵も残したい場合は、画面を画像にするスクリーンショットを使います。
- Windows では、Windows キーと Shift キーを押したまま S キーを押します。範囲を選ぶ画面で、キャンバスの左上から右下までドラッグします。切り取りツールで画像を開き(通知が出た場合はクリックします)、保存ボタンで保存先と名前を選びます。
- Mac では、Shift キーと Command キーを押したまま 4 キーを押します。キャンバスの左上から右下までドラッグして放すと、画像ファイルが保存されます。保存先を変更していなければ、デスクトップにあります。
保存した画像を開き、残したい絵が写っているか確認してから、スケッチを閉じます。範囲を選び直したいときは、ドラッグの途中で Esc キーを押して取り消し、同じ操作をやり直します。画像は見返すために使い、コードの編集を続けるときは HTML を使います。
0.4 保存した作品を開く
保存した HTML ファイルには、二つの開き方があります。
- ファイルをブラウザで開くと、作品を直接実行する
- ファイルを rbCanvas/p5 エディタへドラッグ&ドロップすると、コードを再び編集する
ドラッグ&ドロップは、ファイルをマウスやトラックパッドでつかみ、エディタの上まで運んで放す操作です。画像や音声を取り込むときにも同じ操作を使います。取り込んだ素材はエディタ下部に小さな画像や名前で表示されます。
0.5 エラーは「直す場所の手掛かり」
実行しても絵が出ず、文字のメッセージが表示されることがあります。これはエディタが壊れたのではなく、Ruby がコードを最後まで読めなかったか、実行中に困ったことを知らせています。
最初に見るのは、メッセージにある行番号です。ただし、本当の原因が一つ前の行にある場合もあります。次の順で確かめます。
- 直前に変えた 1 か所を見る
end、丸括弧、引用符の閉じ忘れを見る- メソッド名の大文字と小文字を見る
- 直前の変更を元へ戻して実行する
詳しい症状別の戻り方は、困ったときにあります。
0.6 機能の名前から説明を探す
エディタのヘルプボタンは、rbCanvas/p5 の API リファレンスを開きます。API リファレンスは「使える命令の説明を集めた辞典」であると同時に、まだ知らない表現を見つける図鑑でもあります。「円以外も描きたい」「音に反応させられるかな」と思ったとき、気になる名前を一つ探してみましょう。本書で使う項目は、それぞれの章でも説明します。
p5.js の公式リファレンスにも、図形、色、文字、画像、入力、動き、3D などの機能が分類されています。たとえば circle() は円、rect() は四角形、background() は背景色の説明です。説明中のコードは JavaScript ですが、機能名や引数の順番は rbCanvas/p5 で使うときの手掛かりになります。
// p5.js の例
circle(200, 150, 100);
rbCanvas/p5 では Ruby として、行末のセミコロンを付けずに書きます。
# rbCanvas/p5 での書き方
circle(200, 150, 100)
ただし、p5.js のすべての機能が、使っている rbCanvas/p5 の版で同じように動くとは限りません。本書では rbCanvas/p5 で確認した書き方を先に示します。新しい命令を探したくなったら、まずrbCanvas/p5 0.5.1 API リファレンスで名前とサンプルを確認します。リファレンスの使い方は、第3章で実際に ellipse を探しながら説明します。
準備はこれで終わりです。第1章では、実際に一つの円を変えます。
参考資料
- Microsoft「Snipping Tool を使ったスクリーンショットの撮影」(英語) — Windows での範囲選択と画像保存
- Apple「Mac でスクリーンショットを撮る」 — Mac での範囲選択と保存先
第1部 形と色を発見する
最初の目標は、コードを変えて画面に変化を起こすことです。円、四角形、線、色だけでも、数字の選び方で画面は大きく変わります。
第1部では、サンプルの数字や順番を気軽に変えてみます。最初に第1章で操作を確かめます。そのあとは第2章、第3章の順に進みます。
第1章 最初の 5 分で絵を変える
コードを 1 行変えると、画面はすぐに変わります。この章では動くサンプルを先に試し、入力した数字と画面の変化を見比べます。
1.1 まず動く 6 行
rbCanvas/p5 のエディタを開き、次のコードを貼り付けて実行します。開き方やボタンの場所が分からないときは、先に始める前に エディタの使い方を開いてください。
def setup
createCanvas(400, 300)
background(245, 238, 220)
fill(235, 90, 120)
ellipse(200, 150, 120, 120)
end
薄い背景の中央に、赤みのある円が表示されれば成功です。各行の意味がまだ分からなくても、そのまま先へ進めます。
エディタの左上にある三角形のボタンが実行ボタンです。0.5.1 では、標準設定だと実行結果が新しいタブに表示されます。ツールメニューの「実行画面」にチェックを付けると、コードの右側に実行結果を表示できます。画面の配置は、使う端末やウィンドウの広さによって変わることがあります。
実行ボタンは、書いたコードを Ruby として読み取り、最初から動かし直す合図です。数字を変えただけでは右側が変わらない場合があります。そのときは、もう一度このボタンを押します。
最初の長いコードにある ellipse(200, 150, 120, 120) の、左から 3 番目の 120 だけを 220 へ変えて実行します。円が横へ広がります。次に、最後の 120 も 220 へ変えて、もう一度実行します。次の 1 行は、二つ目の変更が終わった変更部分です。この行だけを実行せず、最初のコードの同じ行を書き換えます。
ellipse(200, 150, 220, 220)
幅と高さがそろい、大きな円になります。エラーが出たら、変更した行を ellipse(200, 150, 120, 120) へ戻して実行します。コード中の数字を変えると、画面を直接調整できます。
この短いコードには、初めて見るメソッドが 4 つあります。メソッドは、コンピューターに頼める処理へ付けられた名前です。
createCanvas(クリエイト・キャンバス)は、絵を描く場所を作るbackground(バックグラウンド)は、キャンバス全体を一色で塗るfill(フィル)は、このあと描く形の内側の色を決めるellipse(イリプス)は、楕円を描く
コードは上から順に実行されます。そのため、「描く場所を作る→背景を塗る→色を決める→楕円を描く」という順番になっています。fill はそれ自身では形を描きません。次に描く ellipse の色を準備しています。
それぞれの名前の後ろにある丸括弧には、その処理へ渡す値が入ります。background(245, 238, 220) と fill(235, 90, 120) の 3 つの値は色を表します。色の数値は第3章で、ellipse の 4 つの値はこのあとの1.2 節で詳しく試します。
ここで、コードの形だけ先に確認します。
def setup
# この間に、最初に実行したい処理を書く
end
def(デフ)は「これからメソッドを定義する」という Ruby の目印です。メソッドは、いくつかの処理へ名前を付けたものです。ここでは名前が setup(セットアップ)です。最後の end(エンド)は「定義はここまで」という目印です。
setup の内側にある行は、行頭を半角スペース 2 個ぶん右へ下げています。これをインデントと呼びます。Ruby はこのスペースだけで処理範囲を決めませんが、どこからどこまでが setup か読みやすくなります。
# から行末まではコメントです。人が読むためのメモで、Ruby は処理として実行しません。上のコメント行は説明用です。コピーしても実行結果は変わらず、スケッチから省くこともできます。
次に、丸括弧を見ます。ここからの短いコード欄は、最初の長いコードから取り出した変更部分です。短い部分だけを実行するのではなく、最初のコードの同じ行を探して書き換えます。
createCanvas(400, 300)
createCanvas が呼び出す機能の名前です。丸括弧の中には、その機能へ渡す値を書きます。この値を引数(ひきすう)と呼びます。カンマは引数の区切りです。ここでは 400 が横幅、300 が高さです。
1.2 4 つの数字と円の形
ellipse は、楕円を描くためのメソッドです。丸括弧の中には 4 つの値があります。次の 1 行も、最初の長いコードの ellipse と置き換える変更部分です。
ellipse(200, 150, 120, 80)
左から順に、中心の x 座標、中心の y 座標、幅、高さです。x は横方向、y は縦方向の位置を表します。
| 値 | 役割 | 変えると起こること |
|---|---|---|
200 | 横の位置 | 小さくすると左、大きくすると右 |
150 | 縦の位置 | 小さくすると上、大きくすると下 |
120 | 幅 | 横に広がる、または細くなる |
80 | 高さ | 縦に伸びる、または平たくなる |
4 つを一度に変えると、どの数字が何をしたか分かりにくくなります。まず 1 つだけ変え、実行し、違いを見ます。この「一度に一つ」は、動かないコードを調べるときにも役立つ方法です。
1.3 setup は最初に一度だけ呼ばれる
def setup から end までが、先ほど確認した setup メソッドの定義です。rbCanvas/p5 は実行開始時に setup を 1 回呼びます。その中で、キャンバスを作り、背景を塗り、円を描いています。
def setup
createCanvas(400, 300)
end
createCanvas(400, 300) は、幅 400、高さ 300 の描画領域を作ります。ここで作る領域がキャンバスです。
end を消すと、Ruby はメソッドの終わりを見つけられず、確認が必要な場所をエラーとして表示します。直前に消した end を元と同じ行へ戻し、もう一度実行します。
1.4 順番を変えると消える理由
描画は上から順に重なります。次のコードでは、円を描いたあとに背景を塗っています。
def setup
createCanvas(400, 300)
fill(235, 90, 120)
ellipse(200, 150, 120, 120)
background(245, 238, 220)
end
円が見えません。ellipse が失敗したのではなく、あとから塗った背景に隠れました。紙の上に絵の具を重ねる場合と同じです。
描画命令の順番によって、何が手前に見え、何が後ろへ隠れるかが変わります。
1.5 元の絵を離れる 3 つの改造
ここからは、好きなものを 1 つ選びます。
backgroundの 3 つの数字を変え、落ち着く色や驚く色を探すellipseをもう 1 行追加し、少しずらして重ねるellipseの幅と高さを別々にして、円ではない形にする
たとえば、2 つの楕円を重ねると次のようになります。
def setup
createCanvas(400, 300)
background(28, 32, 50)
noStroke
fill(255, 190, 70)
ellipse(175, 150, 160, 160)
fill(28, 32, 50)
ellipse(225, 125, 160, 160)
end
noStroke(ノー・ストローク)は、このあと描く形の輪郭線を表示しないメソッドです。同じ背景色の楕円を上に重ねたため、手前の形が穴のように見えます。特殊な月の命令は使っていません。楕円と描画順だけで三日月が生まれました。
好きな画面になったところを、この節の区切りにできます。
丸を生き物へ変える
丸を練習で終わらせず、顔とも機械とも分からない生き物へ育てられます。次のコードを、三日月の例とは別の新しいコードとして試します。
def setup
createCanvas(400, 300)
background(210, 235, 245)
fill(255, 190, 80)
ellipse(200, 155, 190, 150)
fill(30)
ellipse(165, 135, 18, 28)
ellipse(235, 135, 18, 28)
fill(255, 100, 120)
ellipse(200, 185, 70, 25)
end
同じ ellipse だけでも、位置と縦横の大きさを変えると、頭、目、口という役割が生まれます。目を上下でずらす、口を画面の外へはみ出すほど大きくする、背景を夜の色にする、のどれか一つを選んで試してみましょう。「どんな性格に見えるか」を数字で探す作品です。
顔とも機械とも分からない画面に名前を付けて保存します。うまく動かないときは、章の最初の 6 行へ戻せます。次は数字へ名前を付け、位置を動かしやすくします。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
createCanvas、background、fill、ellipse、noStrokeの説明 - Ruby 公式「Ruby とは」 — Ruby のメソッドや変数を含む言語の特徴
第2章 数字は形を動かすつまみ
第1章では、ellipse の数字を直接変えました。数字が 1 か所だけなら、直接書くと位置や大きさをすぐ変えられます。同じ数字を何度も使い始めると、「どれが中心の位置だったか」を探す時間が増えます。変数を使うと、center_x のような名前から、その数字が何に使われているか分かります。
キャンバスでは、左上が座標 (0, 0) です。右へ進むほど x が増え、下へ進むほど y が増えます。
2.1 200 ではなく center_x と書く理由
次の 2 行は、同じ位置に円を描きます。
ellipse(200, 150, 100, 100)
center_x = 200
center_y = 150
ellipse(center_x, center_y, 100, 100)
短いのは最初の例です。しかし 200 だけを見ても、それが位置なのか色なのか分かりません。center_x という名前は、その値が中心の横位置だと伝えます。
center_x は「センター・エックス」と読みます。center は中央、x は横の場所を表すために使っている文字です。center_y の y は縦の場所を表します。ここでは x を「横」、y を「縦」の短い名前として使います。
center_x = 200 は、右側の値 200 を、左側の変数 center_x に代入しています。代入とは、「この名前で値を覚えておく」という操作です。= は、ここでは数学の「左右が等しい」ではなく、右側の値を左側の名前へ入れる記号です。
2.2 一つを変えて全体を動かす
同じ中心から複数の形を描いてみます。
def setup
createCanvas(400, 300)
background(242)
center_x = 200
center_y = 150
fill(250, 100, 110)
ellipse(center_x, center_y, 180, 180)
fill(255)
ellipse(center_x - 35, center_y - 20, 30, 45)
ellipse(center_x + 35, center_y - 20, 30, 45)
fill(40)
ellipse(center_x - 35, center_y - 15, 12, 20)
ellipse(center_x + 35, center_y - 15, 12, 20)
end
center_x - 35 は中心から左へ 35、center_x + 35 は右へ 35 という意味です。center_x を 260 へ変えると、顔と両目がまとめて右へ動きます。
center_x - 30 と書くと、「中心から左へ 30」という位置がコードから分かります。
2.3 キャンバス自身に中心を聞く
幅 400 の半分は 200、高さ 300 の半分は 150 です。キャンバスの大きさを変えるたびに計算し直す代わりに、rbCanvas/p5 が持つ width(ウィズ、幅)と height(ハイト、高さ)を使えます。
def setup
createCanvas(600, 360)
background(242)
center_x = width / 2
center_y = height / 2
fill(250, 100, 110)
ellipse(center_x, center_y, 180, 180)
end
/(スラッシュ)は割り算の記号です。ここでは幅が 600 なので、width / 2 は「600 を 2 で割る」という計算で、答えは 300 です。高さの 360 も 2 で割ると 180 になります。
width は現在のキャンバスの幅、height は高さです。キャンバスを幅 600、高さ 360 に変えても、円は中央へ置かれます。
固定値 200 を使う方法が間違いなのではありません。キャンバスのサイズに関係なく中央へ置きたいなら、width / 2 のほうが意図に合います。左から必ず 200 の場所へ置きたいなら、固定値のほうが正確です。
2.4 座標は画面の左上から始まる
キャンバスの原点(位置を数え始める場所)は左上です。そこでは x と y がどちらも 0 です。x は右へ進むほど大きくなり、y は下へ進むほど大きくなります。
(0, 0) ─────────→ x
│
│
│
↓ y
紙のグラフでは y が上へ増えることが多いため、最初は逆に感じるかもしれません。画面は上から下へ行を並べて表示してきた歴史があり、ブラウザの 2D 座標も左上を基準にします。
座標を暗記する代わりに、次のコードを新しいスケッチとして実行します。図 2-2 と同じ 400×300 の画面で、左上だけに円が見えます。
def setup
createCanvas(400, 300)
background(242)
fill(255, 0, 0)
ellipse(0, 0, 120, 120)
end
左上に、大きな円を 4 分の 1 に切ったような形が見えます。円の中心が (0, 0) にあり、残りがキャンバスの外へ出ているためです。直径を 120 にしているのは、キャンバス内へ残った部分を見つけやすくするためです。
図のほとんどが何も描かれていない灰色に見えるのは、エラーではありません。円の中心を左上の角へ置いたため、直径 120 の円のうち、キャンバス内に入った右下部分だけが見えています。見えている円弧の角、つまり画像の左上が円の中心です。
2.5 名前はあとから変えられる
最初は x と書き、目の位置だと分かったところで eye_x へ変えられます。短い実験では x が読みやすく、要素が増えた作品では left_eye_x が役立つこともあります。
まず2.3 のコードを別名で保存します。center_x = width / 2 を center_x = width / 4 へ変えて実行すると、横の位置が 300 から 150 へ変わり、円が左へ動きます。
次に、同じ行を center_x = width * 0.75 へ変えて実行します。*(アスタリスク)は掛け算の記号です。600 * 0.75 は 600 の 4 分の 3 にあたる 450 なので、円は右へ動きます。中央へ戻すときは、その行を center_x = width / 2 へ戻します。
2.6 座標から夜の街を作る
座標は円の場所だけでなく、画面全体の構図を決めます。rect(レクト)は四角形、triangle(トライアングル)は三角形を描くメソッドです。
def setup
createCanvas(500, 300)
background(18, 24, 55)
noStroke
fill(245, 220, 120)
ellipse(410, 65, 70, 70)
fill(35, 45, 75)
rect(40, 130, 110, 170)
rect(175, 85, 130, 215)
rect(335, 155, 120, 145)
fill(255, 190, 80)
rect(65, 160, 22, 30)
rect(105, 210, 22, 30)
rect(205, 120, 25, 35)
rect(250, 175, 25, 35)
fill(35, 45, 75)
triangle(175, 85, 240, 30, 305, 85)
end
rect(x, y, width, height) は、既定では左上の位置、幅、高さの順です。triangle は 3 つの頂点を、x1, y1, x2, y2, x3, y3 の順で渡します。窓を一つ消すと眠っている建物に、月をビルの後ろへ移すと月が隠れた夜景に見えるかもしれません。どちらか一つを変えて、元の画面と見比べます。
座標を一つ変え、月や窓の見え方が気に入った版を保存します。第3章では、形を重ねた場所の色を変えます。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
width、height、rect、triangleの説明 - p5.js
width・height— キャンバスの幅と高さの概念。サンプルは JavaScript で書かれている
第3章 色と重なりで画面を作る
形の種類が少なくても、色と描画順が変われば別の画面になります。第1章の三日月は、背景と同じ色の楕円を重ねて作りました。この章では、色を「赤、青」と名付けるだけでなく、数値として混ぜ、透明度で重ねます。
3.1 RGB は 3 本の光の量
fill(235, 90, 120) の 3 つの値は、赤、緑、青の光の強さです。RGB は Red、Green、Blue の頭文字です。通常は 0 から 255 の範囲で指定します。
fill(40) や background(245) のように数字が一つだけなら、0 を黒、255 を白とする明るさを表します。途中の数字は灰色になります。
def setup
createCanvas(420, 180)
background(245)
noStroke
fill(255, 0, 0)
ellipse(100, 90, 100, 100)
fill(0, 255, 0)
ellipse(210, 90, 100, 100)
fill(0, 0, 255)
ellipse(320, 90, 100, 100)
end
noStroke は形の輪郭線を描かない設定です。赤だけが最大なら赤、緑だけなら緑、青だけなら青になります。3 つとも 255 なら白、3 つとも 0 なら黒です。
絵の具は混ぜるほど暗くなることがありますが、画面の光は加えるほど明るくなります。RGB を絵の具と同じ感覚で考えると、予想と違う色が出ます。その違いも、数値を動かして知るほうが早くつかめます。
3.2 透明度は色の 4 番目の値
fill に 4 番目の値を加えると、透明度を指定できます。0 は完全に透明、255 は完全に不透明です。
def setup
createCanvas(360, 280)
background(250)
noStroke
fill(255, 70, 90, 150)
ellipse(150, 120, 160, 160)
fill(40, 120, 255, 150)
ellipse(210, 120, 160, 160)
fill(250, 210, 40, 150)
ellipse(180, 175, 160, 160)
end
円が重なった場所には、どれか 1 色ではない色が現れます。透明度を使うと、どの形が重なっているかや、ブラウザで合成された場所の色を見せられます。
3.3 background を毎回塗る意味
この章では setup の中だけで描いているため、背景は 1 回塗れば足ります。第4章から draw を使うと、同じ画面を何度も描き直します。そのとき background をどこへ置くかで、前のフレームを消すか残すかが変わります。
今は、描画順だけを変えて比べます。次の変更部分を新しい setup の中へ入れ、先に createCanvas と background を実行してから試します。
fill(255, 70, 90, 150)
ellipse(150, 120, 160, 160)
fill(40, 120, 255, 150)
ellipse(210, 120, 160, 160)
赤の円と青の円の順番を入れ替えると、重なった場所の見え方がわずかに変わります。透明な色は、手前の色だけで決まらず、その下に何があるかにも影響されます。
3.4 色の役割へ変数名を付ける
背景色や主役の色へ変数名を付けると、どの役割の色を変えているか分かりやすくなります。ここでは第2章で使った変数だけを使い、赤、緑、青を別々に覚えます。
def setup
createCanvas(400, 300)
paper_red = 245
paper_green = 237
paper_blue = 220
ink_red = 35
ink_green = 48
ink_blue = 70
background(paper_red, paper_green, paper_blue)
fill(ink_red, ink_green, ink_blue)
noStroke
ellipse(width / 2, height / 2, 180, 180)
end
コードは長くなりましたが、paper_red を変えれば背景の赤成分、ink_blue を変えれば円の青成分だと名前から分かります。短い実験なら background(245, 237, 220) と直接書くほうが読みやすいこともあります。あとで変えたい数字だけに名前を付けます。
3.5 三色だけで作る制約
色を増やすほど、作品が面白くなるとは限りません。背景色、主役色、差し色の 3 色だけを選ぶと、どの色がどんな役割を持つか見えやすくなります。
- 背景を 1 色で塗る
- 同じ形を主役色で 3 つ以上描く
- そのうち 1 つだけ差し色へ変える
- 位置か大きさを一つだけ崩す
使う色を三つに絞ると、一つずつ変化を比べやすくなります。窮屈に感じたときは、4 色目を足した画面とも比べられます。
3.6 色を選んで小さなポスターを作る
色の練習を、色見本で終わらせず、一枚のポスターへまとめます。
def setup
createCanvas(420, 300)
background(245, 225, 185)
noStroke
fill(235, 75, 90)
rect(35, 35, 210, 230)
fill(45, 70, 120, 210)
ellipse(270, 115, 190, 190)
fill(255, 210, 70, 220)
triangle(170, 260, 390, 260, 330, 70)
end
ここでは形の意味を決めていません。建物、夕日、旗に見える場合も、ただの色面に見える場合もあります。まず背景、主役、差し色のどれか一色だけを変えます。その後、形の重なる順番を変えます。「好きな色」だけでなく、「隣の色によってどう見え方が変わったか」を観察します。
色の見え方や、区別しやすい組み合わせは人によって違います。主役だけ大きくする、輪郭線を足す、位置を離す、動きを付けるなど、色以外の手掛かりも重ねられます。この章は色名を正しく当てる課題ではなく、自分に見える関係を試す章です。
3.7 リファレンスで確かめ、次の形を見つける
リファレンスには、命令の名前、引数、短いサンプルが載っています。忘れた使い方を確かめるだけでなく、まだ知らない命令を見つけるためにも使えます。
rbCanvas/p5 0.5.1 API リファレンスを開き、ここまでに使った ellipse を探します。本書の ellipse(200, 150, 120, 80) と見比べると、最初の二つの数字が位置、後ろの二つが幅と高さだと確認できます。知っているコードを手掛かりにすると、リファレンスのどこに命令の書き方やサンプルが載っているか分かります。
ellipse を確認したら、形を扱う項目に戻り、まだ使っていない名前を眺めます。名前やサンプルの実行結果が気になったものがあれば、一つだけ開いてみます。試したくなった場合は、作りかけの作品を保存してから、別のブラウザタブで rbCanvas/p5 エディタを開き、掲載されたサンプルを実行します。
第1部で、形、位置、色を数値として扱いました。第2部では、その数値を時間とともに変え、静止画を動かします。
参考資料
- rbCanvas/p5 0.5.1「色(カラー)に関する情報」 — RGB と透明度の指定
- HTML Standard「Canvas compositing」 — Canvas に描く色や画像のアルファ合成
第2部 時間と動きを設計する
静止画を何度も少しずつ描き直すと、動きが生まれます。その間に条件や反復を入れると、形は画面の端で戻り、群れになり、揺れ始めます。
第2部で扱うのは、アニメーションの技術だけではありません。「今の値を覚え、次の瞬間に少し変える」というプログラムの基本です。まず動きを見て、画面の変化に使われている数式をあとから確かめます。
第4章 draw が静止画を動かす
アニメーションは、円そのものが動いているわけではありません。少し違う静止画を短い間隔で描き直し、目が動きとして受け取っています。rbCanvas/p5 の draw は、この描き直しを自動で繰り返します。
4.1 setup の 1 回と draw の反復
次のコードを実行すると、円が右へ進みます。
def setup
createCanvas(480, 240)
@x = 0
end
def draw
background(245)
fill(235, 90, 120)
ellipse(@x, height / 2, 60, 60)
@x += 2
end
setup は最初に 1 回、draw はそのあと繰り返し呼ばれます。rbCanvas/p5 の公式資料では、既定の draw は 1 秒あたり 60 回です。端末の性能や処理内容により、実際の間隔は変わることがあります。
@x += 2 は、現在の @x に 2 を足し、その結果を再び @x へ代入する省略記法です。
4.2 なぜ @x には @ が付くのか
次のように setup で普通のローカル変数 x を作っても、draw からは使えません。
def setup
x = 0
end
def draw
ellipse(x, 100, 60, 60)
end
ローカル変数は、そのメソッドの中だけで使える値です。setup が終わると、draw は x を見つけられません。
@x はインスタンス変数です。同じ実行中の別のメソッドからも参照できるため、setup で用意した位置を draw が読み、更新できます。アニメーションには「前のフレームの値を次のフレームまで覚える」必要があり、インスタンス変数がその記憶になります。
4.3 background が軌跡を消している
次の draw は変更部分です。4.1 の長いコードにある def draw から対応する end までと置き換え、background(245) をコメントにした結果を比べます。
def draw
# background(245)
fill(235, 90, 120)
ellipse(@x, height / 2, 60, 60)
@x += 2
end
円の軌跡が帯のように残ります。円が伸びたのではなく、前のフレームを消さず、その上へ新しい円を描き足しています。
どちらが正しいかは、作りたいものによります。物体の輪郭をはっきり動かすなら、毎回背景を描き直します。筆跡や時間の蓄積を見せるなら、背景を最初の setup だけで描く方法があります。
def setup
createCanvas(480, 240)
background(245)
@x = 0
end
def draw
fill(235, 90, 120, 35)
noStroke
ellipse(@x, height / 2, 60, 60)
@x += 2
end
透明度を下げたため、重なった場所ほど濃くなります。前のフレームの円が消えずに残るため、円が通った場所を軌跡として見られます。
4.4 位置と速度を分ける
@x += 2 の 2 は、1 フレームで進む距離です。これを速度として別にすると、動きの調整箇所が明確になります。
def setup
createCanvas(480, 240)
@x = 0
@speed = 2
end
def draw
background(245)
ellipse(@x, height / 2, 60, 60)
@x += @speed
end
@speed を -2 にすると左へ動きます。-2 は 0 より小さい数で、「マイナス 2」と読みます。ここでは 1 回の描き直しごとに x を 2 小さくするため、円が左へ進みます。2 と -2 のプラス・マイナスが、進む方向の違いになります。
4.5 画面から消えることも結果
しばらくすると円は右端から消えます。プログラムが止まったのではありません。@x は増え続け、円がキャンバスの外で描かれています。
このまま軌跡を作るのも一つの作品です。端で戻したい場合は、位置に応じて処理を変える必要があります。第5章では、その分岐を if で書きます。
4.6 動く円を流れ星へ変える
円の前に線を加えると、同じ横移動が流れ星に見えてきます。line(ライン)は、始点の x と y、終点の x と y を受け取り、その 2 点を直線で結びます。
def setup
createCanvas(520, 280)
@x = -80
end
def draw
background(15, 20, 45)
stroke(255, 210, 100)
strokeWeight(5)
line(@x - 90, 110, @x, 150)
noStroke
fill(255, 235, 150)
ellipse(@x, 150, 24, 24)
@x += 4
end
stroke(ストローク)は線の色、strokeWeight(ストローク・ウェイト)は線の太さを決めます。星を速くする、尾を長くする、斜め上へ進ませる、のどれか一つから改造できます。線と円が画面を横切ることで、流れ星が現れて消える動きを作れます。
円が画面の端へ着いたら、次はどう動いてほしいでしょうか。今の速度を残しておくと、第5章で戻る動きと跳ね返る動きを比べられます。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
draw、line、stroke、strokeWeightの説明 - p5.js
draw—drawが繰り返し呼ばれる仕組み。サンプルは JavaScript で書かれている
第5章 条件分岐が振る舞いを生む
第4章の円は、画面の外へ出ても進み続けました。端に着いたときだけ向きを変えるには、「もし端を越えたら」という条件が要ります。if は、値を見て次の行動を選ぶ仕組みです。
5.1 右端を越えたら左へ戻す
前章のコードへ 3 行を足します。
def setup
createCanvas(480, 240)
@x = 0
@speed = 3
end
def draw
background(245)
fill(235, 90, 120)
ellipse(@x, height / 2, 60, 60)
@x += @speed
if @x > width
@x = 0
end
end
@x > width は、円の中心がキャンバスの幅より右へ進んだかを比べています。条件が成り立つときだけ、@x = 0 が実行されます。
円の中心が右端を越えた直後に左へ戻るため、円の左半分がまだ画面内に見えるところで消えます。円が完全に画面外へ出てから戻したいなら、直前のコードの draw にある if 3 行を、次の変更部分と置き換えます。
radius = 30
if @x - radius > width
@x = -radius
end
形の中心だけでなく大きさも考えると、画面の外へ出た瞬間をより正確に表せます。
5.2 戻る動きと跳ね返る動き
左へ戻す代わりに、直前のコードの draw にある if を次の変更部分と置き換え、速度の符号を反転します。
if @x > width - 30
@speed *= -1
end
@speed *= -1 は、今の速度へ -1 を掛け、その結果を同じ変数へ入れる書き方です。右端で 3 は -3 になり、円は左へ進みます。しかし左端の条件がないため、そのまま消えます。
両端で跳ね返す前に、setup の @x = 0 を @x = 30 へ変えて実行します。半径が 30 の円が、左端に収まった位置から右へ出発します。初期位置を 0 のままにすると、次に足す左端の条件を最初から満たし、その場で向きが反転し続けてしまいます。
出発位置を確認したら、今入れた if 3 行を、次の変更部分と置き換えて両端を一つの条件にまとめます。
if @x > width - 30 || @x < 30
@speed *= -1
end
|| は「または」です。右端を越えた場合、または左端を越えた場合に速度を反転します。実行して、右端で左向きに、左端で右向きに変わるところまで見ます。左端で小さく揺れるだけなら、setup が @x = 30 になっているか確かめます。エラーが出たら、if をこの節の最初の 3 行へ戻して実行します。
5.3 条件だけでは端へめり込むことがある
速度を 25 のように大きくすると、円が端を越えてから反転し、少し不自然に揺れることがあります。1 フレームで境界を大きく飛び越えるためです。
直前の if 3 行を次の変更部分と置き換え、境界で位置を戻してから速度を反転すると安定します。
if @x > width - 30
@x = width - 30
@speed *= -1
elsif @x < 30
@x = 30
@speed *= -1
end
elsif は、最初の条件が成り立たなかった場合に、別の条件を調べます。右端と左端を別々に処理できました。
条件分岐を使うと、端を越えたときに戻る、跳ねる、消える、色が変わるといった振る舞いを選べます。
5.4 状態を色で見えるようにする
進む方向に応じて色を変えると、見えない速度の符号を観察できます。次の変更部分は、draw にある fill と ellipse の 2 行と置き換えます。
if @speed > 0
fill(235, 90, 120)
else
fill(50, 130, 230)
end
ellipse(@x, height / 2, 60, 60)
else は、それまでの条件がどれも成り立たなかった場合です。右向きなら赤、左向きなら青になります。
デバッグでは、内部の値を色や文字へ変えて表示すると原因を探しやすくなります。作品として残さなくても、見えない状態を一時的に見えるようにする考え方は今後も使います。
戻る動きと跳ね返る動きを同時に見る
二つの境界処理を同じ画面へ置くと、違いを見失いにくくなります。次はそれだけで実行できる完成コードです。上段の赤い円は右端を越えると左から現れ、下段の青い円は端で向きを変えます。
def setup
createCanvas(600, 260)
@wrap_x = 540
@bounce_x = 540
@bounce_speed = 4
end
def draw
background(245)
@wrap_x += 4
if @wrap_x > width + 30
@wrap_x = -30
end
@bounce_x += @bounce_speed
if @bounce_x > width - 30
@bounce_x = width - 30
@bounce_speed *= -1
elsif @bounce_x < 30
@bounce_x = 30
@bounce_speed *= -1
end
stroke(190)
line(0, 130, width, 130)
noStroke
fill(235, 90, 120)
ellipse(@wrap_x, 70, 60, 60)
fill(50, 130, 230)
ellipse(@bounce_x, 190, 60, 60)
end
どちらも同じ速さで右へ出発します。しかし、赤い円は位置を反対側へ移し、青い円は速度の正負を変えます。図では、赤い円が左へ戻ったあと、青い円が右端で跳ね返って左へ進んでいます。
5.5 自分で選ぶ境界の反応
次のうち一つだけ選び、円の端での反応を変えてみてください。難しさを三段階に分けています。
- そのままでできる: 跳ね返らず、反対側から現れる。5.1 のコードを使える
- ヒントあり: 端に触れるたび速くする。跳ね返す
ifの中へ@speed *= 1.1を足す - 挑戦: 端に触れるたび色を変える。
setupで色の変数を用意し、端のifで二つの色を交互に入れる - 挑戦: 端でしばらく止める。止まる残り時間を覚える変数が必要になるため、今は動きの案だけメモして先へ進んでもよい
まずは「そのままでできる」案を試します。ヒントの意味がまだ分からなければ、数字を変える案へ戻ります。条件を一つ加えると、円が端で見せる動きも変わります。
5.6 跳ね返るものに性格を付ける
端へ触れたときの反応で、登場するものの性格を表せます。たとえば、跳ね返る瞬間だけ目を大きくすると、円が驚いたように見えます。この変更には、目を大きくしておく残り時間を覚える変数が必要です。コードへ加えない場合は、変化する目の形を紙に描いて残せます。端で速度を半分にすると、今度は壁を警戒しているような動きになります。
最初は円の中へ目を 2 つ足します。「速い」「慎重」「端を無視して反対側へ抜ける」から、今の動きに合うものを選びます。
5.7 もっと自然な動きを探すなら
位置と速度に加えて、速度そのものを少しずつ変える値を持たせると、落下や加速のような動きになります。この値を加速度と呼びます。ほかにも、摩擦、重力、ばね、複数のものが集まって動く群れなど、動きから広がる題材があります。
「だんだん速くなる」「止まる前に少し滑る」「壁から離れるほど戻る力が強くなる」のうち、作りたい動きを言葉や矢印で描きます。その動きをコードにしたくなったときは、「加速度」「摩擦」「ばね」「ベクトル」が調べる手掛かりになります。
戻る版と跳ね返る版から、残したい動きを一つ保存します。次章では、同じ形を繰り返して模様を作ります。
参考資料
- Ruby 公式「制御構造」 —
if、elsif、elseによる条件分岐 - rbCanvas/p5 0.5.1 API リファレンス —
widthと図形の位置・大きさに使う機能
第6章 反復は同じ形を別の景色に変える
円を 10 個描くために ellipse を 10 行書くこともできます。位置が少しずつ変わるなら、その違いを規則として書けます。反復回数や間隔を変えたり、一つだけ色を変えたりすると、同じ図形から異なる模様を作れます。
6.1 5 回描く times
まず、同じ場所へ円を 5 回描きます。
def setup
createCanvas(500, 220)
background(245)
5.times do
ellipse(100, 110, 60, 60)
end
end
見える円は一つです。同じ場所へ完全に重ねたからです。反復回数は増えましたが、位置が変わっていません。
times(タイムズ)は、何回目の反復かをブロック変数へ渡せます。直前の長いコードにある 5.times do から対応する end までを、次の変更部分と置き換えます。
5.times do |i|
x = 70 + i * 90
ellipse(x, 110, 60, 60)
end
初めて見る記号がいくつかあります。左から順に読みます。
5.timesは、その後の処理を 5 回繰り返しますdoは、繰り返す処理の始まりです|i|は、今が何回目かを受け取るための書き方です。縦線 2 本の間に変数名を書きます- 最後の
endは、繰り返す範囲の終わりです
i は 0、1、2、3、4 と変わります。プログラムでは番号を 0 から数える場面が多くあります。5 回繰り返しても最後が 4 なのは、最初が 0 だからです。
最初の x は 70 + 0 * 90 なので 70、次は 70 + 1 * 90 なので 160 です。* は掛け算を表します。反復回数を 8 へ変えるだけで、同じ間隔の円が増えます。
6.2 行と列を重ねる
横方向の反復を、縦方向の反復の内側へ置きます。
def setup
createCanvas(480, 360)
background(28, 32, 50)
noStroke
4.times do |row|
6.times do |column|
x = 50 + column * 75
y = 55 + row * 85
fill(240, 180, 70)
ellipse(x, y, 44, 44)
end
end
end
外側の row が 1 回進む間に、内側の column が 0 から 5 まで進みます。4 行 × 6 列なので、円は 24 個です。
反復を重ねるとコードは短くなりますが、最初は動きを追いにくくなります。行数を 2、列数を 3 へ減らすと、どの順で描かれるか想像しやすくなります。
6.3 規則の中の一つだけを変える
すべて同じだと、画面は安定します。直前の長いコードの二重反復部分を、次の変更部分と置き換え、条件分岐で一つだけ色を変えます。
4.times do |row|
6.times do |column|
x = 50 + column * 75
y = 55 + row * 85
if row == 1 && column == 3
fill(235, 80, 120)
else
fill(240, 180, 70)
end
ellipse(x, y, 44, 44)
end
end
== は値が等しいかを比べ、&& は「かつ」です。2 行目であり、かつ 4 列目の円だけが別の色になります。ブロック変数は 0 から始まるため、row == 1 が 2 行目です。
6.4 同じ間隔からずれを作る
列ごとに y 座標を少し変えると、格子が波打って見えます。二重反復の中にある y = 55 + row * 85 の 1 行だけを、次の 1 行と置き換えます。
y = 55 + row * 85 + (column % 2) * 20
% は余りを求める演算子です。column % 2 は、column が偶数なら 0、奇数なら 1 になります。番号が奇数の列だけ 20 下がるため、互い違いの配置になります。
さらに ellipse の大きさへ row や column を使えば、左から右、上から下への変化を作れます。二重反復の中にある ellipse(x, y, 44, 44) の 1 行を、次の変更部分と置き換えます。
size = 24 + column * 6
ellipse(x, y, size, size)
反復の番号を位置、色、大きさへ使うと、同じ図形の並び方を変えられます。
6.5 同じ反復から三つの作品を選ぶ
今の行と列のコードは、形を変えるだけで別の風景になります。
ellipseの色を緑にし、円の大きさを行ごとに変えると植物園rectへ変え、黄色を一部だけ灯すと夜の窓rectの横幅を細くし、列ごとに高さを変えると街の輪郭
三つの案から一つ選び、まず図形と背景色だけを変えてみましょう。そのあと、一個だけ色か大きさを変え、最初に目へ入る図形を作ります。どの位置の図形を変えると目立つかも比べられます。
6.6 発展:形の中へ同じ形を描く
大きな円の中へ半分の円を描き、その円の中へさらに半分の円を描くと、同じ規則が小さくなりながら続く模様ができます。このように、全体と似た形が一部分にも現れる構造をフラクタルと呼びます。
def setup
createCanvas(500, 320)
background(20, 25, 45)
noStroke
draw_circles(width / 2, height / 2, 240)
end
def draw_circles(x, y, size)
return if size < 12
fill(120 + size / 3, 100, 210, 150)
ellipse(x, y, size, size)
next_size = size / 2
draw_circles(x - size / 4, y, next_size)
draw_circles(x + size / 4, y, next_size)
end
draw_circles の中から、もう一度 draw_circles を呼んでいます。自分自身を呼び出す方法を再帰と呼びます。return if size < 12 は、円が 12 より小さくなったら、その呼び出しをそこで終える条件です。この条件がないと、いつまでも小さくしようとして処理を終えられません。
この発展例は、同じ形の中へ形を描くときの変化を画面から確かめます。まずそのまま実行し、setup の 240 だけを 180 へ変えて実行します。模様全体が小さくなるか見たら、240 へ戻して実行します。次に、return if size < 12 の 12 だけを 24 へ変えて実行し、小さい円の数が減るか見ます。エラーが出たら、変更した数字を元へ戻して実行します。その次に、円を四角形へ変える、左右ではなく上下へ分ける、色を大きさから決める、といった改造へ進めます。
格子模様とフラクタルでは、同じ反復でも違う画面になりました。残したいほうへ短い名前を付けたら、第7章で規則的な動きと予想できない変化を比べます。
参考資料
- Ruby 公式
Integer#times— 回数を決めた反復 - Ruby 公式「制御構造」 — ブロック、
return、条件分岐
第7章 周期と偶然のあいだ
機械的に同じ速さで動く円は、動きの仕組みを知るには分かりやすい例です。しかし呼吸、波、風のような動きは、行って戻る変化や、完全には予測できない揺れを含みます。周期と乱数は、その 2 種類の変化を作ります。
7.1 sin が往復運動を返す
sin(サイン)は、三角関数と呼ばれる計算の一つです。この本では、公式も計算方法も覚えません。数字を少しずつ増やして sin へ渡すと、答えが -1 から 1 の間をなめらかに行き来することだけを、画面の動きに使います。-1 は「マイナス 1」と読み、0 より小さい数です。
def setup
createCanvas(500, 240)
end
def draw
background(245)
wave = sin(frameCount * 0.04)
x = width / 2 + wave * 180
fill(235, 90, 120)
ellipse(x, height / 2, 60, 60)
end
frameCount(フレーム・カウント)は、draw が何回呼ばれたかを表す値です。1、2、3 と増えていく番号へ 0.04 を掛け、sin へ渡します。戻ってくる wave は -1 から 1 の間なので、180 を掛けると -180 から 180 の範囲になります。
まず 0.04 を 0.02 へ変えて実行してみましょう。動きが遅くなれば、数字と速さの関係が画面に現れています。次に元へ戻し、180 を 80 へ変えると、左右へ動く幅が小さくなります。
0.04 を小さくするとゆっくり、大きくすると速く往復します。180 は移動幅です。速度と振れ幅を別の数字で調整できる点が重要です。
7.2 同じ周期を大きさへ使う
同じ wave を位置ではなく大きさへ使います。次の変更部分を、7.1 の長いコードにある draw の中身と置き換えます。
background(245)
wave = sin(frameCount * 0.04)
size = 100 + wave * 40
fill(235, 90, 120)
ellipse(width / 2, height / 2, size, size)
中央の赤い円が、大きくなったり小さくなったりします。background(245) が前の円を消すため、今の大きさだけが見えます。大きさは 60 から 140 の間を往復します。wave をそのまま大きさにすると負の値になる時間があります。中心となる 100 を足すことで、扱いやすい範囲へ移しています。
まず 40 を 20 へ変えて実行し、大きさの変化が小さくなるか見ます。元へ戻すときは 40 に戻します。エラーになったときは、draw の中身を上の 5 行へ戻して実行します。
周期の式は、横移動、透明度、色、回転にも使えます。同じ wave を位置と大きさの両方に使うと、二つが同じ速さで繰り返し変化します。別の速さを使うと、変化する時期が少しずつずれます。
7.3 random は毎回別の値を選ぶ
random(400) は、0 以上 400 未満の範囲から値を返します。draw のたびに位置を選ぶと、円が激しく飛びます。
def setup
createCanvas(500, 300)
background(20)
noStroke
end
def draw
x = random(width)
y = random(height)
size = random(4, 28)
fill(255, 220, 120, 120)
ellipse(x, y, size, size)
end
背景を setup に置いたため、点が消えずに積もります。実行するたび別の星空になります。
位置、色、大きさをすべて乱数にすると、どこを見る画面なのか分かりにくくなることがあります。たとえば大きさを揃え、位置だけを乱数にすると、二つの役割を分けて調整できます。
7.4 noise は隣り合う値がつながる
random(ランダム)は呼ぶたび大きく飛べます。noise は、近い入力へ近い値を返すため、連続した揺れを作りやすい機能です。
def setup
createCanvas(500, 240)
end
def draw
background(245)
value = noise(frameCount * 0.01)
x = value * width
fill(60, 130, 220)
ellipse(x, height / 2, 60, 60)
end
noise(ノイズ)の戻り値は通常 0 から 1 の範囲です。width(ウィズ、キャンバスの幅)を掛けると、キャンバスの左端から右端の範囲へ変換できます。
二つの変化を横へ並べる代わりに、上段と下段へ点として記録すると形を比べられます。次はそれだけで実行できる完成コードです。
def setup
createCanvas(600, 300)
background(245)
noStroke
120.times do |i|
x = 30 + i * 4.5
wave_y = 80 + sin(i * 0.18) * 35
fill(235, 90, 120)
ellipse(x, wave_y, 6, 6)
noise_y = 220 + (noise(i * 0.08) - 0.5) * 90
fill(50, 130, 230)
ellipse(x, noise_y, 6, 6)
end
end
上段の赤い点は、同じ波の形を繰り返します。下段の青い点は滑らかにつながっていますが、次の山や谷の大きさは一定ではありません。
7.5 偶然を保存する
乱数を使った作品で気に入った結果が出たら、その画面を保存する方法があります。再現できることも重要なら、randomSeed(ランダム・シード)で乱数の並びを固定できます。
def setup
createCanvas(500, 300)
randomSeed(42)
background(20)
100.times do
fill(255, 220, 120, 140)
ellipse(random(width), random(height), random(4, 24), random(4, 24))
end
end
同じシード値 42 なら、同じ乱数列を得られます。偶然に見える構図を選び、あとから同じ版を作れるようになります。
7.6 波と偶然を同じ海へ置く
sin で規則的に上下する線を作り、noise で少しだけ位置を揺らすと、完全な規則でも完全な乱数でもない水面を作れます。さらに random で小さな光を散らすと、波、風、反射という別々の役割を同じ画面へ置けます。
ここでは完成コードを一つに決めません。星空のコードを青い背景へ変えて水面にする、往復する円を船に見立てて三角形を足す、noise の動きを雲へ使う、のどれかから始めます。同じ数の変化が、何に見立てるかで別作品になることを試す節です。
7.7 模様をもっと探すなら
第6章の行数と列数、第7章の sin、noise、random を組み合わせると、同じコードから規則的な模様と崩れた模様を作り分けられます。まず一つだけ選びます。
- 行と列の数を増やし、遠くから見た印象を比べる
sinを位置や大きさへ使い、並びを波打たせるnoiseを色へ使い、少しずつ変わる帯を作る- 第6章のフラクタルで、円を四角や枝へ変える
さらに探したくなったら、「セル・オートマトン」「フラクタル」「ジェネラティブアート」という言葉を、画像やp5.js の作品例から調べられます。Genuary では、毎年 1 月にクリエイティブコーディングの制作テーマが公開されます。見つけた作品を全部まねるのではなく、気になった形の並び方や動き方を一つ選び、自分のスケッチで短く試します。
第3章で ellipse を探したときと同じように、rbCanvas/p5 0.5.1 API リファレンスで sin、noise、random のどれか一つを探せます。まず掲載されたサンプルを別のタブで開いたエディタで実行し、数字を一つだけ変えます。本書のコードと見比べると、その数字が速さ、揺れ方、値の範囲のどれに関係するかを調べられます。
どの動きをもう一度見たいか決め、その数字をメモします。第3部では、マウスとキーボードから受け取った値で画面を変えます。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
sin、noise、random、randomSeedの説明 - p5.js
noise— 近い入力から滑らかに変化する値を得る考え方。サンプルは JavaScript で書かれている - p5.js
randomSeed— 乱数列を再現するためのシード
第3部 人の操作に応答する
マウスやキーボードから値を受け取ると、見るだけだった画面を操作で変えられます。
ここでいう「人」には、作った本人も含まれます。時間を置いて触り直すと、操作が分かるか、反応が気持ちよいかを自分で確かめられます。ほかの人に試してもらう方法もありますが、この部の確認は一人で完結します。
第8章 マウスの位置を画材にする
マウスの横位置を色へ、縦位置を大きさへ変換します。動かす手の位置が、そのまま作品の入力になります。
8.1 mouseX と mouseY は毎フレーム変わる
次のスケッチでは、円がマウスポインタを追いかけます。
def setup
createCanvas(500, 320)
end
def draw
background(245)
fill(235, 90, 120)
ellipse(mouseX, mouseY, 60, 60)
end
mouseX(マウス・エックス)はキャンバス内の横位置、mouseY(マウス・ワイ)は縦位置です。draw のたびに現在値を読み直すため、円が追従して見えます。
マウスがキャンバス外にある場合も値を持つことがあります。作品の操作をキャンバス内に限定したいなら、後で境界条件を加えます。
8.2 位置をそのまま使わない変換
横位置を円の大きさへ渡します。次の draw は変更部分です。8.1 の長いコードにある def draw から対応する end までと置き換えます。
def draw
background(245)
size = mouseX
ellipse(width / 2, height / 2, size, size)
end
キャンバスの幅が 500 なら、大きさもおよそ 0 から 500 まで変わります。大きすぎる場合は、size = mouseX の 1 行を次の変更部分と置き換え、値の範囲を変換する map(マップ)を使えます。
size = map(mouseX, 0, width, 20, 180)
これは、0 から width の範囲を、20 から 180 の範囲へ対応させます。入力と出力の範囲を分けると、操作の感度を調整できます。
8.3 軌跡を描くか、現在位置だけを見せるか
background を setup へ移すと、マウスの軌跡が残ります。
def setup
createCanvas(500, 320)
background(24, 28, 45)
noStroke
end
def draw
red_amount = map(mouseX, 0, width, 80, 255)
blue_amount = map(mouseY, 0, height, 255, 80)
fill(red_amount, 120, blue_amount, 50)
ellipse(mouseX, mouseY, 30, 30)
end
横に動かすと赤の量が、縦に動かすと青の量が変わります。位置を色へ変換しているため、軌跡から手の動きを読み取れます。
8.4 押している間だけ描く
mouseIsPressed(マウス・イズ・プレスド)は、マウスボタンが押されているかを真偽値(しんぎち)で表します。真偽値は、条件が成り立つことを表す true(トゥルー)と、成り立たないことを表す false(フォルス)の二つの値です。ここでは押している間が true、放している間が false です。次の draw は変更部分です。8.3 の長いコードにある def draw から対応する end までと置き換えます。
def draw
if mouseIsPressed
fill(255, 200, 80, 80)
ellipse(mouseX, mouseY, 36, 36)
end
end
このコードでは、押している間だけ円を描きます。クリックした瞬間だけ処理したい場合と、押し続けている間処理したい場合は別です。連続した線を描く道具では、押している状態を毎フレーム読む方法が合います。
8.5 一人で操作を確かめる
操作と画面の反応を確かめるために、第三者は必須ではありません。
- 操作説明を別の場所へメモする
- コードを HTML として保存する。描いた軌跡も残したい場合は、画面を画像として残す操作をしてから閉じる
- 数時間後または翌日に開く
- 説明を見る前に触ってみる
- 最初に試した操作と、迷った場所を一つずつ記録する
時間を置くと、作った直後には気づかなかった操作の迷いやすさを見つけられます。
8.6 自分のブラシを発明する
マウスの位置へ円を置く代わりに、短い線、細長い四角形、三角形を描けば、筆跡の性格が変わります。次の draw も変更部分で、8.3 の長いコードの draw 全体と置き換えて試します。
def draw
if mouseIsPressed
stroke(255, 190, 80, 90)
strokeWeight(6)
line(mouseX - 18, mouseY + 12, mouseX + 18, mouseY - 12)
end
end
このブラシは、マウスの場所を中心に斜めの線を置き続けます。線の傾き、長さ、透明度のどれか一つを変えてください。次に、押していない間だけ小さな点を描く、画面上部では細く下部では太くする、といった操作と見た目の関係を考えられます。
描き終えたら、線が密集した場所と、何も描かなかった余白を見ます。描かなかった場所も画面の一部です。速く動かした場所、ゆっくり動かした場所、止まった場所の違いが、線のリズムになります。
操作を加えたら、細かなマウス操作が難しい場合にも同じ変化が必要か、クリックしなくても一部を見られるか、操作方法を画面へ書くかを確認します。変えやすいところから一つ直すと、作品を操作できる方法が増えます。
線の長さか傾きを変え、手の動きと筆跡の違いを確認します。次章では、キーボードで場面を切り替えましょう。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
mouseX、mouseY、mouseIsPressed、mapの説明 - p5.js
mouseX・mouseY— ポインター座標の概念。サンプルは JavaScript で書かれている
第9章 キーボードと状態の切り替え
マウスは連続した位置を渡します。キーボードは、押されたキーに応じて「夜へ変える」「動きを止める」「色を選ぶ」といった切り替えに向いています。ここではキー入力を、作品の状態を変える合図として使います。
9.1 矢印キーで位置を変える
keyIsDown(キー・イズ・ダウン)を使うと、指定したキーが現在押されているかを調べられます。
def setup
createCanvas(500, 320)
@x = width / 2
@y = height / 2
end
def draw
@x -= 3 if keyIsDown(LEFT_ARROW)
@x += 3 if keyIsDown(RIGHT_ARROW)
@y -= 3 if keyIsDown(UP_ARROW)
@y += 3 if keyIsDown(DOWN_ARROW)
background(245)
fill(235, 90, 120)
ellipse(@x, @y, 60, 60)
end
if を行末へ置く書き方は、Ruby の後置 if です。条件が短く、実行する処理も 1 行なら、動きの対応が見やすくなります。複雑な条件では通常の複数行の if のほうが読みやすくなります。
9.2 押している状態と押した瞬間
移動では、押している間ずっと位置を変えたいので keyIsDown が合います。一度だけ色を切り替える処理を毎フレーム実行すると、押している短い間に何度も切り替わることがあります。
rbCanvas/p5 では、キーが押されたときに呼ばれるイベントメソッドを定義できます。
def setup
createCanvas(500, 320)
@dark = false
end
def draw
if @dark
background(20, 25, 40)
fill(255, 220, 120)
else
background(240, 230, 200)
fill(235, 90, 120)
end
ellipse(width / 2, height / 2, 100, 100)
end
def keyPressed
@dark = !@dark if key == " "
end
実行結果のキャンバスを一度クリックしてから、スペースキーを押します。押すたびに、明るい背景と暗い背景が切り替わります。@dark は「今は暗い場面か」を覚えており、false なら明るい場面、true なら暗い場面です。! はこの二つの値を入れ替えます。
key(キー)は押されたキーに対応する文字を返します。" " のように文字を半角の "(ダブルクォート)で囲んだものを文字列(もじれつ)と呼びます。ここでは、二つの " の間に半角スペースが一つ入っています。key == " " は「押されたのはスペースキーか」を比べています。エラーになったら、keyPressed の中の行を上のコードへ戻し、スペースと左右の " が残っているか確かめます。
keyPressed(キー・プレスド)は、キーを押したときに呼ばれるメソッドです。0.5.1 ではkey_pressedという書き方でも呼ばれますが、本書では公式 API 一覧に合わせてkeyPressedに統一します。途中のPは大文字です。
9.3 状態は画面の現在を説明する
@dark は背景色そのものを保存していません。「今は暗い場面か」という意味を保存しています。描画側は、その状態から背景色と円の色を決めます。
色を直接何度も書き換える方法もあります。しかし要素が増えると、背景は夜なのに太陽だけ昼の色という、ちぐはぐな状態が起こりやすくなります。状態を一つ持ち、すべての描画をその状態から決めると関係を保てます。
9.4 3 つ以上ならシンボルで名前を付ける
昼と夜の 2 状態は真偽値で表せます。朝、昼、夜の 3 状態になると、true と false では足りません。Ruby のシンボルで名前を付けます。シンボルは :morning のように、名前の先頭へ半角の :(コロン)を付けた値です。ここでは場面を区別する名前として使います。
def setup
createCanvas(500, 320)
@scene = :morning
end
def draw
case @scene
when :morning
background(250, 180, 150)
fill(255, 235, 190)
when :day
background(120, 200, 250)
fill(255, 220, 80)
when :night
background(20, 25, 50)
fill(255, 220, 120)
end
ellipse(width / 2, height / 2, 100, 100)
end
def keyPressed
@scene = :morning if key == "1"
@scene = :day if key == "2"
@scene = :night if key == "3"
end
case(ケース)は、@scene の値に合う when だけを実行します。背景と円の色を同じ状態から決めているため、「夜の背景なのに昼の円」というずれが起こりません。実行したら、数字の 1、2、3 を順に押して画面の変化を確かめます。
状態へ名前を付けると、morning、day、night のどの場面を描いているか、コードから読み取れます。
図 9-1 と図 9-2 を見比べると、円の位置と大きさは同じまま、状態から選ぶ色だけが変わっています。
9.5 キーで世界の季節を変える
状態は背景色だけでなく、同じ風景の読み方をまとめて変えられます。1 を春、2 を夏、3 を夜として、背景、地面、空に浮かぶものを場面ごとに選びます。
- 春では小さなピンクの楕円を散らす
- 夏では黄色い円と濃い影を置く
- 夜では月と窓の明かりだけを残す
最初は図形の位置を共通にし、色だけを切り替えます。その後、一つの場面にだけ図形を足すと、同じ場所の時間や天候が変わったように見せられます。
昼と夜のどちらを最初の場面にするか決めて保存します。次章では、文字も画面を構成する形として動かします。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
keyIsDown、keyPressed、矢印キー定数の説明 - Ruby 公式「リテラル」 —
:morningなどのシンボル
第10章 文字は読める図形
画面の文字には 2 つの顔があります。言葉として意味を伝え、同時に大きさ、間隔、配置を持つ図形として見えます。操作説明を表示するだけなら読めることが最優先です。短い詩なら、文字を動かしたり重ねたりして、意味と形の両方を使った表現を試せます。
10.1 text で一行を置く
エディタのコード全体を次のコードに置き換えて実行します。
def setup
createCanvas(600, 300)
background(245)
fill(35)
textSize(36)
text("Ruby で描く", 60, 150)
end
画面に「Ruby で描く」と表示されます。"Ruby で描く" は、表示したい文字をひとまとまりにした文字列です。半角の " は文字列の始まりと終わりを示す目印で、画面には表示されません。まず左右の " を残したまま、中の言葉だけを「こんにちは」へ変えて実行します。エラーになったら、text の行を上のコードへ戻します。
textSize(テキスト・サイズ)は文字の大きさを指定します。text(テキスト)の最初の引数が文字列、次が x 座標、その次が y 座標です。既定では y 座標が文字の下側にあるベースラインを示すため、円と同じつもりで中央へ置くと少しずれて見えます。
10.2 textAlign で基準点を変える
文字をキャンバス中央へ揃えるには、基準を中央へ変更します。
def setup
createCanvas(600, 300)
background(30, 35, 55)
fill(255)
textSize(42)
textAlign(CENTER, CENTER)
text("ここから動く", width / 2, height / 2)
end
textAlign(テキスト・アライン)は文字を揃える基準を指定します。CENTER は rbCanvas/p5 が用意する定数です。1 つ目が水平方向、2 つ目が垂直方向の揃え方です。基準点を中央にしたため、width / 2 と height / 2 へ置けます。
10.3 文字列補間で値を読めるようにする
プログラム内部の値を表示すると、デバッグにも作品にも使えます。次の draw は変更部分です。10.2 の長いコードへ draw がないので、setup の end より後ろへそのまま足します。
def draw
background(245)
size = map(mouseX, 0, width, 16, 90)
fill(35)
textSize(size)
textAlign(CENTER, CENTER)
text("大きさ #{size.round}", width / 2, height / 2)
end
#{size.round} は文字列補間です。変数 size を丸め、その結果を文字列の中へ埋め込みます。ダブルクォートで囲んだ文字列で使えます。
制作中だけ数字を表示すると、マウス位置によって文字サイズがどう変わるか確認できます。完成した作品で数字が不要なら、この表示を消せます。
10.4 一文字ずつ別の位置へ置く
次のコードで、R、U、B、Y を一文字ずつ異なる高さへ並べます。エディタのコード全体を置き換えて実行します。
def setup
createCanvas(600, 300)
background(245)
textAlign(CENTER, CENTER)
textSize(48)
word = "RUBY"
word.chars.each_with_index do |character, i|
x = 150 + i * 100
y = 150 + sin(i * 1.2) * 50
fill(50 + i * 45, 100, 190)
text(character, x, y)
end
end
4 文字が波のように高さを変えて並びます。まず word = "RUBY" の文字列だけを "そらいろ" へ変えて実行し、同じ並び方になるか見ます。戻すときは "RUBY" に戻します。
chars(チャーズ)は、文字列を一文字ずつに分け、順番に並べた配列(はいれつ)を返します。配列は、複数の値を順序付きでまとめたものです。"RUBY" なら ["R", "U", "B", "Y"] となります。左右の [ と ](角括弧)で全体を囲み、中の値をカンマで区切ります。
each_with_index(イーチ・ウィズ・インデックス)は、その配列から文字と番号を一組ずつ渡します。|character, i| の character(キャラクター)が文字、i が番号を受け取ります。最初は "R" と 0、次は "U" と 1、続いて "B" と 2、"Y" と 3 です。word.chars.each_with_index の点は、word を文字に分け、その結果を順に取り出す処理へつないでいます。
10.5 読めなさを選ぶ前に読める版を残す
文字を回転し、重ね、透明にすると、言葉の意味より形が目立ちます。作品名なら大きく崩せますが、操作説明は読める状態を保ちます。
制作中は、読める版と崩した版を別々に保存します。比べてから、どこまで読める必要があるかを決めます。たとえば、タイトルは形として見せ、操作キーは文字として読める状態にする、という分け方があります。
10.6 一行の言葉へ動きを与える
短い言葉を一つ選び、意味と反対の動きを与えると、文字が作品になります。「しずか」を激しく揺らす、「はやい」をゆっくり動かす、「ここ」を画面の外へ逃がす、といった組み合わせです。
まず、次の完成コードで「しずか」の大きさがゆっくり変わる例を動かします。
def setup
createCanvas(600, 300)
textAlign(CENTER, CENTER)
end
def draw
background(30, 35, 55)
size = 54 + sin(frameCount * 0.03) * 18
fill(255)
textSize(size)
text("しずか", width / 2, height / 2)
end
sin は第7章で使った、-1 から 1 までを滑らかに往復する値です。ここでは文字サイズを 36 から 72 くらいの間で変えます。まず「しずか」を別の言葉へ変えます。次に 0.03 で速さを、18 で大きさの変わる幅を調整できます。実行するたびに、どちらか一方を変えます。
2 文字から 6 文字ほどの言葉を置き、読める状態を保存します。その複製では、位置、大きさ、透明度のどれかを時間とともに変えます。言葉の意味と動きが合う組み合わせと、反対に感じる組み合わせを比べてみてください。
言葉が読める版と、形として目立つ版を見比べます。残したいほうを決めたら、次章で写真や手描きの画像へ図形を重ねます。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
text、textSize、textAlignの説明 - Ruby 公式
String— 文字列、chars、文字列補間に関係する Ruby の機能
第11章 画像を借りて組み替える
画像を読み込むと、写真、紙の質感、自分の手描き素材をスケッチへ持ち込めます。一方で、ファイルの場所、読み込み時間、著作権という、図形だけの章にはなかった条件が増えます。画像は便利な背景ではなく、出典と文脈を持つ素材です。
11.1 setup で画像を読み込む
この章には練習用画像 sample.svg が付属しています。リンク先を保存してから、ファイルをマウスやトラックパッドでつかみ、rbCanvas/p5 エディタの上まで運んで放します。この操作をドラッグ&ドロップと呼びます。取り込みに成功すると、エディタ下部に画像の小さな表示が現れます。素材が表示されないときは、画像や音が読み込めないを上から確認します。ドラッグ操作ができない端末では、この章は図と説明だけ見て第12章へ進めます。
ファイル名はコードと一字ずつ一致させます。sample.svg と Sample.svg は、大文字と小文字が違う別の名前です。自分の画像が PNG 形式なら、無理に名前だけを .svg へ変えず、コード側も実際の名前、たとえば sample.png に合わせます。
def setup
createCanvas(600, 400)
@photo = loadImage("sample.svg")
end
def draw
background(245)
image(@photo, 0, 0, width, height)
end
loadImage(ロード・イメージ)は、取り込んだ画像を読み込むメソッドです。rbCanvas/p5 0.5.1 では非同期処理を意識する必要がないため、公式リファレンスの例と同じく setup の中で読み込みます。読み込んだ画像を @photo へ入れておくと、draw からも使えます。
image(イメージ)の最初の引数が画像、次の 2 つが位置、最後の 2 つが表示幅と高さです。幅と高さの割合を縦横比(じゅうおうひ)と呼びます。元画像と違う縦横比を指定すると、画像が伸びます。
11.2 縦横比を保つ計算
たとえば元画像が幅 600、高さ 400 なら、表示幅を半分の 300 にすると、高さも半分の 200 にすれば形が伸びません。400 * 300 / 600 で、その高さの 200 を求められます。
画像の幅をキャンバス幅へ合わせ、高さを同じ割合で計算します。次の 3 行は変更部分です。11.1 の長いコードにある image(@photo, 0, 0, width, height) の 1 行と置き換えます。
display_width = width
display_height = @photo.height * display_width / @photo.width
image(@photo, 0, 0, display_width, display_height)
元画像の 幅 : 高さ と、表示画像の比率が同じになります。画面全体を埋めることより、人物や円が不自然に伸びないことを優先する場合に使えます。
11.3 画像の上へ描く
読み込んだ画像は、ほかの図形と同じ描画順に入ります。次の draw は変更部分です。11.1 の長いコードにある def draw から対応する end までと置き換えます。
def draw
image(@photo, 0, 0, width, height)
noStroke
fill(255, 80, 120, 90)
ellipse(mouseX, mouseY, 140, 140)
end
画像を先に描き、半透明の円をあとから描いています。順番を逆にすると、画像が円を覆います。
マウス位置へ円を描くだけでも、写真のどこへ注目させるかが変わります。素材を加工することは、ピクセルを壊すことだけではありません。重ねる形と順番で意味を変える方法もあります。
11.4 自分の素材から始める
インターネットで見つけた画像には、作者と利用条件があります。「検索で表示された」は「自由に使える」という意味ではありません。最初の実験には、次の素材が扱いやすい選択です。
- 自分で撮影した写真
- 自分で描いた絵を撮影またはスキャンした画像
- 利用条件が明記されたパブリックドメイン素材
- ライセンスが目的に合い、作者名など必要な表示を行える素材
人物写真には、著作権だけでなく肖像やプライバシーの問題もあります。公開しない個人実験と、Web で配布する作品では条件が変わります。素材を使える条件が分からないときは、文化庁の著作権の基本で、著作物と著作者の説明から確認できます。
付属の sample.svg は本書のために作った素材なので、この章の練習や公開作品へ自由に利用、加工できます。最初は付属素材で操作を確かめ、動いたあとに自分の画像へ置き換えると、ファイル形式の問題とコードの問題を分けて考えられます。
11.5 単一 HTML と素材
rbCanvas/p5 は、画像や音声を含めて単一の HTML ファイルへ保存できることを特徴としています。ただし保存や読み込みの挙動はブラウザの種類と設定に左右されます。保存したら、元のエディタを閉じた状態でも開けるか確認します。
あとから編集を続けるときは、保存した HTML ファイルをエディタへドラッグ&ドロップします。コードだけでなく、いっしょに保存された画像も復元されます。
画像を組み替えるほど、コードにも同じ処理が増えていきます。第4部では、繰り返す描画をメソッドへまとめ、複数の要素を配列とクラスで扱います。
使っているパソコンの都合で素材を取り込めない場合は、図 11-1 と図 11-2 で変化を確認して、第12章へ進みましょう。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
loadImageとimageの仕様 - rbCanvas/p5 エディタの使い方 — 素材の読み込みと単一 HTML の保存方法
- 文化庁「著作権の基本」 — 画像を利用するときの著作物と著作者の基本
第4部 Ruby で作品を育てる
形が増えると、同じコードの繰り返しや、似た変数が目立ち始めます。その重複が、メソッドや配列を使って整理する合図になります。
第4部では、メソッド、配列、クラスを使います。Ruby の機能を覚えるためではなく、気に入った作品を壊さずに育てるために導入します。小さな作品では、一つのメソッドや配列だけで見通しよく書ける場合もあります。
第12章 メソッドで自分の描画命令を作る
同じ顔を 3 か所へ描こうとすると、楕円の行が何度も並びます。コピーは実験の入口として役立ちますが、あとから目の大きさを変えるとき、すべてのコピーを直す必要があります。メソッドは、繰り返した処理へ名前を付け、自分の作品専用の命令にします。
12.1 コピーした顔が教える整理の時期
第6章の発展例に出てきた draw_circles も、自分で作ったメソッドでした。この章では、引数と戻り値を一つずつ確かめます。次のコードでは、def draw_face から最初の end までが自分で作る描画命令、その後の setup がキャンバスを用意して命令を 3 回使います。完成コードをそのまま実行します。
def draw_face(x, y)
fill(250, 170, 80)
ellipse(x, y, 100, 100)
fill(30)
ellipse(x - 20, y - 10, 12, 18)
ellipse(x + 20, y - 10, 12, 18)
end
def setup
createCanvas(500, 300)
background(245)
draw_face(120, 150)
draw_face(250, 120)
draw_face(390, 170)
end
x と y は引数です。呼び出す側から値を受け取ります。顔の描き方は 1 か所にまとまり、位置だけを変えて 3 回使えます。顔の楕円をコピーして別々に直す方法でも試作できますが、目の大きさなどを一度に変えたくなったら、メソッドへまとめる理由が生まれます。
12.2 描くものをメソッド名にする
draw_face は、rbCanvas/p5 に最初から存在する命令ではありません。自分で定義したメソッドです。作品が植物なら draw_leaf、都市なら draw_window のように、何を描く処理なのか分かる名前を付けられます。
メソッド名は短さだけで決めません。thing より draw_sleeping_eye のほうが、何を描くか伝わる場合があります。一方、1 章だけの小さな実験で長い名前を付けすぎると読みづらくなります。あとから自分で読んだときに、何を描く名前か分かるかを基準にします。
12.3 引数で違いを残す
すべて同じ顔ではなく、大きさを変えたい場合は size を受け取ります。12.1 で作った draw_face全体を、次の変更部分と置き換えます。
def draw_face(x, y, size)
fill(250, 170, 80)
ellipse(x, y, size, size)
eye_width = size * 0.12
eye_height = size * 0.18
eye_offset = size * 0.2
fill(30)
ellipse(x - eye_offset, y - size * 0.1, eye_width, eye_height)
ellipse(x + eye_offset, y - size * 0.1, eye_width, eye_height)
end
目の位置と大きさを顔全体の size から計算しています。顔が大きくなれば、目も同じ比率で大きくなります。続く 3 行は変更部分で、setup にある 3 回の draw_face 呼び出しと置き換えます。
draw_face(120, 160, 70)
draw_face(260, 140, 120)
draw_face(410, 170, 90)
図 12-1 では位置だけが違い、図 12-2 では位置と大きさが違います。メソッドへ size を足した効果を、三つの顔で比べられます。
12.4 戻り値で計算を分ける
描画しないメソッドも作れます。キャンバス外へ出ない x 座標を返すメソッドです。次の変更部分は、draw_face と同じく setup の外側へ置きます。
def keep_inside(value, minimum, maximum)
return minimum if value < minimum
return maximum if value > maximum
value
end
Ruby では、最後に計算・確認した値が戻り値になります。これを「最後に評価した式の値」と呼ぶことがあります。範囲内なら最後の value、小さすぎれば minimum、大きすぎれば maximum を返します。続く 2 行は変更部分です。動きを見るには新しい def draw の中へ入れ、setup にある顔の呼び出しは削除します。
safe_x = keep_inside(mouseX, 50, width - 50)
draw_face(safe_x, mouseY, 100)
描画と計算を分けると、計算だけを別の場所で再利用できます。短い一度きりの式は、その場に書いたほうが流れを追いやすい場合もあります。重複して困ったとき、名前を付けると読みやすくなるときが整理の合図です。
12.5 小作品「同じ家族、違う三人」
12.3 の大きさが違う三つの顔へ、短い題名を付けます。「朝が早い三人」「遠くを見る家族」のように、画面から感じた関係を一つ選びます。その題名に合わせ、次のうち一つだけを変えます。
- 三つの顔の間隔を、近くまたは遠くする
- 一つだけ大きさを極端に変える
- 全員を画面の上半分または下半分へ寄せ、余白を作る
同じ draw_face へ位置や大きさを渡すと、共通する描き方を保ったまま複数の案を早く比べられます。
三つの顔の間隔か大きさを一つ変え、残したい並びを保存します。動かないときは、12.1 の完成コードへ戻し、変更前の状態から試し直せます。
第13章では、同じメソッドをいくつもの位置データへ適用します。メソッドが「描き方」、配列が「何をいくつ持つか」を担当します。
参考資料
- Ruby リファレンスマニュアル「メソッド定義」 —
def、引数、戻り値の Ruby での扱い - rbCanvas/p5 0.5.1 API リファレンス — メソッド内で使った描画命令の仕様
第13章 配列が一つを群れに変える
円が一つなら、@x と @y で位置を覚えられます。100 個になると、@x1 から @x100 を作る方法は変更しにくくなります。配列は、同じ役割を持つ複数の値を順序付きでまとめます。
13.1 位置を一つの配列へ集める
def setup
createCanvas(500, 300)
@x_positions = [80, 160, 260, 390]
end
def draw
background(245)
fill(235, 90, 120)
@x_positions.each do |x|
ellipse(x, height / 2, 50, 50)
end
end
each(イーチ)は配列の要素を一つずつブロックへ渡します。配列へ値を足すと、同じ描画コードから円を増やせます。
13.2 x と y を組にするハッシュ
各要素が位置と速度を持つなら、値の意味を名前で表せるハッシュを使えます。
def setup
createCanvas(500, 300)
@dots = [
{ :x => 80, :y => 90, :speed => 1.2 },
{ :x => 160, :y => 170, :speed => 2.0 },
{ :x => 300, :y => 130, :speed => 0.8 }
]
end
各ハッシュには :x、:y、:speed というキーがあります。=> は、左側のキーと右側の値を結び付ける記号です。0.5.1 では、行を分けた省略記法が構文エラーになる組み合わせを実機で確認したため、この書き方へ揃えています。更新と描画を同じ反復で行います。次の draw は変更部分で、直前の setup の end より後ろへ足します。
def draw
background(25, 30, 48)
noStroke
@dots.each do |dot|
dot[:x] += dot[:speed]
dot[:x] = -20 if dot[:x] > width + 20
fill(255, 210, 100, 180)
ellipse(dot[:x], dot[:y], 24, 24)
end
end
3 つの点は同じ更新規則を持ちますが、初期位置と速度が違います。
13.3 push で実行中に増やす
クリックした場所へ点を追加します。次のイベントメソッドは変更部分で、draw の end より後ろへ足します。
def mousePressed
@dots.push({ :x => mouseX, :y => mouseY, :speed => random(0.5, 2.5) })
end
push(プッシュ)は配列の末尾へ要素を加えます。クリックのたび、現在位置と乱数の速度を持つ新しい点が生まれます。
rbCanvas/p5 0.5.1 では、push( の直後で改行すると、内容が正しくても unexpected token tSEMI という構文エラーになる場合があります。この例では、push へ渡すハッシュを同じ行に書いて避けています。
要素が増え続けると、描画処理も増え続けます。「記録を蓄積する」作品では増え続けること自体が表現になります。長く操作する作品では、上限を決めるか、古い要素を削除すると処理量を保てます。
13.4 select で残すものを選ぶ
画面の右端を越えた点を取り除きます。13.2では、右端を越えた点を左端へ戻す次の行があります。次のコードが、まず削除する変更部分です。
dot[:x] = -20 if dot[:x] > width + 20
折り返しを残したままでは、右端を越えた点がすぐ -20 へ戻り、取り除く対象がなくなります。削除したあと、次の変更部分を draw にある @dots.each の反復が終わった直後、draw 最後の end より前へ足します。
@dots = @dots.select do |dot|
dot[:x] <= width + 20
end
select(セレクト)は、ブロックの条件が成り立つ要素だけを集めた新しい配列を返します。ここでは「残す条件」を書いています。
描画中の配列から同時に要素を削除すると、反復の順番が分かりにくくなることがあります。更新が終わったあとで select し直すと、処理の段階が分かれます。
確認するときは、13.2 の三つの点をそのまま動かします。点が右端から完全に出ると配列から取り除かれ、左端からは戻りません。クリックで追加した点も同じ条件で取り除かれます。
13.5 色の候補から一つ選ぶ
配列には、位置だけでなく、作品で使う色の組み合わせも入れられます。
def setup
createCanvas(480, 320)
background(245)
noStroke
palette = [
[190, 45, 70],
[240, 150, 60],
[45, 105, 145],
[90, 55, 120]
]
6.times do |row|
9.times do |column|
chosen_color = palette.sample
fill(chosen_color[0], chosen_color[1], chosen_color[2])
ellipse(30 + column * 53, 28 + row * 53, 38, 38)
end
end
end
palette(パレット)は、絵に使う色をまとめた配列です。その中の各色も、赤、緑、青の 3 要素を持つ配列です。sample(サンプル)は、配列から要素を一つ選んで返す Ruby のメソッドです。chosen_color[0] は最初の赤、chosen_color[1] は次の緑、chosen_color[2] は最後の青を取り出します。角括弧の番号は 0 から始まります。
すべてを自由な乱数色にせず、先に選んだ 4 色からだけ使うことで、偶然が起きても作品全体の関係を保てます。色を一つ入れ替える、特定の行だけ候補を減らす、円を別の形へ変える、の順で敷き詰め模様を育てられます。
13.6 配列のハッシュからクラスへ移る境目
ハッシュは、少数の性質を持つ要素に向いています。点が「更新する」「描く」「端に出たか調べる」という複数の処理を持ち始めると、毎回ハッシュを渡すコードが散らばります。
点ごとのデータと更新・描画の処理を一緒に置きたくなったら、クラスを使えます。次章では、今の点をクラスへ移し、配列とハッシュで書いた版との違いを比べます。
13.7 小作品「記憶の一色」
13.5 の paletteにある四色から一色だけを、自分が覚えている場所や物の色へ置き換えます。「夕方の帰り道」「古いかばん」のように、その色へ短い名前を付け、記憶に近づくよう数字を少しずつ変えます。
実行するたび並びは変わりますが、候補にした 4 色だけが使われます。色を一つ入れ替えれば、次の実行からすべての模様へ反映されます。HTML に保存されるのは、色の候補と並べ方を決めるコードです。開き直すと sample が色を選び直すため、同じ並びにはなりません。今の並びも残したいときは、再実行する前に画面を画像として保存します。編集を続けるための HTML と、見返すための画像を両方残せます。
最初はクリックによる追加を省き、13.1 の四つの円だけを表示してもかまいません。今日残したい並びが決まったら、第14章で点ごとの動きと色をクラスへまとめます。
参考資料
- Ruby リファレンスマニュアル「Array」 —
push、sampleなど配列のメソッド - Ruby リファレンスマニュアル「Enumerable」 —
selectなど、要素を順に調べるメソッド - Ruby リファレンスマニュアル「Hash」 — 名前と値を組にして持つ方法
第14章 クラスで登場人物に個性を持たせる
第13章の点は、位置と速度をハッシュで持っていました。更新処理と描画処理が増えると、点に関するコードが draw の中へ集まりすぎます。クラスは、点が知っている値と、点ができることを一つの定義へまとめます。
14.1 一つの Dot を定義する
次のクラス定義は作品を構成する変更部分です。このあと14.2 の setup、draw、mousePressedを同じエディタへ続けて足してから実行します。
class Dot
def initialize(x, y)
@x = x
@y = y
@speed = random(0.5, 2.5)
@size = random(16, 36)
end
def update
@x += @speed
end
def display
fill(255, 210, 100, 180)
ellipse(@x, @y, @size, @size)
end
def outside?
@x - @size / 2 > width
end
end
initialize(イニシャライズ)は、Dot.new で新しいオブジェクトを作るときに呼ばれます。位置を引数で受け、速度と大きさを乱数で決めます。
outside? の末尾の疑問符は Ruby ではメソッド名の一部です。真偽値を返すメソッドに疑問符を付ける慣習があり、条件式で読みやすくなります。
14.2 配列がオブジェクトを持つ
def setup
createCanvas(500, 300)
@dots = []
end
def draw
background(25, 30, 48)
noStroke
@dots.each do |dot|
dot.update
dot.display
end
@dots = @dots.reject do |dot|
dot.outside?
end
end
def mousePressed
@dots.push(Dot.new(mouseX, mouseY))
end
配列はハッシュではなく Dot のオブジェクトを持ちます。呼び出す側は、位置がどの変数に入っているかを知る必要がありません。update で更新を、display で表示を頼みます。クラス側を draw という名前にすると、rbCanvas/p5 が自動で呼ぶトップレベルの draw と区別できない場合があるため、ここでは display と名付けます。
reject(リジェクト)は、条件に当てはまった要素を取り除いた新しい配列を返します。ここでは各 dot の outside? を呼び、画面の外へ出た点だけを取り除いています。処理を短く書く省略記法もありますが、最初は do から end までを見える形にしたほうが、何を調べているか追いやすくなります。
14.3 個性は同じクラスの初期値から生まれる
すべての点は同じ Dot クラスですが、速度と大きさが違います。個性を増やすため、色も持たせます。次の変更部分で、Dot クラス内の initialize と display をそれぞれ置き換えます。
def initialize(x, y)
@x = x
@y = y
@speed = random(0.5, 2.5)
@size = random(16, 36)
@red = random(120, 255)
@blue = random(80, 220)
end
def display
fill(@red, 150, @blue, 180)
ellipse(@x, @y, @size, @size)
end
同じクラスから作ったオブジェクトは、共通する性質と振る舞いを持ちながら、それぞれ別の値を持てます。
クリックを何度も繰り返さず、実行直後から違いを比べたい場合は、setup を次の変更部分と置き換えます。第7章で使った randomSeed が乱数の並びを固定するため、同じ初期配置をもう一度作れます。
def setup
createCanvas(500, 300)
randomSeed(14)
@dots = []
16.times do
@dots.push(Dot.new(random(width), random(height)))
end
end
16.times の中で Dot.new を 16 回呼び、位置もそれぞれ乱数で決めています。数を 8 や 30 へ変えると、密度だけを比べられます。
16.times を 8.times へ変えた版も、別名で保存して比べます。
数が少ない版では、一つずつの色や大きさへ目が向きます。多い版では、点の集まり全体が先に目へ入ります。どちらを残すかは、一つの点と集まりのどちらを目立たせたいかで決めます。
14.4 クラスを使わないほうが読みやすい場合
円が 2 個だけで、動きも 1 行なら、クラスは説明を増やすだけかもしれません。クラスを使う判断は、コード行数ではなく次の問いで考えます。
- その要素に関する値が複数の場所へ散らばっているか
- 更新、描画、判定を同じ対象の責任として読めるか
- 同じ種類の要素を複数作るか
- 新しい性質を足すたび、関係のない場所まで直しているか
小さな実験ではコードをコピーし、直す場所が増えてからクラスへまとめる順番でも進められます。
14.5 小作品「動きに性格を付ける」
点の性格を一語だけ選びます。たとえば「せっかち」「おくびょう」「のんびり」です。次の三つから一つを変えて実行し、そのあと必要なら二つ目を変えます。
@speedの範囲@sizeの範囲@redと@blueの範囲
性格を表す新しい命令はありません。速さ、大きさ、色という数値の組み合わせを、自分がどう読むかで意味が生まれます。同じクラスから作った点が全員同じである必要もありません。
クラスのコードを追いにくくなったら、第13章のハッシュ版と並べて見ます。作品が動き、次に変える行を見つけやすいほうを選びましょう。
14.6 Ruby そのものをもっと知るなら
描画から少し離れ、Ruby の仕組みを試す道もあります。Ruby 公式ドキュメントのArray やEnumerable を開くと、sample、select、reject 以外にも、並べ替える、数える、変換するといったメソッドを見つけられます。
最初は新しい名前を一つ選び、短い配列で結果を確かめます。同じ点の集まりを配列とハッシュで書き比べる、ハッシュ版とクラス版を保存して読みやすさを比べる、描画を使わず Ruby の式だけを試す、といった方法があります。
クラス版とハッシュ版のどちらで、次に変更する場所を早く見つけられたでしょうか。選んだ版を使い、次章では画面をタイトル、実行中、終了に分けます。
参考資料
- Ruby リファレンスマニュアル「クラス/メソッドの定義」 — クラス、
initialize、インスタンスメソッドの構文 - Ruby リファレンスマニュアル「Object」 — Ruby のオブジェクトが共通して持つ機能
第15章 状態を分けて小さな世界を保つ
タイトル画面、遊んでいる画面、終わった画面を一つの draw に足していくと、条件分岐が互いに影響し始めます。作品全体の状態を一つに決め、その状態に必要な処理だけを呼ぶと、今どこにいるかを追いやすくなります。
15.1 3 つの場面を一つの @scene で表す
def setup
createCanvas(600, 360)
@scene = :title
@x = 0
end
def draw
case @scene
when :title
draw_title
when :playing
update_playing
draw_playing
when :finished
draw_finished
end
end
def draw_title
background(30, 35, 55)
fill(255)
textAlign(CENTER, CENTER)
textSize(36)
text("SPACE ではじめる", width / 2, height / 2)
end
@scene は同時に一つの値だけを持ちます。タイトルと終了画面が同時に表示される矛盾を避けられます。
この段階では @scene が :title なので、draw から呼ばれるのは、いちばん下で定義した draw_title だけです。そのまま実行するとタイトル画面が表示されます。まだ SPACE キーは反応しませんが、エラーではありません。次の節から、残りの場面と動きを一つずつ足します。
15.2 場面ごとに描画を分ける
タイトル画面を描く draw_title は15.1で定義しました。次の 2 つのメソッドは変更部分です。15.1 の長いコードのあとへ続けて足します。
def draw_playing
background(245)
fill(235, 90, 120)
ellipse(@x, height / 2, 60, 60)
end
def draw_finished
background(235, 90, 120)
fill(255)
textAlign(CENTER, CENTER)
textSize(36)
text("もう一度 SPACE", width / 2, height / 2)
end
各メソッドは、自分の場面だけを描きます。色や文字を変えるとき、別の場面へ影響しにくくなります。
図 15-2 と図 15-3 は、15.3と15.4まで追加した完成後の画面を先に示しています。この15.2の段階ではまだ SPACE キーは反応しません。二つのメソッドを追加できたら、図を見て先の変化を予想し、15.3へ進みます。
15.3 更新と描画を分ける理由
次のメソッドも変更部分で、15.2 の 2 つのメソッドのあとへ足します。
def update_playing
@x += 4
@scene = :finished if @x > width + 30
end
update_playing は値を変え、draw_playing は現在値を画面へ描きます。この分離により、一時停止では更新だけ止め、同じ画面を描き続ける設計ができます。
必ず分けなければならない規則ではありません。小さなスケッチでは一つの draw が読みやすい場合があります。状態が増え、変更する処理と見せる処理を別々に考えたくなったときに分けます。
15.4 キーで遷移する
次のイベントメソッドも変更部分で、同じコードの末尾へ足します。
def keyPressed
return if key != " "
case @scene
when :title
@x = 0
@scene = :playing
when :finished
@x = 0
@scene = :playing
end
end
状態が変わることを遷移と呼びます。タイトルから実行中へ、実行中から終了へ移ります。終了から実行中へ戻るときは、@x も初期値へ戻します。
状態だけ変えて値を戻し忘れると、再開した瞬間にまた終了します。場面を切り替えるとき、その場面が必要とする値をどこで初期化するかまで考える必要があります。
15.5 デバッグ表示で状態と位置を確かめる
制作中だけ、現在の状態を左上へ表示できます。次の変更部分は draw の case が終わった直後、draw 最後の end より前へ足します。
fill(0, 160)
rect(6, 6, 210, 26)
fill(255)
textAlign(LEFT, TOP)
textSize(14)
text("scene: #{@scene} x: #{@x}", 10, 10)
見えない状態を文字へ変えると、「キーは反応したが位置を戻していない」といった違いを見つけやすくなります。完成時には、表示を消す方法と、調べる道具として残す方法のどちらも選べます。
15.6 三つの場面に違いを付ける
場面管理を使うと、同じ円を別の出来事として見せられます。まずタイトル場面の背景色だけを変えて実行し、実行中との切り替わりが見えるか確かめます。次に試すなら、終了場面の「もう一度 SPACE」を「夜が明けた」や「種が届いた」のような短い言葉へ変えます。
色と文字を同時に変える必要はありません。場面を一つ選び、変更前の版と見比べます。コードを追えなくなったときは、15.1 のタイトル画面の版へ戻せます。
第4部では、増えた値や処理をメソッド、配列、クラス、状態へ分けました。第5部では、その書き方を使って音、3D、ゲームを作ります。
参考資料
- Ruby リファレンスマニュアル「制御構造」 —
caseと条件分岐の構文 - rbCanvas/p5 0.5.1 API リファレンス — キー入力と文字描画の仕様
第5部 表現領域を広げる
音、3D、ゲームでも、第1部から第4部までに使った値、状態、入力を組み合わせます。
第16章の音、第17章の 3D、第18章のゲームは、興味のある章から選べます。素材ファイルやブラウザの許可が必要な機能では、環境によって動作が異なることがあります。動かなかったときに、自分の能力の問題だと決めつけないでください。コード、素材、ブラウザの設定を一つずつ確認します。
第16章 音と画面を同じ時間に置く
音を鳴らすだけなら、再生ボタンを一つ置けば足ります。クリエイティブコーディングでは、音が鳴っている時間を画面の変化にも使えます。クリックで音を始め、再生中かどうかに合わせて形の大きさや色を変えます。
16.1 ブラウザは勝手な音を止める
多くのブラウザは、ページを開いただけで音が鳴る自動再生を制限します。驚かせる広告や不要な音を防ぐためです。そのため、音の開始にはクリックやキー入力など、利用者の操作が必要です。
音が鳴らないとき、コードだけが原因とは限りません。最初に次を確認します。
- 画面を 1 回クリックしたか
- タブがミュートされていないか
- 端末の音量が 0 ではないか
- 音声ファイルをエディタへ正しく取り込んだか
- ブラウザが音声形式を再生できるか
16.2 setup で音を用意する
この章には、練習用の短い音声 sound.wav が付属しています。リンク先を保存してエディタへドラッグ&ドロップし、素材一覧へ sound.wav が現れたことを確認します。素材名が出ないときは、画像や音が読み込めないを確認します。ドラッグ操作ができない端末では、この章は図と説明だけ見て第17章へ進めます。
loadSound(ロード・サウンド)は、取り込んだ sound.wav を音声オブジェクトとして読み込みます。rbCanvas/p5 0.5.1 では setup の中で読み込みます。その音声オブジェクトを @sound へ入れておくと、別のメソッドからも再生や停止を指示できます。
def setup
createCanvas(500, 300)
@sound = loadSound("sound.wav")
end
def draw
background(25, 30, 48)
fill(255)
textAlign(CENTER, CENTER)
textSize(24)
if @sound.isPlaying
message = "再生中"
else
message = "クリックで再生"
end
text(message, width / 2, height / 2)
end
def mousePressed
if @sound.isPlaying
@sound.stop
else
@sound.play
end
end
isPlaying(イズ・プレイング)は、音が再生中なら true(真)、停止中なら false(偽)を返します。再生中なら「再生中」、停止中なら「クリックで再生」を変数 message に入れます。第5章で使った if と else を、表示する文字とクリック時の処理の切り替えに使っています。
mousePressed(マウス・プレスド)は、キャンバス上でマウスボタンを押したとき、またはタッチ操作をしたときに呼ばれるメソッドです。@sound.play は読み込んだ音を先頭から再生し、@sound.stop は再生を止めます。.play の前の点は、「@sound に再生を頼む」というつながりを表します。
rbCanvas/p5 の版、ブラウザ、音声形式によって読み込み方法や自動再生制限の挙動が変わることがあります。本書では付属の
sound.wavを 0.5.1 Opal 版へ取り込み、Chrome でクリック再生と停止を確認しています。別の環境で音が鳴らない場合は、この節の確認項目を上から試すと原因を分けられます。
16.3 再生の状態を作品全体で共有する
16.2では、表示とクリック処理の両方が音声オブジェクトの状態を基準にしています。次は同じ isPlaying を、文字だけでなく円の大きさと色へ結び付けます。図 16-3、16-4 では変化を見やすく切り出すため、キャンバスを 420×220 へ小さくします。最初に setup の createCanvas(500, 300) を createCanvas(420, 220) へ置き換えます。
続く draw は変更部分です。16.2 の長いコードにある def draw から対応する end までと置き換えます。mousePressed は16.2のまま残します。
def draw
background(25, 30, 48)
if @sound.isPlaying
size = 120 + sin(frameCount * 0.1) * 40
fill(255, 200, 90)
else
size = 80
fill(100, 120, 150)
end
noStroke
ellipse(width / 2, height / 2, size, size)
end
このコードは、「再生中か」という状態を画面の動きへ対応させています。扱う状態が一つなので、音が始まったときと止まったときの画面をその場で確かめられます。
音が最後まで鳴り終わると、@sound.isPlaying は自動的に false へ戻ります。そのため、表示も小さな円へ戻り、次のクリックでは一度で再生が始まります。表示と操作で同じ状態を調べると、画面と音のずれを防げます。
16.4 音と画面を同じ状態で動かす
同じ映像へ曲を後から加える方法もあります。この節では、再生中と停止中で形の大きさや色を変え、音と画面が同じ状態を示すようにします。
音の再生と画面の変化を対応させるため、次の問いから一つ選びます。
- 音が始まるまで、形は待つか
- 音が止まると、画面は消えるか残るか
- 再生中の円を一定の速さで大きくするか、色を変えるか
- 無音の時間に何を見せるか
音声素材にも著作権があります。自分で録音した音、利用条件が明確な素材、生成条件を確認できる音を使います。公開しない実験と配布する作品を分けて考えてください。
付属音は 440 Hz の音をプログラムで合成した 1.2 秒の教材用素材です。第三者の演奏や録音を含まず、この章の練習や公開作品へ自由に利用、加工できます。
音を使えない環境では、図 16-1 から 16-4 で停止中と再生中の違いを確認できます。第17章では、3D 描画へ切り替え、奥行きのある箱を動かしましょう。
参考資料
- rbCanvas/p5 0.5.1 概要 — 音声機能を含む対応範囲と実行環境
- rbCanvas/p5 0.5.1 API リファレンス —
loadSound、play、stop、isPlayingの仕様
第17章 3D は座標を一つ増やした舞台
2D では x と y で位置を決めました。3D では奥行きの z が加わります。ただし、立体の命令を使っただけで奥行きが伝わるわけではありません。回転、光、手前と奥の重なりによって、平らな画面へ空間を感じさせます。
17.1 WEBGL で原点が中央へ移る
def setup
createCanvas(500, 400, WEBGL)
end
def draw
background(25, 30, 48)
normalMaterial
box(120)
end
WEBGL を指定すると 3D 描画モードになります。2D の既定では原点が左上でしたが、WEBGL モードでは原点がキャンバス中央です。第2章の座標感覚をそのまま使うと、位置がずれて見えます。
box(ボックス)は箱を描きます。box(120) なら幅、高さ、奥行きが同じです。normalMaterial(ノーマル・マテリアル)は面の向きに応じて色を付けるため、光源をまだ設定していなくても立体の向きを観察しやすくなります。
17.2 回転しなければ箱は四角に見える
正面から箱を見ると、奥の面が手前に隠れ、四角形のように見えることがあります。x 軸と y 軸の周りに回転します。次の draw は変更部分です。17.1 のコードにある def draw から対応する end までと置き換えます。
def draw
background(25, 30, 48)
normalMaterial
rotateX(frameCount * 0.01)
rotateY(frameCount * 0.013)
box(120)
end
rotateX(ローテート・エックス)と rotateY(ローテート・ワイ)は、回転する角度を受け取ります。角度にはラジアンという単位が使われます。この章では、画面の回転から値の違いを確かめます。frameCount に 0.01 のような小さな値を掛けると、時間とともに少しずつ回転します。0.01 を 0.03 へ変えると回転が速くなります。
17.3 push と pop で座標変換を閉じ込める
2 つの箱を別々に動かします。次の draw も変更部分で、直前の draw 全体と置き換えます。
def draw
background(25, 30, 48)
normalMaterial
push
translate(-120, 0, 0)
rotateY(frameCount * 0.02)
box(80)
pop
push
translate(120, 0, 0)
rotateX(frameCount * 0.015)
box(80)
pop
end
translate(トランスレート)や rotate(ローテート)は、その後の描画座標へ影響します。push(プッシュ)は現在の座標系を保存し、pop(ポップ)は元へ戻します。これがないと、1 つ目の移動と回転が 2 つ目の箱にも重なります。
translate(x, y, z) は、これから描くものの原点を移動します。最初の箱では x を -120 にして左へ、2 つ目では x を 120 にして右へ移しています。y と z が 0 なので、上下と奥行きの位置は変わりません。
push と pop は、立体専用ではありません。2D の回転や拡大縮小でも、変換の影響範囲を分けるために使えます。
17.4 光が形の読み方を変える
normalMaterial を外し、光を置きます。次の draw も変更部分で、直前の draw 全体と置き換えます。
def draw
background(20)
ambientLight(70)
directionalLight(255, 240, 220, -1, 0.5, -1)
noStroke
fill(235, 90, 120)
rotateX(frameCount * 0.01)
rotateY(frameCount * 0.013)
box(140)
end
環境光は全体を弱く照らし、平行光源は一定方向から照らします。同じ箱でも、明暗によって面の向きが読みやすくなります。
ambientLight(アンビエント・ライト)は全方向から当たる光、directionalLight(ディレクショナル・ライト)は一方向から当たる光を置きます。directionalLight の最初の 3 つの数字は光の赤・緑・青、後ろの 3 つは光が進む向きです。最初は数字を暗記せず、色の 3 つの数字や向きの一つだけを変えて、どの面の明るさが変わるか観察します。
3D は計算負荷が高くなりやすく、端末によって滑らかさが違います。動きが重いときは、物体数を減らす、形を簡単にする、キャンバスを小さくする、の順に試します。回転や光の変化を残したまま、処理を軽くできます。
17.5 箱を展示物へ変える
箱を小さな展示物へ変えます。box(140) を box(60, 180, 60) へ変えると細長い柱に、box(180, 20, 120) へ変えると薄い板になります。box(ボックス)へ三つの数字を渡すと、幅、高さ、奥行きを別々に指定できます。まずどちらか一つだけ試します。
形の比率を確かめたあと、fill の三つの数字を変えると色も選べます。回転と光を残したまま、柱か板のどちらかを一つ保存します。次章では 2D へ戻り、キーで動かせる作品を作ります。
参考資料
- rbCanvas/p5 0.5.1 概要 — 3D 描画を含む対応範囲と実行環境
- rbCanvas/p5 0.5.1 API リファレンス —
WEBGL、box、回転、光の仕様 - p5.js WebGL チュートリアル — 座標変換の概念。サンプルは JavaScript で書かれている
第18章 ゲームは反応のまとまり
これまで使った位置、速度、入力、条件分岐、状態を組み合わせ、操作すると結果が返る画面を作ります。その結果が次の操作を変えると、遊びの流れが生まれます。
18.1 まず目標のない操作を作る
矢印キーで円を動かします。第9章と同じ入力方法です。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
end
def draw
@player_x -= 4 if keyIsDown(LEFT_ARROW)
@player_x += 4 if keyIsDown(RIGHT_ARROW)
@player_y -= 4 if keyIsDown(UP_ARROW)
@player_y += 4 if keyIsDown(DOWN_ARROW)
@player_x = constrain(@player_x, 20, width - 20)
@player_y = constrain(@player_y, 20, height - 20)
background(25, 30, 48)
fill(90, 210, 180)
ellipse(@player_x, @player_y, 40, 40)
end
constrain(コンストレイン)は、値を最小と最大の範囲へ収めます。第12章の keep_insideで自作した処理と同じ働きが、rbCanvas/p5 に用意されています。円の半径 20 を境界に使い、画面外へ出ないようにしています。
keyIsDown は、指定したキーが現在押されているかを調べます。LEFT_ARROW、RIGHT_ARROW、UP_ARROW、DOWN_ARROW は、それぞれ左・右・上・下の矢印キーを表す名前です。処理の後ろに if と条件を書く形を使い、押されている間は位置を 4 ずつ変えます。-= は左辺の値を減らし、+= は増やす記号です。
まだ勝敗はありませんが、操作への反応はあります。最初からスコアや敵を足さず、動かす感触を決めると調整点が見えます。
18.2 距離で触れたことを判定する
目標の位置を加えます。次の setup は変更部分です。18.1 の長いコードにある def setup から対応する end までと置き換えます。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
@score = 0
end
続いて、18.1 の長いコードの draw で位置を更新したあと、background より前へ次の変更部分を足し、中心同士の距離を調べます。
distance = dist(@player_x, @player_y, @target_x, @target_y)
if distance < 35
@score += 1
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
end
プレイヤーの半径が 20、目標の半径が 15 なら、中心間の距離が 35 より小さいときに円同士が重なります。四角い範囲で調べる方法もありますが、円には中心間距離が形と対応します。
dist(ディスト)は二点間の距離を求めます。dist(x1, y1, x2, y2) では、1 つ目の点 (x1, y1) から 2 つ目の点 (x2, y2) までの距離を返します。ここではプレイヤーと目標の中心座標を渡し、結果を distance へ入れています。@score += 1 は、今の得点へ 1 を足す書き方です。
18.3 結果をすぐ画面へ返す
目標とスコアを描きます。次の変更部分は、18.1 の drawの最後、プレイヤーを描く ellipse のあとへ足します。
fill(250, 190, 70)
ellipse(@target_x, @target_y, 30, 30)
fill(255)
textAlign(LEFT, TOP)
textSize(20)
text("score: #{@score}", 16, 16)
触れた瞬間に目標が移動し、スコアが増えます。操作、判定、反応が 1 フレームの中でつながりました。これがゲームの最小の循環です。
得点まで動く完成コード
ここまでの変更部分を一つへまとめます。最初の目標だけはプレイヤーの右 80 ピクセルへ置き、右矢印キーを押せば必ず最初の得点を試せるようにしています。2 個目からは乱数で場所を決めます。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
@target_x = width / 2 + 80
@target_y = height / 2
@score = 0
end
def draw
@player_x -= 4 if keyIsDown(LEFT_ARROW)
@player_x += 4 if keyIsDown(RIGHT_ARROW)
@player_y -= 4 if keyIsDown(UP_ARROW)
@player_y += 4 if keyIsDown(DOWN_ARROW)
@player_x = constrain(@player_x, 20, width - 20)
@player_y = constrain(@player_y, 20, height - 20)
distance = dist(@player_x, @player_y, @target_x, @target_y)
if distance < 35
@score += 1
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
end
background(25, 30, 48)
fill(90, 210, 180)
ellipse(@player_x, @player_y, 40, 40)
fill(250, 190, 70)
ellipse(@target_x, @target_y, 30, 30)
fill(255)
textAlign(LEFT, TOP)
textSize(20)
text("score: #{@score}", 16, 16)
end
18.4 制限時間より先に終わり方を選ぶ
スコアが 5 になったら終了する作品を作ります。終了を表す値を 1 行だけ足しても、draw がその値を読まなければ画面は変わりません。次の完成コードでは、@finished を用意し、得点が 5 になったら true へ変えます。draw の最初で @finished を調べ、終了中は終了画面を描いて return でそのフレームの残りを行いません。
def setup
createCanvas(600, 360)
@player_x = width / 2
@player_y = height / 2
@target_x = width / 2 + 80
@target_y = height / 2
@score = 0
@finished = false
end
def draw
if @finished
background(45, 35, 70)
fill(255)
textAlign(CENTER, CENTER)
textSize(32)
text("5個みつけた!", width / 2, height / 2)
return
end
@player_x -= 4 if keyIsDown(LEFT_ARROW)
@player_x += 4 if keyIsDown(RIGHT_ARROW)
@player_y -= 4 if keyIsDown(UP_ARROW)
@player_y += 4 if keyIsDown(DOWN_ARROW)
@player_x = constrain(@player_x, 20, width - 20)
@player_y = constrain(@player_y, 20, height - 20)
distance = dist(@player_x, @player_y, @target_x, @target_y)
if distance < 35
@score += 1
if @score >= 5
@finished = true
else
@target_x = random(40, width - 40)
@target_y = random(40, height - 40)
end
end
background(25, 30, 48)
fill(90, 210, 180)
ellipse(@player_x, @player_y, 40, 40)
fill(250, 190, 70)
ellipse(@target_x, @target_y, 30, 30)
fill(255)
textAlign(LEFT, TOP)
textSize(20)
text("score: #{@score}", 16, 16)
end
return は、ここでは「この draw の処理をここで終える」という命令です。終了画面を描いたあとにプレイヤーや目標を重ねず、キーによる位置更新も止められます。すぐもう一度遊ぶには、エディタの実行ボタンを押します。発展として再挑戦キーを作りたくなったら、第15章の場面管理を使い、キー入力で setup と同じ初期値へ戻します。
得点を競わない遊び方も作れます。たとえば、次のように終了条件や表示を変えられます。
- 5 個集めると色が変わり、そのまま続く
- スコアを表示せず、集めた位置だけ軌跡として残る
- 目標から逃げるほど音が静かになる
- 終了条件を置かず、好きなだけ触れる
まず一つの操作に一つの反応を対応させます。遊び直して必要だと感じたルールだけを足します。
同じ移動コードでも、作品の意味は変えられます。
- 集める: 目標へ触れると増える。見つける喜びを作る
- 避ける: 目標へ触れない時間を残す。距離と緊張を作る
- 痕跡を描く: スコアを消し、通った場所へ線を残す。勝敗ではなく動きそのものを見る
選んだルールによって、速さ、探索、軌跡など、遊んでいるときに注目するものが変わります。操作を確かめるときは、「速い操作が得意な人だけ有利になっていないか」「キーを押し続けにくい人も遊べるか」「失敗したとき、責める表示になっていないか」を順に見ます。問題があれば、速度、操作、言葉のうち該当する箇所を直します。
18.5 一人で遊び直す
作った直後は、操作もルールも全部知っています。一度保存して閉じ、翌日に説明を読まず遊び直します。
- 最初の 3 秒で何を押せばよいか分かるか
- 目標へ触れたことが画面から分かるか
- 速すぎる、遅すぎると感じる場所はどこか
- もう一度遊びたい変化が一つあるか
気になった箇所を一つ直してもう一度遊び、前の版と比べます。
18.6 ゲームをもっと育てるなら
今のゲームを作り直さず、変える場所を一つ選びます。
- 第15章の場面管理を使い、開始画面と再挑戦を加える
- プレイヤーの速さを 3 種類作り、いちばん操作しやすいものを選ぶ
- スコアを消し、色、音、軌跡で触れた結果を返す
- 集める代わりに避ける、追いかける代わりに観察するルールへ変える
遊ぶ人に集めてほしいのか、避けてほしいのか、動いた跡を見てほしいのかを決めます。その操作と関係のないルールは外せます。
キーを押したときの速さや得点表示に、もう一度触りたくなる反応があるか遊び直します。次章では、これまでのスケッチから一つ選び、完成版へ近づけます。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス — キー入力、
dist、constrainなどの仕様 - Ruby リファレンスマニュアル「制御構造」 — 条件分岐の構文
第19章 作品を選び、保存する
プログラムは、数字を変えたり機能を足したりすれば、いつまでも編集できます。この章では、今残したい版を決め、あとから開ける形で保存します。
19.1 一番楽しかった寄り道を選ぶ
最終作品は、これまでの章で作ったものを出発点にします。次のどれかを選びましょう。
- 何度も数字を変えたサンプル
- 予想外の結果が出て保存したスケッチ
- もう一つ機能を足したい作品
- 見た目は単純でも、動かしていて気持ちよかった作品
選ぶ基準は技術の多さではありません。自分がもう一度開きたいものを選びます。
19.2 テーマを一語決める
テーマを一語だけ自分で決めます。目に付いた物でも、好きな色でもかまいません。たとえば「ルビー」を選んだなら、次のような考え方があります。
- 赤い宝石の形や光を観察する
- Ruby のコードや記号を図形として扱う
- 赤から連想した熱、夕方、警告、祝祭を描く
- 「大切にしまわれたもの」という物語を作る
テーマから思い付いた言葉を 5 個書き、その中から画面にしてみたいものを一つ選びます。選んだ言葉へ、第6章の反復、第7章の偶然、第8章の操作など、気に入った仕組みを一つ組み合わせます。
19.3 変更を一つずつ保存する
大きく直す前に、現在の版を保存します。ファイル名には、意味のある短い違いを入れます。
orbit-slow.html
orbit-fast.html
orbit-blue-trail.html
final、final2、final-final では、あとから違いが分かりません。「速い」「青い軌跡」のように画面の違いを名前へ入れると、比較しやすくなります。
19.4 引き算で主役を確かめる
機能を足す前に、一つを一時的に消してみます。
- 背景の模様を消す
- 音を止める
- スコア表示を隠す
- 円の数を半分にする
- 色を 3 色へ減らす
消したことで形や色の組み合わせが崩れたなら、その要素には残す理由があります。何も変わらない、または見やすくなったなら、その要素を使わない版も比べられます。削除する前と後を比べると、どの形、色、動きへ最初に目が向くかを確かめられます。
19.5 時間を置いたセルフレビュー
第三者の講評がなくても、時間差を使って見直せます。
- 現在版を保存する
- エディタとメモを閉じる
- 数時間後または翌日に作品だけを開く
- 最初に見た場所、最初に試した操作を記録する
- 好きなところを一つ選ぶ
- 分かりにくいところがあれば、一つだけ直す
セルフレビューは採点ではありません。「良い作品か」ではなく、「自分はどこをもう少し触りたいか」を探します。何も直したくなければ、その版を区切りにできます。
19.6 最終制作「まわる光」
一つの作品が、三つの版を通って変わる過程を見ます。一度に一つだけ変更し、採用しなかった案も記録する例です。
版 A: まず動きだけ作る
最初の問いは「同じ場所へ戻る動きを、ずっと見ていたくなるか」です。黄色い円を一つだけ周回させます。
def setup
createCanvas(500, 320)
@angle = 0
end
def draw
background(20, 28, 52)
@angle += 0.03
x = width / 2 + sin(@angle + 1.5708) * 110
y = height / 2 + sin(@angle) * 80
noStroke
fill(255, 210, 90)
ellipse(x, y, 42, 42)
end
この版では、速さ 0.03 と横・縦の広がり 110、80 だけを試します。作者メモは「円が何の周りを回っているのか分からない。でも、楕円の軌道は残したい」です。
左右と上下の両方に sin を使っています。ただし左右側だけ、角度へ 1.5708 を足しました。これは円の 4 分の 1 周ほど変化の始まりをずらす数字です。二つの波の始まる位置をずらすと、円が斜めに行ったり来たりせず、中心の周りを回ります。110 と 80 を掛けることで、横長の軌道になります。0.5.1 で本文と同じコードを使えるよう、ここでは新しい cos を増やさず、第7章で確認した sinだけで組み立てます。
版 B: 中心を見えるようにする
次の 2 行は変更部分です。版 A の draw の最後の end より前へ足します。
fill(120, 190, 255, 80)
ellipse(width / 2, height / 2, 120, 120)
中心へ半透明の青い円を置くと、黄色い円がその周りを巡っているように見えます。採用しなかった案は、背景へ星を 50 個足すことでした。試すと中心より星へ目が移ったため、この版では星を外しました。
図 19-1 と図 19-2 は、中心の青い円があるかどうかだけが大きな違いです。小さな追加でも、黄色い円を「一つで動く形」と見るか、「何かの周りを巡る光」と見るかが変わります。
版 C: 速さと題名を選ぶ
@angle += 0.03 を @angle += 0.015 へ変えると、待つ時間が長くなります。速い版と遅い版を別名で保存し、見比べます。ここでは遅い版を選び、題名を「まわる光」としました。
最終メモは次の 3 行です。
暗い場所の中心を、黄色い光が見回る作品です。
中心の円を半透明にし、輪郭が強く目立たないようにしました。
速い版は落ち着かなかったため、動きを半分の速さにしました。
版 A で動きを確かめ、版 B で中心の円を足し、版 C で速さを比べて一つ選びました。自分の作品でも、色、数、入力、音などから一項目ずつ比べられます。
19.7 単一 HTML を別の環境で開く
rbCanvas/p5 は、プログラムと素材を単一の HTML ファイルとして保存できることを特徴にしています。保存したファイルは、エディタを介さずブラウザで実行できます。
エディタの保存ボタンを押すと、ブラウザが HTML ファイルをダウンロードします。保存場所が表示されない場合は、ブラウザのダウンロード一覧を確認します。もう一度編集したいときは、その HTML ファイルを rbCanvas/p5 エディタへドラッグ&ドロップします。画像や音声を取り込んでいた場合も、同じファイルから復元されます。
保存後は次を確認します。
- エディタを閉じてもブラウザで開けるか
- 画像や音声が欠けていないか
- キーボードとマウスの操作が反応するか
- 音声開始にクリックが必要なら、画面に説明があるか
- 強い点滅や急な音量変化がないか
ブラウザごとに保存や読み込みの挙動が異なる場合があります。Chrome で動いても、別のブラウザで同じとは限りません。公開するなら、少なくとも自分が対応対象と決めた環境で確認します。
19.8 工夫を短い文章にする
作品を公開するとき、テーマをどう考え、どこを工夫したか添えることがあります。制作中に実際に選んだ形、色、動きを短く書けば伝わります。
テーマを「暗い場所で見つける赤い光」と考えました。
同じ形を並べ、マウスに近い形だけ明るくなるようにしました。
赤が強すぎると光に見えなかったので、周囲を暗い青へ変えました。
「何を考えたか」「コードで何を変えたか」「試して何を選んだか」の 3 点を書くと、制作の過程が伝わります。実際に試したことから言葉を選びます。
19.9 公開しない選択と公開する責任
作品は自分の端末へ保存したところで完成にできます。公開は、そのあとに選べます。
公開する場合は、コード以外の条件も確認します。
- 写真、音、フォント、3D モデルを公開できるか
- 必要な作者名とライセンスを表示したか
- 他人の個人情報が含まれていないか
- 操作方法を短く書いたか
- 動きや音を止める手段が必要か
- 公開するファイルが、最後に動作を確かめた版と同じか
タイトルや制作意図には、「マウスの速さを色にしました」のように、何を試したかを 1 文で書きます。後日ファイルを開いたときにも、どんな作品か思い出せます。
借りた素材や参考にしたコードがある場合は、制作メモや作品の説明へ出典を残します。必要な内容は利用条件によって異なりますが、迷ったときは次の項目を記録します。
画像: 作者名「作品名」
入手元: (素材を入手したページのURL)
ライセンス: CC BY 4.0
変更: 色を青へ変更し、背景の一部として使用
コードの参考: 作者名「スケッチ名」
URL: (参考にしたスケッチのURL)
参考にした点: 円を等間隔へ並べる考え方
URL だけでなく、誰の何を、どのように使ったかを残します。本書のコードを改造した場合は「『Ruby で描く、動かす、遊ぶ — rbCanvas/p5 ではじめるクリエイティブコーディング』第6章をもとに改造」のように書けます。
例にあるCC BY 4.0 では、作者名、ライセンスへのリンク、変更したことなどを示すことで、作品を共有したり改変したりできます。実際に素材を使うときは、その素材に表示されたライセンスと条件を確認します。
19.10 作品から変化を一つ選んで試す
p5.js の作品例やProcessing で公開されている作品には、JavaScript や Java で書かれたものもあります。コード全体を rbCanvas/p5 へ移す前に、次の順で観察します。
- 画面のどこが、どのように変化しているかを言葉にする
- 位置、速度、色、反復、入力のどれを使っていそうか考える
- 作品全体ではなく、気になった変化を一つだけ別のスケッチで試す
- 同じ機能名が 0.5.1 のリファレンスにあるか確かめる
見つけた作品は、作品名、作者名、見たページの URL、気になったところ、自分で試したところを制作ノートへ残します。画像やコードを使う場合は、作者が示した利用条件も確認します。考え方から刺激を受けた場合も、制作意図で元の作品に触れると、自分の発見がどこから始まったかを伝えられます。
19.11 保存した作品から始める次の制作
ここまでに、形、時間、入力、Ruby の構造、音、3D、ゲームを使いました。気に入った章へ戻れば、同じコードを別の数字で試せます。
次に試せることには、Ruby の言語機能を詳しく調べる、p5.js の作品を観察する、音を録る、画像を描く、ゲームのルールを変える、といったものがあります。今いちばん気になった章を開き、その章の発展案から試したいものを選んでみましょう。
保存した作品を後日もう一度開いたら、数字を一つ変えて実行してみてください。そこから新しい版を作れます。
参考資料
- rbCanvas/p5 エディタの使い方 — HTML の保存と再編集の手順
- rbCanvas/p5 0.5.1 API リファレンス — 作品で使う機能の 0.5.1 対応状況
- Creative Commons 表示 4.0 国際 — 第三者素材が CC BY 4.0 の場合の表示条件
困ったとき
動かなくなったときは、原因を一度に全部考えず、画面に見えている症状から順番に切り分けます。途中で別の章へ進み、あとから戻る方法もあります。
エディタの基本操作そのものを確認したいときは、先に始める前に エディタの使い方へ戻れます。
1 何も表示されない
最初に、次の最小コードへ戻します。
def setup
createCanvas(400, 300)
background(245)
ellipse(200, 150, 100, 100)
end
これが動けば、エディタと基本描画は使えています。元のコードへ戻すときは、最初から半分ずつ消そうとせず、次の易しい順で試します。
- 直前に変えた 1 か所だけ元へ戻す
- 動いていた HTML ファイルを保存していれば、その版をエディタへ戻す
- 元のコードを別の場所へ残し、上の最小コードへ 1 節ずつ足す
- 慣れてきたら、処理のまとまりごとに先頭へ
#を付け、どのまとまりを外すと動くか調べる
# は、その行を一時的に実行しないコメントにします。元の行を消さずに試せるので、戻すときは # だけを外します。
確認する場所は次の順です。
def setupまたはdef drawに対応するendがあるか- メソッド名の大文字と小文字が合っているか
- 丸括弧、角括弧、ダブルクォートが閉じているか
backgroundが図形のあとに描かれていないか- 図形の座標がキャンバス外ではないか
- 色と背景が同じではないか
2 エラーの行と本当の原因がずれる
Ruby が示す行は、「ここで解釈を続けられなくなった場所」です。原因がその行にあるとは限りません。閉じ忘れた括弧や end は、数行後で発見されることがあります。
エラー行だけでなく、その少し前を確認します。直前に追加した変更を一度戻し、動く状態から 1 行ずつ足し直す方法が確実です。
end が足りない例
def setup
createCanvas(400, 300)
ellipse(200, 150, 100, 100)
最後の end がないため、Ruby はメソッドの終わりを見つけられません。エラーがコードの末尾を示していても、その直前で閉じ忘れた可能性があります。最後へ end を 1 行戻します。
名前が一致しない例
def setup
createCanvas(400, 300)
elipse(200, 150, 100, 100)
end
正しい名前は ellipse ですが、l が一つ足りません。NoMethodError と表示されたときは、メッセージに出た名前と、本文のコードを一文字ずつ比べます。
素材が見つからない例
def setup
@photo = loadImage("sample.svg")
end
コードが正しくても、エディタへ sample.svg を取り込んでいなければ LoadError になります。ファイル名だけでなく、エディタ下部の素材一覧に同じ名前があるか確認します。
3 動くが速すぎる、重い
draw は短い間隔で繰り返されます。1 回の処理が軽くても、要素が増えると負荷が上がります。
- キャンバスを小さくする
- 描く要素数を半分にする
- 画像や 3D モデルの数を減らす
- 配列へ要素を増やし続けていないか確認する
- 画面外の要素を
selectやrejectで取り除く
速さを意図的に落とす場合は frameRate を試せます。ただし処理自体が重い場合、設定した値まで出ないことがあります。
4 画像や音が読み込めない
- ファイル名の大文字と小文字を確認する
- 拡張子を含む名前が一致しているか確認する
- エディタへ素材を取り込んだか確認する
loadImageやloadSoundをsetupの中で一度だけ呼んでいるか確認する- 音は画面をクリックしてから再生する
- 別形式の素材で試す
素材を使わない図形だけの最小コードが動けば、問題は Ruby 全体ではなく素材読み込み周辺に絞れます。
5 元へ戻るための保存
大きな変更の前に、動く版を別名で保存します。
waves-01.html
waves-02-color.html
waves-03-mouse.html
番号だけでなく、何を変えたか短く入れると戻りやすくなります。失敗版も、画面が面白ければ別名で残せます。
6 動かなくなった場所を絞る
資料を検索するときは、「動きません」だけでなく、画面に出た言葉や使った命令の名前も手掛かりになります。まず次の 4 点を自分用のメモへ書きます。
- 何を表示または動かそうとしたか
- 実際に表示された画面やエラーメッセージ
- 最後に動いていたのはどの状態か
- そのあとに変えた 1 か所はどこか
エラーメッセージの一部と、ellipse や loadImage などの命令名を組み合わせて検索します。誰かへ質問を公開しなくても、最後に変えた場所まで戻ることで原因を見つけられる場合があります。
7 保存したファイルが見つからない
保存ボタンを押しても、保存場所を選ぶ画面が出ないブラウザがあります。その場合は、ブラウザのメニューから「ダウンロード」を開きます。ファイル名の横にあるフォルダの印から、保存された場所を表示できることがあります。
同じ名前で何度も保存すると、ファイル名の末尾に番号が付く場合があります。更新日時を見て、最後に保存したものを確かめます。
8 リファレンスの例をそのまま貼っても動かない
p5.js 公式リファレンスの例は JavaScript で書かれています。rbCanvas/p5 では Ruby を書くため、波括弧 { }、行末のセミコロン ;、let や function などはそのまま使えません。
まず同じ機能名をrbCanvas/p5 0.5.1 API リファレンスで探します。見つからなければ、その機能が現在の rbCanvas/p5 では使えない可能性もあります。本書の動く最小コードへ戻し、試したいメソッドを一つだけ足して確認します。
おわりに
最初の章で変えた数字を覚えているでしょうか。思い出せないときは、もう一度コードを開くと確かめられます。
この本では、コードを暗記する前に「これを変えたらどうなるだろう」と試してきました。予想どおりの画面も、崩れてできた不思議な模様も、保存しておけば次の作品で見返せます。
本書は一人でも進められるように作りました。作品を誰かへ見せるか、自分のパソコンへ保存しておくかは、自分で決められます。
次に rbCanvas/p5 を開いたら、好きな色を置く、昨日のコードから 1 行消す、気になった動きを試す、のどれかから始めてみてください。
著者
町田哲平
フィヨルドブートキャンプメンター
PR
フィヨルドブートキャンプについて
「ほかの人のコードを見たい」「自分のコードへ意見がほしい」「チームで一つのものを作ってみたい」と感じたときは、誰かと学べる場所を探す方法もあります。
フィヨルドブートキャンプは、プログラミング未経験者も対象にしたオンラインプログラミングスクールです。Rails エンジニアコースとフロントエンドエンジニアコースがあり、現役ソフトウェアエンジニアによるレビュー、受講生や卒業生と学ぶコミュニティ、仕事を想定したチーム開発、自作 Web サービスの公開へ取り組めます。
この本で「一つ変えて、実行して、結果を見る」ことを楽しめたなら、その好奇心は Web サービス作りにもつながります。エンジニアという仕事に興味を持ったときは、フィヨルドブートキャンプの学習内容を見るところから始めてみてください。
この本はフィヨルドブートキャンプの教材ではありませんが、プログラミングをもっと深く続けたくなったときの学びの場として紹介します。