Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

はじめに

この本について

『Rubyで描く、動かす、遊ぶ — rbCanvas/p5ではじめるクリエイティブコーディング』は、プログラミングを初めて試す人が、コードで絵、動き、音、遊べる作品を作るための本です。ブラウザで動くrbCanvas/p5を使うため、最初に難しい開発環境を用意する必要はありません。

最初は、丸を一つ描く短いコードから始めます。数字を一つ変えると丸が大きくなり、もう一つ変えると画面の外へ飛び出します。そこから色、模様、アニメーション、マウスやキーボードへの反応、文字、画像、音、3D、ゲームへ少しずつ広げます。最後には、気に入った仕組みを選び、自分の作品として保存します。

この本が目指すのは、Rubyの文法をたくさん覚えることではありません。プログラミングで遊び、好きになり、次も試したくなることです。講師や学習仲間がいなくても進められるよう、初めて出る言葉や命令は、使う場所で一つずつ説明します。

クリエイティブコーディングとは

クリエイティブコーディングは、プログラムを使って絵や動き、音、遊べる仕組みなどを作る活動です。

たとえば、画面に丸を一つ描きます。その丸を動かすと、生き物のように見えるかもしれません。たくさん並べると模様になり、半透明にして重ねると、思いがけない色が生まれます。マウスの動きへ反応させれば、見るだけだった絵が、触って変えられる作品になります。

ここで使うコードは、コンピューターへの命令であると同時に、絵筆や楽器のような制作道具でもあります。

普通の絵では、紙に引いた線がそのまま残ります。コードで描いた線には、「少しずつ右へ動く」「毎回違う色になる」「クリックされたら増える」といった振る舞いを加えられます。形だけでなく、時間の流れや人の操作も作品の一部にできることが、クリエイティブコーディングの面白さです。

クリエイティブコーディングには、一つの正解や決まった完成形がありません。同じコードから始めても、変える数字や色、残した偶然によって、違う作品になります。きれいな画面だけが成功でもありません。形がはみ出したり、動きが速すぎたりして生まれた不思議な画面も、次のアイデアになります。

この本では、次の小さな繰り返しを大切にします。

  1. まず動かす
  2. 一つだけ変える
  3. 何が起きたか見る
  4. 面白ければ、もう一度変える

仕組みを全部理解してから作り始める必要はありません。作りながら疑問を見つけ、必要になったところで仕組みを知れば大丈夫です。

今すぐ描いてみたい人へ

歴史や仕組みはあとから戻って読めます。エディタを開く準備へ進み、第1章の最初のコードを動かしてください。最初の円が出るところまで、およそ5分です。

PR|フィヨルドブートキャンプからのお知らせ

この本でプログラミングに興味を持ち、「もっと本格的に学びたい」「将来はエンジニアとして働きたい」と思った人には、オンラインプログラミングスクールのフィヨルドブートキャンプという選択肢があります。プログラミング未経験から始められ、現役ソフトウェアエンジニアのレビューを受けながら、Web開発、チーム開発、自分で考えたWebサービスの公開まで進みます。この本はフィヨルドブートキャンプの教材ではありませんが、プログラミングをもっと深く続けたくなったときの学びの場として紹介します。

この本で使う rbCanvas/p5

ここからの歴史と仕組みは、少し詳しく知りたいときの読み物です。最初の作品を作ったあとに戻ってもかまいません。

rbCanvas/p5 は、ブラウザで Ruby を実行し、絵やアニメーションを作れる環境です。インストールせずに始められます。画面へ描画する部分には、クリエイティブコーディングのための JavaScript ライブラリである p5.js の機能が使われています。

rbCanvas/p5 には、内部の仕組みが異なる版があります。本書では、安定して動作し、公式の操作説明とAPIリファレンスがそろっている0.5.1のOpal版を使います。2025年4月には0.7.2のruby.wasm版も暫定公開されましたが、本書のコードと画面は0.5.1を基準にします。

Rubyは、人が読み書きするために作られた

Ruby は、まつもとゆきひろさんが作り、1995年に公開したプログラミング言語です。まつもとさんは、いくつもの好きな言語の特徴を組み合わせ、人にとって自然に読み書きできる言語を目指しました。

コンピューターが理解しやすいことだけでなく、コードを書く人が楽しめることも大切にしている点は、この本で Ruby を使う理由の一つです。

p5.jsは、コードを表現の道具にする流れから生まれた

p5.js は、アーティストでプログラマーの Lauren Lee McCarthy(ローレン・リー・マッカーシー)さんが2013年に作り始め、Evelyn Eastmond(エブリン・イーストモンド)さんが立ち上げに加わりました。無料で利用、改良、再配布できるオープンソースの道具で、現在は世界中の人が開発へ参加しています。

その前には、Casey Reas(ケイシー・リース)さんと Ben Fry(ベン・フライ)さんが2001年に始めた Processing があります。Processing は、アーティストやデザイナーもコードを視覚表現に使いやすくする環境です。p5.js はその考え方を受け継ぎ、ウェブブラウザで作品を作れるようにした JavaScript のライブラリとして始まりました。

作者が一人ですべてを完成させ、そのまま残っているわけではありません。使う人が問題を報告し、説明を書き、機能を直すことで育ってきた道具です。プログラムには、作った人の考えと、それを育てたコミュニティの歴史があります。

rbCanvas/p5は、Rubyとp5.jsの間に橋を架けた

p5.js のサンプルは、本来 JavaScript で書きます。rbCanvas/p5 は、その多くの機能を Ruby の書き方で使えるようにしたプログラミングツールです。rbCanvasプロジェクトの作者は、まず自分でも使いたいと思えること、サンプルをすぐ編集して試せること、プログラミングの面白さを伝えることを考えながら開発しています。

公式サイトと開発記事は、どちらも「rbCanvas」という名前で公開されています。開発記事では作者自身が一人称で制作の考えを説明していますが、本書の調査では個人名を確認できる公式資料を見つけられませんでした。そのため、確かめられない名前を推測せず、本書では作者を「rbCanvasプロジェクトの作者」と表します。道具の作者を調べるときも、検索結果だけで決めず、公式サイトや本人の発表まで確かめることが大切です。

本書で使うrbCanvas/p5 0.5.1は、RubyのコードをJavaScriptへ変換するOpal(オパール)という技術を使います。変換後のJavaScriptがp5.jsを呼び、ブラウザへ絵を描きます。一方、2025年4月に暫定公開された0.7.2は、ruby.wasmを使います。ruby.wasmは、Ruby本体の代表的な実装であるCRubyを、WebAssemblyというブラウザでも実行できる形式にしたプロジェクトです。

Opal版が古いから使えない、ということではありません。作者は、0.5.1には動作の軽さなどの利用価値があると説明し、Opal版とruby.wasm版を用途によって使い分けられる形で併存させる可能性に触れています。本書では、初めて試すときの安定性を優先して0.5.1を選びます。道具は、目的と技術の間で試行錯誤しながら変化します。

この本では、次の 3 つが重なって動いています。

あなたが書く Ruby
        ↓
Opal が JavaScript へ変換
        ↓
rbCanvas/p5 の橋渡し
        ↓
p5.js とブラウザのキャンバス

普通の Ruby と同じ構文を多く使えますが、まったく同じ実行環境ではありません。違いが問題になる場所では、その都度説明します。

流れを短く並べると、Rubyの公開(1995年)、Processingの開始(2001年)、p5.jsの開始(2013年)を経て、Rubyでp5.jsを扱うrbCanvas/p5が作られました。rbCanvas/p5では、0.5.1のOpal版に加え、2025年に0.7.2のruby.wasm版が暫定公開されました。新しい版がいつも唯一の正解なのではなく、軽さや安定性など、作りたいものに合う道具を選びます。

作品を見て、問いを持つ

クリエイティブコーディングの歴史は、道具の年表だけでなく、作品を見ると身近になります。

Casey Reasさんの「Process」シリーズでは、形が動くための短い規則を決めると、実行するたびに異なる画面が生まれます。作品を見たら、「どんな形があるか」「どんな規則で動きそうか」「一つだけ自分のコードへ持ち帰るなら何か」を考えてみましょう。

Lauren Lee McCarthyさんの「SOMEONE」では、人がスマートホームの音声アシスタント役になり、離れた家の中を見守って話しかけます。便利さだけでなく、見られること、信頼すること、機械の代わりを人が務めることも作品の題材です。「この仕組みを使う人はどんな気持ちか」「自分なら何を問いかけたいか」を考えると、コードは見た目を作るだけでなく、考えを伝える材料にもなります。

最初から難しい作品をまねる必要はありません。気になった色、反復、動き、問いのどれか一つを、自分の小さな実験へ移せば十分です。

プログラミングが初めてでも大丈夫

この本は、年齢を問わず、コードを一度も書いたことがない人を対象にしています。defend、丸括弧、カンマといった記号も、最初に出てきた場所で説明します。知らない言葉が出てきても、前もって勉強する必要はありません。

これまでに学んでいない数学が出てくることもあります。公式を覚えたり、計算問題を解いたりする必要はありません。まずコードをそのまま動かし、数字を一つ変え、画面で何が起きるかを見ます。仕組みを詳しく知りたくなったら、説明を読めば先へ進めます。

コードは、最初から自分ですべて入力しなくてもかまいません。まず掲載コードをコピーして実行し、画面が変わることを確認します。そのあと、指定された 1 か所だけを変えます。「どの行を変えたため、画面のどこが変わったか」を見ることが、最初の学習です。

エラーが表示されても、壊してしまったわけではありません。直前に変更した 1 か所を元へ戻せば、動いていた状態へ帰れます。戻し方が分からないときは、付録Aのエディタで困ったときを使えます。

3 通りの読み方

最初から順に読まなくてもかまいません。

まず遊ぶ読み方では、コードをコピーして実行し、「変えてみる」の箇所だけ試します。説明は気になったときに戻って読めます。

順番に作る読み方では、第1章から進みます。前の章で使った考え方が、次の章の動きへつながります。

好きなものから選ぶ読み方では、最初の 3 章を終えたあと、興味に近い章へ移ります。動きなら第4章、マウスなら第8章、文字なら第10章、ゲームなら第18章が入口です。

一人で進められる設計

この本は、講師や学習仲間がいない状態を標準にしています。誰かに作品を見せたり、採点してもらったりしなくても最後まで進めます。

動かなくなったら、付録Aのエディタで困ったときを開いてください。作品を見直すときは、一度保存して閉じ、時間を置いてからもう一度開きます。昨日の自分が作った作品は、今日の自分にとって十分に新しい観察対象になります。

コピーから始めてよい理由

最初から全部を入力しても、プログラミングが上手になるとは限りません。動くものを手元に置き、少し変えて結果を比べるほうが、コードと画面の関係を早く発見できます。

この本のサンプルコードはコピーしてかまいません。変えた場所が一つでもあれば、そこから自分の実験が始まっています。予想外の画面になったら、すぐ元へ戻さず、少し眺めてみてください。バグが新しい模様を教えてくれることもあります。

最初の目的地は、わずか 5 分先です。第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 画面の左右を見る

エディタを開くと、コードを書く場所と、実行結果を見る場所があります。

左側に最初からコードが入っていても、消して書き直してかまいません。本書のコードは、コードブロック右上のコピーボタンでコピーし、エディタへ貼り付けられます。

rbCanvas/p5 0.5.1エディタの実画面。左側にRubyコード、右側に薄いクリーム色のキャンバスと赤い円の実行結果が表示され、上部にヘルプ、ツール、保存、実行の各ボタンが並んでいる
図 0-1 0.5.1エディタ。左でコードを書き、上部の三角形の実行ボタンを押すと、右側へ結果を表示できる。

0.2 実行して、止めて、もう一度実行する

三角形の印が付いた実行ボタンを押すと、コードが読み取られ、結果が表示されます。古い版では新しいタブが開く設定もあり、ツールメニューの「実行画面」で、エディタ右側へ表示するか新しいタブへ表示するかを選べます。

コードを直したあと、画面が自動で変わらないときは、もう一度実行ボタンを押します。右側に実行結果がある場合は、もう一度三角形の実行ボタンを押すと、新しい結果へ入れ替わります。実行結果が別タブで開いた場合は、そのタブを閉じます。

最初は次の小さな往復を繰り返します。

  1. コードを 1 か所だけ変える
  2. 実行ボタンを押す
  3. 画面のどこが変わったか見る
  4. 気に入らなければ元へ戻す

一度に一つだけ変えると、コードと結果のつながりを見つけやすくなります。

0.3 新しく始める前に保存する

保存ボタンを押すと、作品が HTML ファイルとしてダウンロードされます。HTML はウェブページに使われるファイル形式ですが、ここでは「コードと実行に必要なものを入れた作品ファイル」と考えれば十分です。

画像や音声を取り込んだ作品では、それらも一つの HTML ファイルに含めて保存されます。保存先はブラウザの設定で変わります。見つからないときは、まずブラウザの「ダウンロード」一覧を開いてください。

新規作成やブラウザの再読み込みをすると、保存していない変更は失われることがあります。新しく始める前に、動いている版を保存しておくと安心です。

0.4 保存した作品を開く

保存した HTML ファイルには、二つの開き方があります。

  • ファイルをブラウザで開くと、作品を直接実行する
  • ファイルを rbCanvas/p5 エディタへドラッグ&ドロップすると、コードを再び編集する

ドラッグ&ドロップは、ファイルをマウスやトラックパッドでつかみ、エディタの上まで運んで放す操作です。画像や音声を取り込むときにも同じ操作を使います。取り込んだ素材はエディタ下部に小さな画像や名前で表示されます。

0.5 エラーは「直す場所の手掛かり」

実行しても絵が出ず、文字のメッセージが表示されることがあります。これはエディタが壊れたのではなく、Ruby がコードを最後まで読めなかったか、実行中に困ったことを知らせています。

最初に見るのは、メッセージにある行番号です。ただし、本当の原因が一つ前の行にある場合もあります。次の順で確かめます。

  1. 直前に変えた 1 か所を見る
  2. end、丸括弧、引用符の閉じ忘れを見る
  3. メソッド名の大文字と小文字を見る
  4. 直前の変更を元へ戻して実行する

詳しい症状別の戻り方は、付録Aのエディタで困ったときにあります。

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 で確認した書き方を先に示します。自分で新しい機能を探すときは、次の順に確認すると迷いにくくなります。

  1. rbCanvas/p5 のヘルプで機能名を探す
  2. 短いサンプルをそのまま実行する
  3. 引数を一つだけ変える
  4. 必要なら p5.js 公式リファレンスで図や説明を補う

意味が分からない名前を見つけても、それは失敗ではありません。今は使わず、名前だけ覚えて先へ進んでもかまいません。

15分のリファレンス宝探し

リファレンスを調べる練習は、大切な作品とは別の新しいエディタで行います。

  1. rbCanvas/p5 0.5.1 APIリファレンスを開き、名前や見出しを眺める
  2. 気になったものを一つ選び、「何が起こる命令だろう」と予想する
  3. 短いサンプルがあれば、まず変えずに実行する
  4. 数字や色などを一つだけ変え、もう一度実行する
  5. 予想と実際に起きたことを比べる

図形や色のほかにも、文字、画像、マウスやキーボード、音、3Dなどの入口があります。端末やブラウザによって動かない機能もあります。動かなかったときは自分の間違いと決めつけず、「0.5.1にもある機能かな」「この端末で使えるかな」と確かめます。

見つけた名前:
起こりそうだと思ったこと:
一つ変えたところ:
実際に起きたこと:

この4行を制作ノートへ残せば、すぐ作品に使わなくても発見は消えません。本編でも、章で覚えたことの近くにある機能を探す小さな課題が登場します。

準備はこれで終わりです。第1章では、実際に一つの円を変えます。

参考資料

第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 として読み取り、最初から動かし直す合図です。数字を変えただけでは右側が変わらない場合があります。そのときは、もう一度このボタンを押します。

この短いコードには、初めて見るメソッドが 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 が高さです。

最初の長いコードにある ellipse の、2つの 120220 へ変えてください。次の1行は変更後の部分です。

ellipse(200, 150, 220, 220)

もう一度実行すると、円が大きくなります。コード中の数字は、覚える対象ではなく、画面を動かすつまみです。

薄いクリーム色の400×300のキャンバス中央に、直径120の赤い円が描かれている実行結果
図 1-1 変更前は円の幅と高さが 120。
薄いクリーム色の400×300のキャンバス中央に、直径220の大きな赤い円が描かれている実行結果
図 1-2 同じ位置のまま、幅と高さを 220 へ変更。

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 はメソッドの終わりを見つけられません。エラーが出たら、コードが壊れた証拠ではなく、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(ノー・ストローク)は、このあと描く形の輪郭線を表示しないメソッドです。同じ背景色の楕円を上に重ねたため、手前の形が穴のように見えます。特殊な月の命令は使っていません。楕円と描画順だけで三日月が生まれました。

濃紺の400×300のキャンバスに、黄色い円の一部を背景色の円で隠して作った三日月が描かれている実行結果
図 1-3 2つの楕円の重なりだけで作った三日月。

好きな画面になったら、そこで止めてかまいません。次章では、数字に名前を付け、同じ値を何度も使う方法を試します。

丸を生き物へ変える

丸を練習で終わらせず、顔とも機械とも分からない生き物へ育てられます。次のコードを、三日月の例とは別の新しいコードとして試します。

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行へ戻ります。

参考資料

第2章 数字は形を動かすつまみ

第1章では、ellipse の数字を直接変えました。数字が 1 か所だけなら、それで十分です。同じ数字を何度も使い始めると、「どれが中心の位置だったか」を探す時間が増えます。変数は、この探し物を減らし、作品の意図に名前を付けます。

キャンバスでは、左上が座標 (0, 0) です。右へ進むほど x が増え、下へ進むほど y が増えます。

左上を原点として、右へx、下へyが増えるキャンバスの座標図
図解 2-A 画面上の位置は、横の 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_yy は縦の場所を表します。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_x260 へ変えると、顔と両目がまとめて右へ動きます。

薄い灰色の400×300のキャンバス中央に、赤い丸い顔と白黒の両目が描かれている実行結果
図 2-1 中心の値をもとに顔と両目を配置。

数字を減らすこと自体が目的ではありません。「中心からどれだけ離すか」という関係がコードに現れることが重要です。

2.3 キャンバス自身に中心を聞く

幅 400 の半分は 200、高さ 300 の半分は 150 です。キャンバスの大きさを変えるたびに計算し直す代わりに、rbCanvas/p5 が持つ widthheight を使えます。

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

width は現在のキャンバスの幅、height は高さです。キャンバスを幅 600、高さ 360 に変えても、円は中央へ置かれます。

固定値 200 を使う方法が間違いなのではありません。キャンバスのサイズに関係なく中央へ置きたいなら、width / 2 のほうが意図に合います。左から必ず 200 の場所へ置きたいなら、固定値のほうが正確です。

2.4 座標は画面の左上から始まる

キャンバスの原点(位置を数え始める場所)は左上です。そこでは x と y がどちらも 0 です。x は右へ進むほど大きくなり、y は下へ進むほど大きくなります。

(0, 0) ─────────→ x
  │
  │
  │
  ↓ y

紙のグラフでは y が上へ増えることが多いため、最初は逆に感じるかもしれません。画面は上から下へ行を並べて表示してきた歴史があり、ブラウザの 2D 座標も左上を基準にします。

座標を暗記する代わりに、次の変更部分を setup の中の最後へ一時的に足すと確かめられます。

fill(255, 0, 0)
ellipse(0, 0, 120, 120)

左上に、大きな円を4分の1に切ったような形が見えます。円の中心が (0, 0) にあり、残りがキャンバスの外へ出ているためです。直径を120にしているのは、キャンバス内へ残った部分を見つけやすくするためです。

薄い灰色の400×300のキャンバス左上に、直径120の赤い円の右下4分の1が大きな円弧として見えている実行結果
図 2-2 中心は左上の角にある。赤い円のうち、右下の4分の1だけがキャンバス内に見える。

図のほとんどが何も描かれていない灰色に見えるのは、エラーではありません。円の中心を左上の角へ置いたため、直径120の円のうち、キャンバス内に入った右下部分だけが見えています。見えている円弧の角、つまり画像の左上が円の中心です。

2.5 名前はあとから変えてよい

最初から完璧な変数名を考える必要はありません。x で始め、目の位置だと分かったら eye_x へ変えられます。短い実験では x が読みやすく、要素が増えた作品では left_eye_x が役立つこともあります。

今のコードを壊したくなければ、まず複製を保存します。そのうえで、center_xwidth / 4width * 0.75 に変えてください。計算式が構図をどう動かすかを見た経験が、次章の色と重なりにもつながります。

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の順で渡します。窓を一つだけ消す、建物を極端に細くする、月をビルの後ろへ移すと、街の物語が変わります。

濃紺の夜空に、三つの建物、黄色く光る四つの窓、右上の月を配置した実行結果
図 2-3 円、四角形、三角形を座標で組み合わせた夜の街。

この章の小さな区切り

  • 円の位置を左右か上下へ動かせた
  • width / 2height / 2で中央を使えた
  • 余裕があれば、夜の街の窓か建物を一つ変えた

座標を一つ変えて画面の違いを確認できれば、保存して次章へ進めます。

参考資料

第3章 色と重なりで画面を作る

形の種類が少なくても、色と描画順が変われば別の画面になります。第1章の三日月は、背景と同じ色の楕円を重ねて作りました。この章では、色を「赤、青」と名付けるだけでなく、数値として混ぜ、透明度で重ねます。

半透明の赤、青、黄の円が重なり、重なった部分が別の色に見える図
図解 3-A 透明な色は、下にある色と目の中で混ざる。

3.1 RGB は 3 本の光の量

fill(235, 90, 120) の 3 つの値は、赤、緑、青の光の強さです。RGB は Red、Green、Blue の頭文字です。通常は 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-1 RGB の各成分だけを最大にした3つの円。

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-2 半透明の円が重なった場所に新しい色が生まれる。

3.3 background を毎回塗る意味

この章では setup の中だけで描いているため、背景は 1 回塗れば足ります。第4章から draw を使うと、同じ画面を何度も描き直します。そのとき background をどこへ置くかで、前のフレームを消すか残すかが変わります。

今は、描画順だけを変えて比べます。次の変更部分を新しい setup の中へ入れ、先に createCanvasbackground を実行してから試します。

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. 背景を 1 色で塗る
  2. 同じ形を主役色で 3 つ以上描く
  3. そのうち 1 つだけ差し色へ変える
  4. 位置か大きさを一つだけ崩す

これは正解へ近づく手順ではなく、選択肢を一度減らして遊びやすくする制約です。窮屈に感じたら 4 色目を足してかまいません。

第1部で、形、位置、色を数値として扱いました。第2部では、その数値を時間とともに変え、静止画を動かします。

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-3 三つの色と形を重ねて作った小さなポスター。

この章の小さな区切り

  • RGBの数字を一つ変え、どの色がどう変わったか見つけた
  • 三色の役割を「背景・主役・差し色」から一つ選べた
  • 色だけで見分けにくい場所へ、形・大きさ・位置の手掛かりを一つ足した

いちばん気になった重なりを一つ保存します。次へ進む前に、最初に目が行く場所と、空けておきたい余白を一言だけ残します。

参考資料

第2部 時間と動きを設計する

静止画を何度も少しずつ描き直すと、動きが生まれます。その間に条件や反復を入れると、形は画面の端で戻り、群れになり、揺れ始めます。

第2部で扱うのは、アニメーションの技術だけではありません。「今の値を覚え、次の瞬間に少し変える」というプログラムの基本です。数式を先に覚える必要はありません。動きを見てから、必要な仕組みを確かめます。

第4章 draw が静止画を動かす

アニメーションは、円そのものが動いているわけではありません。少し違う静止画を短い間隔で描き直し、目が動きとして受け取っています。rbCanvas/p5 の draw は、この描き直しを自動で繰り返します。

背景を描く、形を描く、位置を変える、という処理が繰り返される図
図解 4-A `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 が終わると、drawx を見つけられません。

@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-1 背景を毎回消さないと、円が通った時間が軌跡として残る。

4.4 位置と速度を分ける

@x += 22 は、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(ストローク・ウェイト)は線の太さを決めます。星を速くする、尾を長くする、斜め上へ進ませる、のどれか一つから改造できます。円の移動を学んでいるのは同じでも、作品の時間が生まれます。

濃紺の背景を、黄色い尾を引いた丸い流れ星が右へ進む場面
図 4-2 線と円を同じ位置から動かすと、尾を引く流れ星になる。

この章の小さな区切り

  • drawが同じ処理を繰り返すことを画面で確認した
  • @xまたは@speedを変え、動く向きか速さを変えた
  • 円、軌跡、流れ星から、もう一度見たい版を保存した

円が画面外へ消えても失敗ではありません。動いていたことを確認できれば第5章へ進めます。

参考資料

第5章 条件分岐が振る舞いを生む

第4章の円は、画面の外へ出ても進み続けました。端に着いたときだけ向きを変えるには、「もし端を越えたら」という条件が要ります。if は、値を見て次の行動を選ぶ仕組みです。

条件が成り立つ場合と成り立たない場合へ処理が分かれる図
図解 5-A `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

右端で 3-3 になり、円は左へ進みます。しかし左端の条件がないため、そのまま消えます。今入れた if 3行を、次の変更部分と置き換えて両端を一つの条件にまとめます。

if @x > width - 30 || @x < 30
  @speed *= -1
end

|| は「または」です。右端を越えた場合、または左端を越えた場合に速度を反転します。

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 にある fillellipse の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

どちらも同じ速さで右へ出発します。しかし、赤い円は位置を反対側へ移し、青い円は速度の正負を変えます。図では、赤い円が左へ戻ったあと、青い円が右端で跳ね返って左へ進んでいます。

薄い灰色の600×260のキャンバスが横線で上下に分かれ、上段左に赤い円、下段中央付近に青い円がある実行結果
図 5-1 上段は反対側へ戻り、下段は端で跳ね返る。同じ速度でも境界処理で位置が分かれる。

5.5 自分で選ぶ境界の反応

次のうち一つだけ選び、円の端での反応を変えてみてください。難しさを三段階に分けています。

  • そのままでできる: 跳ね返らず、反対側から現れる。5.2のコードを使える
  • ヒントあり: 端に触れるたび速くする。跳ね返すifの中へ@speed *= 1.1を足す
  • 挑戦: 端に触れるたび色を変える。setupで色の変数を用意し、端のifrandomを入れ直す
  • 挑戦: 端でしばらく止める。止まる残り時間を覚える変数が必要になるため、今は動きの案だけメモして先へ進んでもよい

一度に全部を足す必要はありません。「そのままでできる」一つだけでも十分です。ヒントの意味がまだ分からなければ、数字を変える案へ戻ります。条件が一つ増えるだけで、単なる移動が「性格のある動き」へ変わります。次章では、形そのものを繰り返して画面を埋めます。

5.6 跳ね返るものに性格を付ける

端へ触れたときの反応は、作品の登場人物が何を感じたかのように見せられます。たとえば、跳ね返る瞬間だけ目を大きくすると、ただの円が驚く生き物になります。これは数フレーム分の状態を覚える必要がある挑戦課題です。今は絵として想像するだけでもかまいません。反対に、端で速度を半分にすると、壁を怖がっているように見えます。

最初は顔を描くコードを複雑にせず、円の中へ目を2つ足すだけで十分です。「速い性格」「慎重な性格」「端を無視して反対側へ抜ける性格」から一つ選び、条件式を振る舞いの演出に使ってください。

5.7 もっと自然な動きを探すなら

位置と速度に加えて、速度そのものを少しずつ変える値を持たせると、落下や加速のような動きになります。この値を加速度と呼びます。ほかにも、摩擦、重力、ばね、複数のものが集まって動く群れなど、動きから広がる題材があります。

今すぐ式を覚える必要はありません。まず「だんだん速くなる」「止まる前に少し滑る」「壁から離れるほど戻る力が強くなる」のどれかを言葉や矢印で描きます。その一文が、あとで「加速度」「摩擦」「ばね」「ベクトル」を調べる手掛かりになります。

この章の小さな区切り

  • 円が端へ着いたかをifで調べられた
  • 折り返すか跳ね返るかを一つ試した
  • 余裕があれば、速度か見た目で動きへ性格を付けた

端で一度反応するところまで確認できれば、保存して次章へ進めます。

参考資料

第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 個です。

外側の繰り返しが行を進み、内側の繰り返しが列を進む格子の図
図解 6-A 二重の繰り返しを、行と列として見る。

反復を重ねるとコードは短くなりますが、最初は動きを追いにくくなります。行数を 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 行目です。

濃紺の480×360のキャンバスに黄色い円が4行6列で並び、2行目の1個だけが赤い実行結果
図 6-1 24個の規則の中で、条件に合う1個だけを変える。

6.4 同じ間隔からずれを作る

列ごとに y 座標を少し変えると、格子が波打って見えます。二重反復の中にある y = 55 + row * 85 の1行だけを、次の1行と置き換えます。

y = 55 + row * 85 + (column % 2) * 20

% は余りを求める演算子です。column % 2 は、偶数列で 0、奇数列で 1 になります。奇数列だけ 20 下がるため、互い違いの配置になります。

さらに ellipse の大きさへ rowcolumn を使えば、左から右、上から下への変化を作れます。二重反復の中にある ellipse(x, y, 44, 44) の1行を、次の変更部分と置き換えます。

size = 24 + column * 6
ellipse(x, y, size, size)

反復は同じものを量産する命令ではなく、番号を位置、色、大きさへ変換する仕組みです。次章では、番号ではなく角度や乱数から変化を作ります。

6.5 同じ反復から三つの作品を選ぶ

今の行と列のコードは、形を変えるだけで別の風景になります。

  • ellipseのまま、緑の大きさを行ごとに変えると植物園
  • rectへ変え、黄色を一部だけ灯すと夜の窓
  • triangleへ変え、列ごとに傾きを想像して配置すると旗の列

全部を作る必要はありません。一つ選び、まず図形と背景色だけを変えます。そのあと、一個だけ異なる要素を「間違い」ではなく「物語の主役」として置きます。なぜその一個だけ違うのかは、言葉で説明できなくてもかまいません。

6.6 発展:形の中へ同じ形を描く

大きな円の中へ半分の円を描き、その円の中へさらに半分の円を描くと、同じ規則が小さくなりながら続く模様ができます。このように、全体と似た形が一部分にも現れる構造をフラクタルと呼びます。

大きな円から二つの小さな円へ枝分かれし、さらに小さな円へ続く図
図解 6-B 大きさを半分にして同じ処理を呼ぶと、枝分かれが続く。
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より小さくなったら、その呼び出しをそこで終える条件です。この条件がないと、いつまでも小さくしようとして処理を終えられません。

ここは発展なので、仕組みを一度で理解する必要はありません。まずそのまま実行し、240180へ、1224へ変えてください。その次に、円を四角形へ変える、左右ではなく上下へ分ける、色を大きさから決める、といった改造へ進めます。

濃紺の背景に、半透明の紫色の円が左右へ小さく枝分かれしながら重なった模様
図 6-2 同じ処理を小さくして呼び直した円のフラクタル。

この章の小さな区切り

  • timesで同じ形を繰り返せた
  • 行か列の数を一つ変えた
  • 余裕があれば、フラクタルの大きさか分かれ方を変えた

格子模様を表示できれば、再帰を理解しきらなくても保存して次章へ進めます。

参考資料

第7章 周期と偶然のあいだ

機械的に同じ速さで動く円は、動きの仕組みを知るには分かりやすい例です。しかし呼吸、波、風のような動きは、行って戻る変化や、完全には予測できない揺れを含みます。周期と乱数は、その 2 種類の変化を作ります。

規則正しいsinの波と、滑らかに揺らぐnoiseの線を並べた図
図解 7-A `sin` は繰り返す波、`noise` は滑らかで不規則な変化を作る。

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.040.02 へ変えて実行してください。動きが遅くなれば、数字と速さの関係を確認できています。次に元へ戻し、18080 へ変えると、左右へ動く幅が小さくなります。

0.04 を小さくするとゆっくり、大きくすると速く往復します。180 は移動幅です。速度と振れ幅を別の数字で調整できる点が重要です。

7.2 同じ周期を大きさへ使う

同じ wave を位置ではなく大きさへ使います。次の変更部分を、7.1の長いコードにある draw の中身と置き換えます。

wave = sin(frameCount * 0.04)
size = 100 + wave * 40
ellipse(width / 2, height / 2, size, size)

大きさは 60 から 140 の間を往復します。wave をそのまま大きさにすると負の値になる時間があります。中心となる 100 を足すことで、扱いやすい範囲へ移しています。

周期の式は、横移動、透明度、色、回転にも使えます。同じ波を複数の性質へ使うと、画面全体が同じ呼吸を持ちます。別の速さを使うと、少しずつずれる関係が生まれます。

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

上段の赤い点は、同じ波の形を繰り返します。下段の青い点は滑らかにつながっていますが、次の山や谷の大きさは一定ではありません。

薄い灰色の600×300のキャンバスで、上段に規則正しく繰り返す赤い波、下段になめらかで不規則な青い波が小さな点で描かれている実行結果
図 7-2 上段のsinは同じ周期を繰り返し、下段のnoiseは滑らかに揺らぐ。

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 なら、同じ乱数列を得られます。偶然に見える構図を選び、あとから同じ版を作れるようになります。

黒に近い500×300のキャンバス全体へ、大小の黄色い点が星空のように散らばっている実行結果
図 7-1 シードを固定して再現できる100個の点。

第2部では、値が時間で変わる仕組みを見ました。第3部では、変化のきっかけをマウスとキーボードから受け取ります。

7.6 波と偶然を同じ海へ置く

sinで規則的に上下する線を作り、noiseで少しだけ位置を揺らすと、完全な規則でも完全な乱数でもない水面を作れます。さらにrandomで小さな光を散らすと、波、風、反射という別々の役割を同じ画面へ置けます。

ここでは完成コードを一つに決めません。星空のコードを青い背景へ変えて水面にする、往復する円を船に見立てて三角形を足す、noiseの動きを雲へ使う、のどれかから始めます。同じ数の変化が、何に見立てるかで別作品になることを試す節です。

7.7 模様をもっと探すなら

第6章の行数と列数、第7章のsinnoiserandomを組み合わせると、同じコードから規則的な模様と崩れた模様を作り分けられます。まず一つだけ選びます。

  • 行と列の数を増やし、遠くから見た印象を比べる
  • sinを位置や大きさへ使い、並びを波打たせる
  • noiseを色へ使い、少しずつ変わる帯を作る
  • 第6章のフラクタルで、円を四角や枝へ変える

さらに探したくなったら、「セル・オートマトン」「フラクタル」「ジェネラティブアート」「Genuary」という言葉を、画像や作品例から調べられます。見つけた作品を全部まねるのではなく、気になった規則を一つだけ今のスケッチへ持ち帰ります。

この章の小さな区切り

  • randomsinnoiseのどれか一つを画面で確認した
  • 数字を一つ変え、変化の速さか散らばり方を比べた
  • 余裕があれば、星空、水面、雲のどれかに見立てた

三つを全部使わなくても、一つの変化を観察できれば保存して次章へ進めます。

参考資料

第3部 人の操作に応答する

マウスやキーボードから値を受け取ると、作品は見るだけの画面から、触って変えられる道具になります。

ここでいう「人」は、ほかの誰かでなくてもかまいません。作った本人が時間を置いて触り直せば、操作が分かるか、反応が気持ちよいかを確かめられます。第三者による試用は任意です。

第8章 マウスの位置を画材にする

マウスの位置は、ボタンを押すためだけに使うものではありません。横位置を色へ、縦位置を大きさへ変換すれば、動かす手そのものが作品の入力になります。

キャンバス上のポインター位置をmouseXとmouseYで表す図
図解 8-A ポインターの横位置が `mouseX`、縦位置が `mouseY`。

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 軌跡を描くか、現在位置だけを見せるか

backgroundsetup へ移すと、マウスの軌跡が残ります。

def setup
  createCanvas(500, 320)
  background(24, 28, 45)
  noStroke
end

def draw
  red = map(mouseX, 0, width, 80, 255)
  blue = map(mouseY, 0, height, 255, 80)
  fill(red, 120, blue, 50)
  ellipse(mouseX, mouseY, 30, 30)
end

横に動かすと赤の量が、縦に動かすと青の量が変わります。位置を色へ変換しているため、軌跡から手の動きを読み取れます。

濃紺の500×320のキャンバスに、マウスの動きに沿って半透明の色付き円が連続して残っている実行結果
図 8-1 マウスの場所を位置と色の両方へ利用。

8.4 押している間だけ描く

mouseIsPressed は、マウスボタンが押されているかを真偽値で表します。次の draw は変更部分です。8.3の長いコードにある def draw から対応する end までと置き換えます。

def draw
  if mouseIsPressed
    fill(255, 200, 80, 80)
    ellipse(mouseX, mouseY, 36, 36)
  end
end

このコードでは、押している間だけ円を描きます。クリックした瞬間だけ処理したい場合と、押し続けている間処理したい場合は別です。連続した線を描く道具では、押している状態を毎フレーム読む方法が合います。

8.5 一人で操作を確かめる

インタラクションの確認に、第三者は必須ではありません。

  1. 操作説明を別の場所へメモする
  2. スケッチを保存して閉じる
  3. 数時間後または翌日に開く
  4. 説明を見る前に触ってみる
  5. 最初に試した操作と、迷った場所を一つずつ記録する

時間を置くと、作った直後には見えなかった分かりにくさに気づけます。次章では、キーボードで操作を離散的に切り替え、作品が現在どの状態にあるかを覚えます。

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

このブラシは、マウスの場所を中心に斜めの線を置き続けます。線の傾き、長さ、透明度のどれか一つを変えてください。次に、押していない間だけ小さな点を描く、画面上部では細く下部では太くする、といった操作と見た目の関係を考えられます。

描き終えたら、線が密集した場所と、何も描かなかった余白を見ます。全部を埋める必要はありません。速く動かした場所、ゆっくり動かした場所、止まった場所の違いが、線のリズムになります。

操作を作品へ加えるときは、「この作品は誰に何をさせるか」も考えられます。細かなマウス操作が難しい人にも同じ変化が必要か、クリックしなくても一部を見られるか、操作方法を画面へ書くか、の中から一問だけ選びます。すべての人へ同じ操作を強いるのではなく、入口を増やす発想です。

この章の小さな区切り

  • マウスの横または縦位置を、色・大きさ・線のどれかへ渡せた
  • 押している間だけ描く違いを確認できた
  • 自分のブラシで、密な場所と余白の両方を残した版を保存した

参考資料

第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

スペースキーを押すたび、@darkfalsetrue の間で切り替わります。! は真偽値を反転する演算子です。

イベントメソッド名は rbCanvas/p5 の API 表記に合わせて keyPressed と書きます。通常の Ruby の命名慣習とは異なる camelCase ですが、ここで snake_case へ変えると自動で呼ばれません。

9.3 状態は画面の現在を説明する

@dark は背景色そのものを保存していません。「今は暗い場面か」という意味を保存しています。描画側は、その状態から背景色と円の色を決めます。

色を直接何度も書き換える方法もあります。しかし要素が増えると、背景は夜なのに太陽だけ昼の色という、ちぐはぐな状態が起こりやすくなります。状態を一つ持ち、すべての描画をその状態から決めると関係を保てます。

9.4 3 つ以上ならシンボルで名前を付ける

昼と夜の 2 状態は真偽値で表せます。朝、昼、夜の 3 状態になると、truefalse では足りません。Ruby のシンボルで名前を付けます。

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 だけを実行します。背景と円の色を同じ状態から決めているため、「夜の背景なのに昼の円」というずれが起こりません。実行したら、数字の 123 を順に押して画面の変化を確かめます。

状態へ名前を付けると、コードが作品の構成を語り始めます。次章では、文字列を画面へ置き、操作説明だけではない視覚素材として扱います。

濃紺の500×320のキャンバス中央に淡い黄色の円がある、数字の3を押したあとの夜の場面
図 9-1 数字の3を押すと、night状態から背景と円の色を決定する。
水色の500×320のキャンバス中央に黄色い円がある、数字の2を押したあとの昼の場面
図 9-2 同じコードでday状態にすると、背景と円の組み合わせが昼の色へ変わる。

図9-1と図9-2を見比べると、円の位置と大きさは同じまま、状態から選ぶ色だけが変わっています。

9.5 キーで世界の季節を変える

状態は背景色だけでなく、同じ風景の読み方をまとめて変えられます。1を春、2を夏、3を夜として、背景、地面、空に浮かぶものを場面ごとに選びます。

  • 春では小さなピンクの楕円を散らす
  • 夏では黄色い円と濃い影を置く
  • 夜では月と窓の明かりだけを残す

最初は図形の位置を共通にし、色だけを切り替えます。その後、一つの場面にだけ図形を足します。キー入力を「色変更ボタン」で終わらせず、同じ場所に別の時間が流れる作品へ育てられます。

この章の小さな区切り

  • キーを押して画面の色か場面を切り替えられた
  • 今どの場面かを変数で覚えられた
  • 余裕があれば、一つの場面だけへ形を足した

二つの場面を切り替えられれば、三つ目を作らなくても保存して次章へ進めます。

参考資料

第10章 文字は読める図形

画面の文字には 2 つの顔があります。言葉として意味を伝え、同時に大きさ、間隔、配置を持つ図形として見えます。操作説明を表示するだけなら読めることが最優先です。短い詩を動かすなら、読める限界そのものを作品にできます。

10.1 text で一行を置く

def setup
  createCanvas(600, 300)
  background(245)
  fill(35)
  textSize(36)
  text("Ruby で描く", 60, 150)
end

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

CENTER は rbCanvas/p5 が用意する定数です。1 つ目が水平方向、2 つ目が垂直方向の揃え方です。基準点が中央になったため、width / 2height / 2 へ素直に置けます。

濃紺の600×300のキャンバス中央に、白い大きな文字で「ここから動く」と表示された実行結果
図 10-1 横と縦の基準を中央へ揃えた文字。

10.3 文字列補間で値を読めるようにする

プログラム内部の値を表示すると、デバッグにも作品にも使えます。次の draw は変更部分です。10.2の長いコードへ draw がないので、setupend より後ろへそのまま足します。

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 一文字ずつ別の位置へ置く

文字列は chars で文字の配列へ分けられます。

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

文字列を一つの塊ではなく、一文字ずつ位置を持つ図形として扱っています。each_with_index は、要素と番号を同時に受け取る Ruby のメソッドです。

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で大きさの変わる幅を調整します。一度に変えるのは一つだけで十分です。

濃紺の600×300のキャンバス中央で、白い文字の「しずか」が大きく表示された実行結果
図 10-2 0.5.1 Opal版で、sinに合わせて文字サイズがゆっくり変わる途中を撮影した画面。

言葉を長く考える必要はありません。二文字から六文字ほどにし、まず読める状態を保存します。その複製で、位置、大きさ、透明度の一つだけを時間とともに変えます。文字を情報表示として使うだけでなく、意味と動きのずれを楽しむタイポグラフィへ進めます。

この章の小さな区切り

  • 文字を一行表示できた
  • 文字の大きさ、位置、言葉のどれか一つを変えた
  • 余裕があれば、「しずか」の完成コードを動かして速さか振れ幅を変えた

中央に一行表示できれば、動く文字まで進まなくても保存して次章へ進めます。

参考資料

第11章 画像を借りて組み替える

画像を読み込むと、写真、紙の質感、自分の手描き素材をスケッチへ持ち込めます。一方で、ファイルの場所、読み込み時間、著作権という、図形だけの章にはなかった条件が増えます。画像は便利な背景ではなく、出典と文脈を持つ素材です。

11.1 preload で描く前に読み込む

この章には練習用画像 sample.svgが付属しています。リンク先を保存してから、ファイルをマウスやトラックパッドでつかみ、rbCanvas/p5 エディタの上まで運んで放します。この操作をドラッグ&ドロップと呼びます。取り込みに成功すると、エディタ下部に画像の小さな表示が現れます。素材が表示されないときは、付録Aの画像や音が読み込めないを上から確認します。ドラッグ操作ができない端末では、この章は図と説明だけ見て第12章へ進めます。

ファイル名はコードと一字ずつ一致させます。sample.svgSample.svg は、大文字と小文字が違う別の名前です。自分の画像が PNG 形式なら、無理に名前だけを .svg へ変えず、コード側も実際の名前、たとえば sample.png に合わせます。

def preload
  @photo = loadImage("sample.svg")
end

def setup
  createCanvas(600, 400)
end

def draw
  background(245)
  image(@photo, 0, 0, width, height)
end

preload は、setup より前に素材を読み込むためのメソッドです。画像の読み込みが完了する前に描画が始まる問題を避けます。

image の最初の引数が画像、次の 2 つが位置、最後の 2 つが表示幅と高さです。元画像と違う縦横比を指定すると、画像が伸びます。

濃紺の600×400のキャンバスに、山並み、四つの窓、黄色い月を描いた付属画像が表示された実行結果
図 11-1 `sample.svg`を取り込み、キャンバス全体へ表示した結果。

11.2 縦横比を保つ計算

画像の幅をキャンバス幅へ合わせ、高さを比率から計算します。次の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-2 実際にマウスを動かすと、半透明の円が画像の上を移動する。

11.4 自分の素材から始める

インターネットで見つけた画像には、作者と利用条件があります。「検索で表示された」は「自由に使える」という意味ではありません。最初の実験には、次の素材が扱いやすい選択です。

  • 自分で撮影した写真
  • 自分で描いた絵を撮影またはスキャンした画像
  • 利用条件が明記されたパブリックドメイン素材
  • ライセンスが目的に合い、作者名など必要な表示を行える素材

人物写真には、著作権だけでなく肖像やプライバシーの問題もあります。公開しない個人実験と、Web で配布する作品では条件が変わります。

付属の sample.svg は本書のために作った素材なので、この章の練習や公開作品へ自由に利用、加工できます。最初は付属素材で操作を確かめ、動いたあとに自分の画像へ置き換えると、ファイル形式の問題とコードの問題を分けて考えられます。

11.5 単一 HTML と素材

rbCanvas/p5 は、画像や音声を含めて単一の HTML ファイルへ保存できることを特徴としています。ただし保存や読み込みの挙動はブラウザの種類と設定に左右されます。保存したら、元のエディタを閉じた状態でも開けるか確認します。

あとから編集を続けるときは、保存した HTML ファイルをエディタへドラッグ&ドロップします。コードだけでなく、いっしょに保存された画像も復元されます。

画像を組み替えるほど、コードにも同じ処理が増えていきます。第4部では、繰り返す描画をメソッドへまとめ、複数の要素を配列とクラスで扱います。

この章の小さな区切り

  • 付属画像を素材一覧へ取り込み、表示できた
  • 画像の位置か大きさを一つ変えた
  • 余裕があれば、半透明の色や図形を重ねた

端末の都合で素材を取り込めない場合は、図11-1と11-2で変化を確認できれば次章へ進めます。

参考資料

第4部 Ruby で作品を育てる

形が増えると、同じコードの繰り返しや、似た変数が目立ち始めます。それは失敗ではありません。作品が、整理する理由を教えてくれている状態です。

第4部では、メソッド、配列、クラスを使います。Ruby の機能を覚えるためではなく、気に入った作品を壊さずに育てるために導入します。小さな作品なら、クラスを使わない判断も正解です。

第12章 メソッドで自分の描画命令を作る

同じ顔を 3 か所へ描こうとすると、楕円の行が何度も並びます。コピーは実験の入口として役立ちますが、あとから目の大きさを変えるとき、すべてのコピーを直す必要があります。メソッドは、繰り返した処理へ名前を付け、自分の作品専用の命令にします。

12.1 コピーした顔が教える整理の時期

最初は、メソッドとsetupを一つにした次の完成コードをそのまま実行します。次のコードでは、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

xyは引数です。呼び出す側から値を受け取ります。顔の描き方は1か所にまとまり、位置だけを変えて3回使えます。顔の楕円をコピーして別々に直す方法でも試作できますが、目の大きさなどを一度に変えたくなったら、メソッドへまとめる理由が生まれます。

薄い灰色の500×300のキャンバスに、同じ描き方で作ったオレンジ色の顔が三つ、異なる位置へ並んでいる実行結果
図 12-1 同じdraw_faceメソッドへ別の座標を渡し、三つの位置へ描く。

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)
薄い灰色の500×300のキャンバスに、小・大・中の三つのオレンジ色の顔が並ぶ実行結果
図 12-2 同じdraw_faceへ異なるsizeを渡すと、顔と目の比率を保ったまま大きさが変わる。

図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を使うからこそ、位置や大きさの違いが見えます。整理はコードを短くするだけでなく、同じ規則から多くの案を早く比べる道具です。

この章の小さな区切り

  • draw_faceを3回呼び、三つの顔を表示できた
  • 引数の数字を一つ変え、見た目の違いを説明できた
  • いちばん気になった配置を、題名の分かるファイル名で保存した

全部できなくても、三つの顔が表示できれば第13章へ進めます。困ったら12.1の完成コードへ戻ります。

第13章では、同じメソッドをいくつもの位置データへ適用します。メソッドが「描き方」、配列が「何をいくつ持つか」を担当します。

参考資料

第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 というキーがあります。=>は、左側のキーと右側の値を結び付ける記号です。rbCanvas/p5 0.5.1で確実に読めるこの形を使います。更新と描画を同じ反復で行います。次の draw は変更部分で、直前の setupend より後ろへ足します。

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 で実行中に増やす

クリックした場所へ点を追加します。次のイベントメソッドは変更部分で、drawend より後ろへ足します。

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へ渡すハッシュを同じ行に書いて避けています。

濃紺の500×300のキャンバスに、最初から配列へ入っている黄色い点が三つ描かれた実行結果
図 13-2 クリック前は、setupで用意した三つの点を描く。
濃紺の500×300のキャンバスに黄色い点が四つあり、新しい点がクリックした位置へ追加された実行結果
図 13-3 キャンバスを1回クリックすると、配列の末尾へ新しい点が加わる。

要素が増え続けると、描画処理も増え続けます。作品の意図が「記録を蓄積する」なら増え続けてもよい場合があります。長く遊ぶ作品なら、上限や削除が必要です。

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 色の候補から一つ選ぶ

配列には、位置だけでなく、作品で使う色の組み合わせも入れられます。

赤、橙、青、紫の四つの色に0から3の番号が付いた配列の図
図解 13-A 配列の場所を表す番号は 0 から始まる。
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|
      color = palette.sample
      fill(color[0], color[1], color[2])
      ellipse(30 + column * 53, 28 + row * 53, 38, 38)
    end
  end
end

palette(パレット)は、絵に使う色をまとめた配列です。その中の各色も、赤、緑、青の3要素を持つ配列です。sample(サンプル)は、配列から要素を一つ選んで返すRubyのメソッドです。color[0]は最初の赤、color[1]は次の緑、color[2]は最後の青を取り出します。角括弧の番号は0から始まります。

すべてを自由な乱数色にせず、先に選んだ4色からだけ使うことで、偶然が起きても作品全体の関係を保てます。色を一つ入れ替える、特定の行だけ候補を減らす、円を別の形へ変える、の順で敷き詰め模様を育てられます。

赤、橙、青、紫の四色から選ばれた54個の円が6行9列に並ぶ敷き詰め模様
図 13-1 四色だけを使い、偶然の並びを作品のまとまりへ変える。

13.6 配列のハッシュからクラスへ移る境目

ハッシュは、少数の性質を持つ要素に向いています。点が「更新する」「描く」「端に出たか調べる」という複数の処理を持ち始めると、毎回ハッシュを渡すコードが散らばります。

その時点でクラスが役立ちます。クラスは配列より上等な方法ではありません。データと、そのデータに関わる処理を一緒に置きたいときの選択です。次章では、今の点をクラスへ移し、違いを比べます。

13.7 小作品「記憶の一色」

13.5のpaletteにある四色から一色だけを、自分が覚えている場所や物の色へ置き換えます。正確なRGBを探す必要はありません。「夕方の帰り道」「古いかばん」のように、その色へ短い名前を付け、数字を少しずつ変えます。

実行するたび並びは変わりますが、選んだ色の関係は残ります。気に入った偶然が出たら保存します。配列は、使える色を制限するためだけでなく、自分が選んだ材料をひとまとめにする道具です。

この章の小さな区切り

  • 配列から複数の点または色を取り出して描けた
  • クリックで要素が一つ増えることを確認できた
  • パレットの一色を変え、前後の印象を一言で比べた

クリック追加まで進まなくても、13.1の四つの円が表示できれば次章へ進めます。今日残したい画面を一つ保存します。

参考資料

第14章 クラスで登場人物に個性を持たせる

第13章の点は、位置と速度をハッシュで持っていました。更新処理と描画処理が増えると、点に関するコードが draw の中へ集まりすぎます。クラスは、点が知っている値と、点ができることを一つの定義へまとめます。

14.1 一つの Dot を定義する

次のクラス定義は作品を構成する変更部分です。このあと14.2の setupdrawmousePressed を同じエディタへ続けて足してから実行します。

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(リジェクト)は、条件に当てはまった要素を取り除いた新しい配列を返します。ここでは各 dotoutside? を呼び、画面の外へ出た点だけを取り除いています。処理を短く書く省略記法もありますが、最初は do から end までを見える形にしたほうが、何を調べているか追いやすくなります。

14.3 個性は同じクラスの初期値から生まれる

すべての点は同じDotクラスですが、速度と大きさが違います。個性を増やすため、色も持たせます。次の変更部分で、Dotクラス内のinitializedisplayをそれぞれ置き換えます。

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回呼び、位置もそれぞれ乱数で決めています。数を830へ変えると、密度だけを比べられます。

濃紺の500×300のキャンバスに、大きさと色の異なる半透明な点が散らばっている実行結果
図 14-1 同じDotクラスから作った16個でも、それぞれ別の値を持つ。

16.times8.timesへ変えた版も、別名で保存して比べます。

濃紺の500×300のキャンバスに、16個版より少ない半透明の点が離れて配置された実行結果
図 14-2 8個で始めた版。移動中に右端を越えた点はrejectで取り除かれ、余白がさらに広くなる。

数が少ない版では、一つずつの色や大きさへ目が向きます。多い版では、個体より群れ全体の密度が先に見えます。どちらが正しいかではなく、見せたい関係に合う数を選びます。

14.4 クラスを使わないほうが読みやすい場合

円が 2 個だけで、動きも 1 行なら、クラスは説明を増やすだけかもしれません。クラスを使う判断は、コード行数ではなく次の問いで考えます。

  • その要素に関する値が複数の場所へ散らばっているか
  • 更新、描画、判定を同じ対象の責任として読めるか
  • 同じ種類の要素を複数作るか
  • 新しい性質を足すたび、関係のない場所まで直しているか

小さくコピーして遊び、困ってから整理する順番でも遅くありません。次章では、個々の登場人物ではなく、作品全体がタイトル、実行中、終了という状態を持つ場合を扱います。

14.5 小作品「動きに性格を付ける」

点の性格を一語だけ選びます。たとえば「せっかち」「おくびょう」「のんびり」です。次の三つから二つを変え、性格が動きに見えるか試します。

  • @speedの範囲
  • @sizeの範囲
  • @red@blueの範囲

性格を表す新しい命令はありません。速さ、大きさ、色という数値の組み合わせを、自分がどう読むかで意味が生まれます。同じクラスから作った点が全員同じである必要もありません。

この章の小さな区切り

  • Dot.newから複数の点を作れた
  • 同じクラスの点が別々の値を持つことを画面で確かめた
  • 性格語を一つ選び、二つの数値範囲を変えた版を保存した

クラスが難しく感じたら、13章のハッシュ版を使い続けてもかまいません。作品が動いていて、また変えたい場所が分かれば先へ進めます。

14.6 Rubyそのものをもっと知るなら

描画から少し離れ、Rubyの仕組みを試す道もあります。Ruby公式ドキュメントでArrayEnumerableを開くと、sampleselectreject以外にも、並べ替える、数える、変換するといったメソッドを見つけられます。

最初は新しい名前を一つだけ選び、短い配列で結果を確かめます。同じ点の集まりを配列とハッシュで書き比べる、ハッシュ版とクラス版を保存して読みやすさを比べる、描画を使わずRubyの式だけを試す、といった進み方もできます。難しい書き方へ進むことより、「この処理を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

各メソッドは、自分の場面だけを描きます。色や文字を変えるとき、別の場面へ影響しにくくなります。

濃紺の600×360のキャンバス中央に、白い文字で「SPACE ではじめる」と表示されたタイトル画面
図 15-1 title 状態では開始案内だけを描く。

図15-2と図15-3は、15.3と15.4まで追加した完成後の画面を先に示しています。この15.2の段階ではまだSPACEキーは反応しません。二つのメソッドを追加できたら、図を見て先の変化を予想し、15.3へ進みます。

薄い灰色の600×360のキャンバスで赤い円が左から右へ進んでいる実行中の画面
図 15-2 SPACEキーを押したあとのplaying状態では、位置を更新して円を描く。
赤い600×360のキャンバス中央に白い文字で「もう一度 SPACE」と表示された終了画面
図 15-3 円が画面外へ出るとfinished状態へ移り、終了案内を描く。

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 unless key == " "

  case @scene
  when :title
    @x = 0
    @scene = :playing
  when :finished
    @x = 0
    @scene = :playing
  end
end

状態が変わることを遷移と呼びます。タイトルから実行中へ、実行中から終了へ移ります。終了から実行中へ戻るときは、@x も初期値へ戻します。

状態だけ変えて値を戻し忘れると、再開した瞬間にまた終了します。場面を切り替えるとき、その場面が必要とする値をどこで初期化するかまで考える必要があります。

15.5 デバッグ表示は作品の裏側を見せる

制作中だけ、現在の状態を左上へ表示できます。次の変更部分は drawcase が終わった直後、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.1だけを実行し、エラーなしでタイトル画面を表示できた
  • SPACEキーでtitleからplayingへ移れた
  • 円が画面外へ出たあとfinishedが表示され、もう一度SPACEで始められた

三つの場面を全部作らなくても、タイトルから実行中へ一度移れれば次章へ進めます。途中で分からなくなったら、15.1の動くタイトル版へ戻ります。

第4部で、作品を増やしても追える構造を作りました。第5部では、この構造を土台に音、3D、ゲームへ進みます。

参考資料

第5部 表現領域を広げる

音、3D、ゲームには、それぞれ新しい考え方があります。ただし第1部から第4部までと別世界ではありません。値を変える、状態を覚える、入力に反応するという同じ材料を組み替えています。

この部は興味のある章だけ選べます。素材ファイルやブラウザの許可が必要な機能では、環境によって動作が異なることがあります。動かなかったときに、自分の能力の問題だと決めつけないでください。

第16章 音と画面を同じ時間に置く

音を鳴らすだけなら、再生ボタンを一つ置けば足ります。クリエイティブコーディングで面白いのは、音が鳴る時間と、画面が変わる時間を同じ値から作れることです。クリックで音を始め、再生位置や音量に合わせて形を変えます。

16.1 ブラウザは勝手な音を止める

多くのブラウザは、ページを開いただけで音が鳴る自動再生を制限します。驚かせる広告や不要な音を防ぐためです。そのため、音の開始にはクリックやキー入力など、利用者の操作が必要です。

音が鳴らないとき、コードだけが原因とは限りません。最初に次を確認します。

  • 画面を 1 回クリックしたか
  • タブがミュートされていないか
  • 端末の音量が 0 ではないか
  • 音声ファイルをエディタへ正しく取り込んだか
  • ブラウザが音声形式を再生できるか

16.2 preload で音を用意する

この章には、練習用の短い音声 sound.wavが付属しています。リンク先を保存してエディタへドラッグ&ドロップし、素材一覧へ sound.wav が現れたことを確認します。素材名が出ないときは、付録Aの画像や音が読み込めないを確認します。ドラッグ操作ができない端末では、この章は図と説明だけ見て第17章へ進めます。

preload は、絵を描き始める前に画像や音を読み込むためのメソッドです。loadSound("sound.wav") は、取り込んだ sound.wav を音声オブジェクトとして読み込みます。その音声オブジェクトを @sound へ入れておくと、別のメソッドからも再生や停止を指示できます。

def preload
  @sound = loadSound("sound.wav")
end

def setup
  createCanvas(500, 300)
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

@sound.isPlayingは、音が再生中ならtrue(真)、停止中ならfalse(偽)を返します。再生中なら「再生中」、停止中なら「クリックで再生」を変数messageに入れます。第5章で使ったifelseを、表示する文字とクリック時の処理の切り替えに使っています。

mousePressed は、キャンバス上でマウスボタンを押したとき、またはタッチ操作をしたときに呼ばれるメソッドです。@sound.play は読み込んだ音を先頭から再生し、@sound.stop は再生を止めます。ここで使う .play の前の点は、「@sound に再生を頼む」というつながりを表します。

濃紺の500×300のキャンバス中央に、クリックで再生と表示された音声停止中の実行結果
図 16-1 音を読み込んだ直後は停止中なので、「クリックで再生」と表示される。
濃紺の500×300のキャンバス中央に、再生中と表示されたクリック後の実行結果
図 16-2 0.5.1 Opal版のキャンバスを実際にクリックし、付属音を再生した直後。

rbCanvas/p5 の版、ブラウザ、音声形式によって読み込み方法や自動再生制限の挙動が変わることがあります。本書では付属のsound.wavを0.5.1 Opal版へ取り込み、Chromeでクリック再生と停止を確認しています。別の環境で音が鳴らない場合は、この節の確認項目を上から試してください。

16.3 再生の状態を作品全体で共有する

16.2では、表示とクリック処理の両方が音声オブジェクトの状態を基準にしています。次は同じisPlayingを、文字だけでなく円の大きさと色へ結び付けます。図16-3、16-4では変化を見やすく切り出すため、キャンバスを420×220へ小さくします。最初にsetupcreateCanvas(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へ戻ります。そのため、表示も小さな円へ戻り、次のクリックでは一度で再生が始まります。表示と操作で同じ状態を調べると、画面と音のずれを防げます。

濃紺の420×220のキャンバス中央に、小さな灰青色の円が表示された音声停止中の実行結果
図 16-3 `isPlaying`が`false`の停止中は、小さな灰青色の円を描く。
濃紺の420×220のキャンバス中央に、大きな黄色い円が表示された音声再生中の実行結果
図 16-4 実際にクリックして音を鳴らすと、`isPlaying`が`true`になり、円が大きく黄色へ変わる。

16.4 音を飾りではなく構造にする

同じ映像へ好きな曲を後付けしても、音付き作品にはなります。しかし音が消えても画面の構造が何も変わらないなら、両者はまだ独立しています。

次の問いから一つ選ぶと、関係を作りやすくなります。

  • 音が始まるまで、形は待つか
  • 音が止まると、画面は消えるか残るか
  • 同じリズムを大きさ、色、出現間隔のどれへ渡すか
  • 無音の時間に何を見せるか

音声素材にも著作権があります。自分で録音した音、利用条件が明確な素材、生成条件を確認できる音を使います。公開しない実験と配布する作品を分けて考えてください。

付属音は440 Hzの音をプログラムで合成した1.2秒の教材用素材です。第三者の演奏や録音を含まず、この章の練習や公開作品へ自由に利用、加工できます。

次章では、画面に奥行きを加えます。音と同じく、機能を足すことより、増えた次元を何に使うかが中心です。

この章の小さな区切り

  • クリックで音を再生または停止できた
  • 再生中かどうかに合わせて文字か円が変わった
  • 余裕があれば、無音中と再生中の色や大きさを選び直した

音を使えない環境では、図16-1から16-4で停止中と再生中の違いを確認できれば次章へ進めます。

参考資料

第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(120) は幅、高さ、奥行きが同じ箱を描きます。normalMaterial は面の向きに応じて色を付けるため、光源をまだ設定していなくても立体の向きを観察しやすくなります。

濃紺の500×400のキャンバス中央に、正面を向いた青い箱が四角形のように見えている実行結果
図 17-1 正面からは奥の面が隠れ、箱が四角形のように見える。

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

rotateXrotateY は、回転する角度を受け取ります。角度にはラジアンという単位が使われますが、ここでは単位の計算を覚える必要はありません。frameCount0.01 のような小さな値を掛けると、時間とともに少しずつ回転します。0.010.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

translaterotate は、その後の描画座標へ影響します。push は現在の座標系を保存し、pop は元へ戻します。これがないと、1 つ目の移動と回転が 2 つ目の箱にも重なります。

translate(x, y, z) は、これから描くものの原点を移動します。最初の箱では x を -120 にして左へ、2つ目では x を 120 にして右へ移しています。y と z が 0 なので、上下と奥行きの位置は変わりません。

pushpop は、立体専用ではありません。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(70) は、全方向から当たる弱い光を置きます。directionalLight の最初の3つの数字は光の赤・緑・青、後ろの3つは光が進む向きです。最初は数字を暗記せず、色の3つの数字や向きの一つだけを変えて、どの面の明るさが変わるか観察します。

黒い500×400のキャンバス中央でピンク色の箱が斜めに回転し、上面と側面の明るさが異なって見える実行結果
図 17-2 回転と光によって、正面では隠れていた複数の面が見える。

3D は計算負荷が高くなりやすく、端末によって滑らかさが違います。物体数を減らす、形を簡単にする、画面サイズを小さくするという調整は、作品の質を下げる作業ではありません。見せたい関係を残す編集です。

次章では、2D へ戻ってゲームを作ります。3D を通して見た座標変換と状態管理は、ゲームの画面設計にも使えます。

この章の小さな区切り

  • WEBGLを指定して箱を一つ表示できた
  • 回転、移動、光のどれか一つを変えた
  • 余裕があれば、正面版と斜め版を保存して見比べた

箱が一つ表示できれば、端末で動きが重くても保存して次章へ進めます。

参考資料

第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 は値を最小と最大の範囲へ収めます。円の半径 20 を境界に使い、画面外へ出ないようにしています。

keyIsDown は、指定したキーが現在押されているかを調べます。LEFT_ARROWRIGHT_ARROWUP_ARROWDOWN_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(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
濃紺の600×360のキャンバスに緑のプレイヤーと右隣の黄色い目標があり、左上にscore: 0と表示された実行結果
図 18-1 開始時は、右矢印キーで届く位置へ最初の目標を置く。
濃紺の600×360のキャンバスで緑のプレイヤーが右へ移動し、黄色い目標が左上へ移り、左上にscore: 1と表示された実行結果
図 18-2 目標へ触れると得点が1になり、次の目標が別の位置へ移る。

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と同じ初期値へ戻します。

濃い紫色の600×360のキャンバス中央に、白い文字で「5個みつけた!」と表示された終了画面
図 18-3 5点に達するとプレイヤーと目標の描画を止め、終了画面だけを表示する。

ただし「ゲームには勝敗が必要」とは限りません。次の終わり方も選べます。

  • 5 個集めると色が変わり、そのまま続く
  • スコアを表示せず、集めた位置だけ軌跡として残る
  • 目標から逃げるほど音が静かになる
  • 終了条件を置かず、好きなだけ触れる

ルールは多いほど面白いのではありません。プレイヤーにしてほしい操作と、返したい反応が一つずつ対応しているかを見ます。

同じ移動コードでも、作品の意味は変えられます。

  • 集める: 目標へ触れると増える。見つける喜びを作る
  • 避ける: 目標へ触れない時間を残す。距離と緊張を作る
  • 痕跡を描く: スコアを消し、通った場所へ線を残す。勝敗ではなく動きそのものを見る

どれがゲームらしいかを決める必要はありません。ルールは、遊ぶ人に何を大切だと感じさせるかを変えます。「速い操作が得意な人だけ有利になっていないか」「キーを押し続けにくい人も遊べるか」「失敗したとき、責める表示になっていないか」から一つ選び、必要なら速度、操作、言葉を変えます。

18.5 一人で遊び直す

作った直後は、操作もルールも全部知っています。一度保存して閉じ、翌日に説明を読まず遊び直します。

  • 最初の 3 秒で何を押せばよいか分かるか
  • 目標へ触れたことが画面から分かるか
  • 速すぎる、遅すぎると感じる場所はどこか
  • もう一度遊びたい変化が一つあるか

直すのは一度に一つで十分です。次章では、完成度を上げ続ける代わりに、自分で区切りを決め、作品を保存します。

18.6 ゲームをもっと育てるなら

今のゲームを作り直さず、変える場所を一つ選びます。

  • 第15章の場面管理を使い、開始画面と再挑戦を加える
  • プレイヤーの速さを3種類作り、いちばん操作しやすいものを選ぶ
  • スコアを消し、色、音、軌跡で触れた結果を返す
  • 集める代わりに避ける、追いかける代わりに観察するルールへ変える

ゲームを育てるとは、機能を増やすことだけではありません。遊ぶ人へどんな時間を渡したいかを選び、そのために不要なルールを外すことも改良です。

この章の小さな区切り

  • 矢印キーで円を動かし、画面内へ留められた
  • 目標へ触れた結果が、位置・得点・色のどれかで返った
  • 「集める・避ける・痕跡を描く」から、自分が続けたい一案を選んだ

終了画面まで作らなくても、操作と反応が一つつながれば保存して次章へ進めます。

参考資料

第19章 作品を保存して次の入口を作る

プログラムには、自然に訪れる完成の瞬間がありません。数字はいつでも変えられ、機能はいくらでも足せます。だから完成は「もう直すところがない状態」ではなく、今の興味に区切りを付け、あとから戻れる形を残す判断です。

作る、見る、一つ変える、残すを循環する作品制作の図
図解 19-A 作品は、一つ変えて確かめる小さな循環で育てる。

19.1 一番楽しかった寄り道を選ぶ

最終作品を白紙から考える必要はありません。これまでの章から、次のどれかを選びます。

  • 何度も数字を変えたサンプル
  • 予想外の結果が出て保存したスケッチ
  • もう一つ機能を足したい作品
  • 見た目は単純でも、動かしていて気持ちよかった作品

選ぶ基準は技術の多さではありません。自分がもう一度開きたいものを選びます。

19.2 テーマを自分の言葉へ変える

作品募集や自主制作でテーマが決まっていても、その言葉をそのまま絵にする必要はありません。たとえばテーマが「ルビー」なら、次のように別の入口を選べます。

  • 赤い宝石の形や光を観察する
  • Rubyのコードや記号を図形として扱う
  • 赤から連想した熱、夕方、警告、祝祭を描く
  • 「大切にしまわれたもの」という物語を作る

最初に正しい解釈を探すのではなく、思い付いた言葉を5個書き、その中から画面にしてみたいものを一つ選びます。選んだ言葉へ、第6章の反復、第7章の偶然、第8章の操作など、気に入った仕組みを一つ組み合わせます。

19.3 変更を一つずつ保存する

大きく直す前に、現在の版を保存します。ファイル名には、意味のある短い違いを入れます。

orbit-slow.html
orbit-fast.html
orbit-blue-trail.html

finalfinal2final-final では、あとから違いが分かりません。「速い」「青い軌跡」のように画面の違いを名前へ入れると、比較しやすくなります。

19.4 引き算で主役を確かめる

機能を足す前に、一つを一時的に消してみます。

  • 背景の模様を消す
  • 音を止める
  • スコア表示を隠す
  • 円の数を半分にする
  • 色を 3 色へ減らす

消して寂しくなったなら、その要素は作品の関係を支えています。何も変わらない、または見やすくなったなら、なくてもよい可能性があります。削除は作品を小さくするだけでなく、何が面白さの中心かを見つける実験です。

19.5 時間を置いたセルフレビュー

第三者の講評がなくても、時間差を使って見直せます。

  1. 現在版を保存する
  2. エディタとメモを閉じる
  3. 数時間後または翌日に作品だけを開く
  4. 最初に見た場所、最初に試した操作を記録する
  5. 好きなところを一つ選ぶ
  6. 分かりにくいところがあれば、一つだけ直す

セルフレビューは採点ではありません。「良い作品か」ではなく、「自分はどこをもう少し触りたいか」を探します。何も直したくなければ、その版を区切りにできます。

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と横・縦の広がり11080だけを試します。作者メモは「円が何の周りを回っているのか分からない。でも、楕円の軌道は残したい」です。

左右と上下の両方にsinを使っています。ただし左右側だけ、角度へ1.5708を足しました。これは円の4分の1周ほど変化の始まりをずらす数字です。二つの波の始まる位置をずらすと、円が斜めに行ったり来たりせず、中心の周りを回ります。11080を掛けることで、横長の軌道になります。0.5.1で本文と同じコードを使えるよう、ここでは新しいcosを増やさず、第7章で確認したsinだけで組み立てます。

濃紺の500×320のキャンバスに黄色い円だけがあり、見えない中心の周りを回っている版Aの実行結果
図 19-3 版Aは動く黄色い円だけで、軌道の中心は描かない。

版B: 中心を見えるようにする

次の2行は変更部分です。版Aのdrawの最後のendより前へ足します。

fill(120, 190, 255, 80)
ellipse(width / 2, height / 2, 120, 120)

中心へ半透明の青い円を置くと、黄色い円がその周りを巡っているように見えます。採用しなかった案は、背景へ星を50個足すことでした。試すと中心より星へ目が移ったため、この版では外しました。削除も制作上の選択です。

濃紺の500×320のキャンバスで、半透明の青い円の周りを黄色い円が回る版Bの実行結果
図 19-2 版Bでは中心を描き足し、二つの円の関係を見えるようにした。

図19-3と図19-2は、中心の青い円があるかどうかだけが大きな違いです。小さな追加でも、黄色い円を「一つで動く形」と見るか、「何かの周りを巡る光」と見るかが変わります。

版C: 速さと題名を選ぶ

@angle += 0.03@angle += 0.015へ変えると、待つ時間が長くなります。速い版と遅い版を別名で保存し、見比べます。ここでは遅い版を選び、題名を「まわる光」としました。

最終メモは次の3行です。

暗い場所の中心を、黄色い光が見回る作品です。
中心を半透明にして、物ではなく気配に見えるようにしました。
速い版は落ち着かなかったため、動きを半分の速さにしました。

この過程で重要なのは、版Cが正解だからではありません。版Aで動きを確かめ、版Bで関係を足し、版Cで比較して一つ選びました。自分の作品では、色、数、入力、音など別の一項目を選べます。

19.7 単一 HTML を別の環境で開く

rbCanvas/p5 は、プログラムと素材を単一の HTML ファイルとして保存できることを特徴にしています。保存したファイルは、エディタを介さずブラウザで実行できます。

エディタの保存ボタンを押すと、ブラウザが HTML ファイルをダウンロードします。保存場所が表示されない場合は、ブラウザのダウンロード一覧を確認します。もう一度編集したいときは、その HTML ファイルを rbCanvas/p5 エディタへドラッグ&ドロップします。画像や音声を取り込んでいた場合も、同じファイルから復元されます。

rbCanvas/p5 0.5.1エディタで軌道を回る円のコードと実行結果が並び、左上に下向き矢印の保存ボタンが見えている画面
図 19-1 左上の下向き矢印が保存ボタン。押したあと、作品の違いが分かる名前を入力する。

保存後は次を確認します。

  • エディタを閉じてもブラウザで開けるか
  • 画像や音声が欠けていないか
  • キーボードとマウスの操作が反応するか
  • 音声開始にクリックが必要なら、画面に説明があるか
  • 強い点滅や急な音量変化がないか

ブラウザごとに保存や読み込みの挙動が異なる場合があります。Chrome で動いても、別のブラウザで同じとは限りません。公開するなら、少なくとも自分が対応対象と決めた環境で確認します。

19.8 工夫を短い文章にする

作品募集では、テーマをどう考え、どこを工夫したかを書くことがあります。立派な解説を書くのではなく、制作中に実際に選んだことを書きます。

テーマを「暗い場所で見つける赤い光」と考えました。
同じ形を並べ、マウスに近い形だけ明るくなるようにしました。
赤が強すぎると光に見えなかったので、周囲を暗い青へ変えました。

「何を考えたか」「コードで何を変えたか」「試して何を選んだか」の3点があれば、制作の過程が伝わります。うまく見せるために、していないことを書く必要はありません。

19.9 公開しない選択と公開する責任

作品は公開しなくても完成です。自分の端末に保存し、あとで見返すだけでもかまいません。

公開する場合は、コード以外の条件も確認します。

  • 写真、音、フォント、3D モデルを公開できるか
  • 必要な作者名とライセンスを表示したか
  • 他人の個人情報が含まれていないか
  • 操作方法を短く書いたか
  • 動きや音を止める手段が必要か

タイトルや制作意図は、長い説明でなくてかまいません。「マウスの速さを色にしました」のように、何を試したか一文あれば、未来の自分も作品へ戻りやすくなります。

借りた素材や参考にしたコードがある場合は、制作メモや応募文へ出典を残します。必要な内容は利用条件によって異なりますが、迷ったときは次の項目を記録します。

画像: 作者名「作品名」
入手元: (素材を入手したページのURL)
ライセンス: CC BY 4.0
変更: 色を青へ変更し、背景の一部として使用

コードの参考: 作者名「スケッチ名」
URL: (参考にしたスケッチのURL)
参考にした点: 円を等間隔へ並べる考え方

URLだけでなく、誰の何を、どのように使ったかを残します。本書のコードを改造した場合は「『Rubyで描く、動かす、遊ぶ — rbCanvas/p5ではじめるクリエイティブコーディング』第6章をもとに改造」のように書けます。

19.10 作品募集へ出す前の確認

単一HTMLファイルを提出する形式なら、次を上から確認します。

  1. 0.5.1のエディタからHTMLファイルを保存した
  2. 保存したHTMLをブラウザで直接開けた
  3. 画像や音が作品内に含まれている
  4. 操作方法が作品内または応募文で分かる
  5. テーマの解釈と工夫を自分の言葉で書いた
  6. 借りた素材の利用条件と作者表示を確認した
  7. 提出するファイルが最後に確認した版と同じである

この段階まで来れば、乱数配置、敷き詰め、フラクタル、図形を組み合わせたモチーフ、アニメーション、操作へ反応する作品のいずれかを、自分で選んで完成させられます。すべての技術を一作品へ詰め込む必要はありません。

19.11 作品を見つけ、仕組みを一つ持ち帰る

p5.jsやProcessingの作品には、JavaScriptやJavaで書かれたものもあります。コード全体をrbCanvas/p5へ移す前に、次の順で観察します。

  1. 画面のどこが、どのように変化しているかを言葉にする
  2. 位置、速度、色、反復、入力のどれを使っていそうか考える
  3. 作品全体ではなく、気になった変化を一つだけ別のスケッチで試す
  4. 同じ機能名が0.5.1のリファレンスにあるか確かめる

見つけた作品は、作品名、作者名、見たページのURL、気になったところ、自分で試したところを制作ノートへ残します。画像やコードを使う場合は、作者が示した利用条件も確認します。考え方から刺激を受けた場合も、制作意図で元の作品に触れると、自分の発見がどこから始まったかを伝えられます。

19.12 終了ではなく分岐

ここまでで、形、時間、入力、Ruby の構造、音、3D、ゲームを通りました。すべてを覚えている必要はありません。気に入った章へ戻り、別の数字を試せれば、本書の目的は達成されています。

次に進む道は一つではありません。Ruby の言語機能を深く知る、p5.js の作品から発想を借りる、音を録る、絵を描く、ゲームのルールを考えるという別々の入口があります。今いちばん気になった章へ戻り、その章の「もっと探すなら」「もっと育てるなら」から一つだけ選べます。

この章の小さな区切り

  • 自分がもう一度開きたい作品を一つ選んだ
  • 変更前と変更後を別名で保存し、違いを比べた
  • 好きなところ、変えたところ、選んだ理由を一文ずつ残した
  • 公開するかしないかを自分で選び、公開する場合は素材と操作方法を確認した

作品を公開しなくても、この本は完了です。保存した作品を後日もう一度開き、数字を一つ変えたくなったなら、次の入口はもうできています。

参考資料

付録

付録は、本文を止めずに進むための避難場所です。エラーで困ったときや、用語の意味を確認したいときに開いてください。Ruby と rbCanvas/p5 の使い方や、次に試せる発展の案は、それぞれを実際に使う章の本文で細かく説明します。付録を先に読む必要はありません。

付録A エディタで困ったとき

動かない時間もプログラミングの一部ですが、長く立ち止まる必要はありません。原因を一度に全部考えず、画面に見えている症状から順番に切り分けます。

エディタの基本操作そのものを確認したいときは、先に始める前に エディタの使い方へ戻れます。

A.1 何も表示されない

最初に、次の最小コードへ戻します。

def setup
  createCanvas(400, 300)
  background(245)
  ellipse(200, 150, 100, 100)
end

これが動けば、エディタと基本描画は使えています。元のコードへ戻すときは、最初から半分ずつ消そうとせず、次の易しい順で試します。

  1. 直前に変えた1か所だけ元へ戻す
  2. 動いていたHTMLファイルを保存していれば、その版をエディタへ戻す
  3. 元のコードを別の場所へ残し、上の最小コードへ1節ずつ足す
  4. 慣れてきたら、処理のまとまりごとに先頭へ#を付け、どのまとまりを外すと動くか調べる

#は、その行を一時的に実行しないコメントにします。元の行を消さずに試せるので、戻すときは#だけを外します。

確認する場所は次の順です。

  1. def setup または def draw に対応する end があるか
  2. メソッド名の大文字と小文字が合っているか
  3. 丸括弧、角括弧、ダブルクォートが閉じているか
  4. background が図形のあとに描かれていないか
  5. 図形の座標がキャンバス外ではないか
  6. 色と背景が同じではないか

A.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 preload
  @photo = loadImage("sample.svg")
end

コードが正しくても、エディタへsample.svgを取り込んでいなければLoadErrorになります。ファイル名だけでなく、エディタ下部の素材一覧に同じ名前があるか確認します。

A.3 動くが速すぎる、重い

draw は短い間隔で繰り返されます。1 回の処理が軽くても、要素が増えると負荷が上がります。

  • キャンバスを小さくする
  • 描く要素数を半分にする
  • 画像や 3D モデルの数を減らす
  • 配列へ要素を増やし続けていないか確認する
  • 画面外の要素を selectreject で取り除く

速さを意図的に落とす場合は frameRate を試せます。ただし処理自体が重い場合、設定した値まで出ないことがあります。

A.4 画像や音が読み込めない

  • ファイル名の大文字と小文字を確認する
  • 拡張子を含む名前が一致しているか確認する
  • エディタへ素材を取り込んだか確認する
  • 素材の読み込みを preload に置いたか確認する
  • 音は画面をクリックしてから再生する
  • 別形式の素材で試す

素材を使わない図形だけの最小コードが動けば、問題は Ruby 全体ではなく素材読み込み周辺に絞れます。

A.5 元へ戻るための保存

大きな変更の前に、動く版を別名で保存します。

waves-01.html
waves-02-color.html
waves-03-mouse.html

番号だけでなく、何を変えたか短く入れると戻りやすくなります。失敗版も、画面が面白ければ別名で残せます。

A.6 一人で質問を作る

資料を検索するとき、「動きません」だけでは候補が広すぎます。次の 4 点を自分用のメモへ書きます。

  1. 何を作ろうとしたか
  2. 何が起きると予想したか
  3. 実際に何が起きたか
  4. どこまで小さくすると動くか

質問を公開しなくても、ここまで整理すると原因が見つかることがあります。

A.7 保存したファイルが見つからない

保存ボタンを押しても、保存場所を選ぶ画面が出ないブラウザがあります。その場合は、ブラウザのメニューから「ダウンロード」を開きます。ファイル名の横にあるフォルダの印から、保存された場所を表示できることがあります。

同じ名前で何度も保存すると、ファイル名の末尾に番号が付く場合があります。更新日時を見て、最後に保存したものを確かめます。

A.8 リファレンスの例をそのまま貼っても動かない

p5.js 公式リファレンスの例は JavaScript で書かれています。rbCanvas/p5 では Ruby を書くため、波括弧 { }、行末のセミコロン ;letfunction などはそのまま使えません。

まず同じ機能名を rbCanvas/p5 のヘルプで探します。見つからなければ、その機能が現在の rbCanvas/p5 では使えない可能性もあります。本書の動く最小コードへ戻し、試したいメソッドを一つだけ足して確認します。

参考資料

用語集

API

プログラムから機能を呼び出すために用意された名前と使い方の集まり。本書では ellipsebackground など、rbCanvas/p5 を通して使う描画機能を指す場合が多いです。

p5.js

ブラウザで絵、動き、入力、音、3D などを扱うクリエイティブコーディング用 JavaScript ライブラリ。rbCanvas/p5 は、その機能を Ruby から利用できるよう橋渡しします。

rbCanvas/p5

ブラウザ上で Ruby の構文を使い、p5.js の描画機能を利用できる環境。本書では0.5.1のOpal版を基準にします。

Opal

RubyのコードをJavaScriptへ変換する仕組み。本書で使うrbCanvas/p5 0.5.1の土台です。Rubyの多くの構文を使えますが、通常のデスクトップ版Rubyとすべて同じではありません。

ruby.wasm

WebAssemblyを利用し、ブラウザなどでRubyを動かすための仕組み。rbCanvas/p5 0.7.2で採用されていますが、本書の実行環境には使いません。

インスタンス変数

@x のように @ で始まる変数。同じオブジェクトのメソッド間で値を保つために使います。本書では setup から draw へ状態を渡す場面で使います。

インタラクション

人の入力と作品の応答の関係。クリックできることだけでなく、操作へどのような変化を返すかまで含みます。

キャンバス

ブラウザ上の描画領域。createCanvas で幅と高さを指定します。

クリエイティブコーディング

機能や業務処理だけを目的にせず、表現、発見、遊びのためにコードを書く活動。完成作品に加え、試行の過程も大切にします。

シード

乱数の並びを決める初期値。同じシードを使うと、同じ乱数列を再現できます。

状態

プログラムが現在どの段階にあり、どんな値を覚えているかを表す情報。位置、速度、昼夜、タイトル画面などがあります。

スケッチ

実行できる小さな作品や実験コード。完成度に関係なく、変化を試す単位として使います。

セルフレビュー

作品を保存して時間を置き、作った本人がもう一度触って観察する活動。採点ではなく、次に試したいことを見つけます。

ノイズ

近い入力に近い値を返し、なめらかな不規則さを作る機能。本書では noise を指します。

フレーム

アニメーションを構成する 1 枚の静止画。draw が呼ばれるたび、新しいフレームが描かれます。

メソッド

名前を付けた処理。Ruby では defend で定義します。

乱数

呼び出すまで結果が決まらないように扱う数値。作品へ偶然を入れるときに使います。

参考資料

おわりに

最初の章で変えた数字を覚えているでしょうか。覚えていなくても問題ありません。もう一度コードを開き、変えて確かめられます。

プログラミングを続ける力は、すべてを暗記することだけから生まれません。「これを変えたらどうなるだろう」と思い、自分で実行する経験からも育ちます。画面が予想どおりになったときも、崩れて不思議な模様になったときも、その経験は次の実験の材料です。

本書は一人で進められるように作りました。誰かへ見せなくても、作品は作品です。公開しなくても、保存したスケッチは未来の自分へ渡せます。

次に rbCanvas/p5 を開くとき、本書の続きから始める必要もありません。好きな色を一つ置く、昨日のコードから 1 行消す、気になった動きを真似してみる。その小さな入口から、またプログラミングを楽しんでください。

PR|一人で作る楽しさの、その先へ

この本は一人で最後まで進められます。一方で、「ほかの人のコードを見たい」「自分のコードへ意見がほしい」「チームで一つのものを作ってみたい」と感じたなら、誰かと学ぶ段階へ進む合図かもしれません。

フィヨルドブートキャンプは、プログラミング未経験者も対象にしたオンラインプログラミングスクールです。Railsエンジニアコースとフロントエンドエンジニアコースがあり、現役ソフトウェアエンジニアによるレビュー、受講生や卒業生と学ぶコミュニティ、仕事を想定したチーム開発、自作Webサービスの公開へ取り組めます。

この本で「一つ変えて、実行して、結果を見る」ことを楽しめたなら、その好奇心はWebサービス作りにもつながります。エンジニアという仕事に興味を持ったときは、フィヨルドブートキャンプの学習内容を見るところから始めてみてください。

著者

町田哲平
フィヨルドブートキャンプメンター

参考資料