計算で図形を描く

前回は初めてプログラムで絵を描いたわけですが、図形の位置も色もすべて数値を手作業で入力したので、お絵かきソフトで描くよりもむしろ大変だったと思います。 今回は、図形をたくさん一気に描くなど、プログラムらしい図形の描き方をやっていきましょう。

定数(constant) と変数 (variable)

前回の例ではプログラム中に多くの数値が出てきました。 書いているときはまだしも後からプログラムを見返すと「この数値なんだっけ?」となることはよくあります。 また、ユーザの操作などに応じて値を変化させて、描画する図形を変化させたいときなどもあります。 そんなときは名前を付けて値を扱う機能「定数」と「変数」を使うと便利です。 物理なんかでよく出てくる「速度をvとする」と同じノリです。 プログラムは変数名を使って記述して、そこに色々な値を代入するというわけです。

具体例で見てみましょう。以下のプログラムでは2つの同じ大きさの正方形を異なる場所に描画しています。 正方形の一辺の長さをsという名前の定数とすることで、一辺の長さはどれも同じだということがわかりやすくなっています。 後から長さを変更したくなったときにも代入する部分だけを変更すれば済みますので便利です。 正方形の位置は変数pを使って指定しています。 1つ目の正方形を描画した後にpに新しい値を代入してから2つ目の正方形を描画しているので、2つの正方形は異なる位置に描画されます。

function setup() {
createCanvas(100,100);
const s = 20; // s という名前の定数を宣言する
let p = 10; // p という名前の変数を宣言する
rect(p, p, s, s);
p = 70; // p に 70 を代入する
rect(p, p, s, s);
}
実行結果

定数および変数を利用するための文法は以下の通りです。いずれも使用する前に必ず宣言が必要なので気を付けてください。

const 定数名 =; // 定数は再代入できないので、宣言と同時に値を代入する
定数名 = 新しい値; // 再代入しようとするとエラー
let 変数名; // 変数の宣言
let x, y, z; // カンマ区切りで複数まとめて宣言してもOK
変数名 =; // 変数に値を代入
let 変数名 =; // 定数と同様に、宣言と代入を同時にしてもOK

定数や変数の名前は自由に決めることができますので、速度なら v など、イメージしやすい名前を付けます。 ただし、 let 等の予約語(文法として使用される語)を変数名にすることはできません。 また、数字から始める名前にすることもできません。 漢字やひらがな等の日本語も使用できますが、あまりお勧めしません。

定数は使わないで変数だけでプログラムを書くこともできますが、変更する予定がない値は定数として宣言しておくと、誤って変更しようとしたときにエラーが出て気付くことができます。

演算

足し算・掛け算・割り算など揃っています。 計算結果を使って描画することや、計算結果を変数に代入して値を変化させることもできます。

function setup() {
createCanvas(100, 100);
let n = 21;
let y = 10;
line(0, y, n, y);
n = n - 2 + 9; // 等式ではなく右辺を計算して左辺に代入
y += 10; // y = y + 10 と同じ意味の省略記法
rect(0, y, n, n);
y += 40;
rect(0, y, n * 3, n / 2); // 掛け算は * で、割り算は /
}
実行結果

他には % (剰余、割り算の余り)、 ++ (1増やす)、 -- (1減らす) などがあります。

条件の演算

条件によってプログラムの動作を変えたいことがあります。たとえば、「奇数行と偶数行で色を変えて縞模様にする」「敵に触れた回数が3と等しいときだけゲームオーバーの処理をする」などです。 こういった処理は、敵に触れた回数など必要な値を変数に代入しておき、その値が条件を満たすかどうかを調べます。

コンピュータでは条件も計算で調べます。計算結果が true (条件成立) か false (条件不成立) のどちらかになるような演算です。 以下のようなものがあります。

変数に代入するときは = で、等しいか調べるときは == (イコール2つ) で間違えやすいので注意しましょう。 以上・以下・等しくない、どれも2文字の演算子ですが、イコールは2文字目です。

複数の条件を組み合わせた条件を作ることもできます。たとえば、数値がある範囲内かどうかを調べるだけでも「~以上」「~未満」の2つの条件を組み合わせる必要があります。

1 < x < 3 というようなまとめた書き方はできず、1 < x && x < 3 のように書く必要があります。 &| というように1文字だと異なる演算になりますのでこれも注意しましょう。

繰り返し (loop)

条件を利用するケースのひとつが繰り返しです。似た処理を何回か繰り返し実行してほしいとき、いつまで繰り返すのかを条件として記述します。 繰り返しには whilefor の2種類があります。

while 文

while は次のようなとてもシンプルな文法です。

while(繰り返しを続ける条件){
// 繰り返したい処理
}

たとえば次のように使います。四角を繰り返し描いて、端まで来たら終わりというプログラムです。

function setup() {
createCanvas(100,100);
fill(0);
let x = 0;
while(x < 100){ // 座標が描画範囲内であれば続ける
rect(x, x, 20, 20);
x += 20; // 繰り返し1回ごとに場所を動かす
}
}
実行結果

x += 20 を忘れたり、条件を書き間違えたりすると永遠に繰り返しが終わらないプログラムができあがります。 画面には何も表示されません。 しかし、ずっと繰り返し演算をしていますのでCPUの温度が上昇してファンが音を立てて回り始めたりするかもしれません。 冬場なら緊急時の暖房代わりになるでしょう。 俗に「無限ループ」と呼ばれる現象です。 試しにわざとやってみたい人は、やってみた後、プログラムを正常に書き直すのをお忘れなく。

for 文

繰り返しのもう一つの書き方が for 文です。 for 文はシンプルな繰り返しをコンパクトに書くことができるのが特徴です。

for(繰り返し前にする処理; 繰り返しを続ける条件; 繰り返し毎にする処理){
// 繰り返したい処理
}

先ほどと同じプログラムを for 文を使って書き直したものが次になります。繰り返しで変化する変数 x に関する記述が一か所にまとまってコンパクトになりました。 「x は最初 0 で、一回ごとに 20 増えて、100 以上になったら終了」とまとまっています。

function setup() {
createCanvas(100,100);
fill(0);
for(let x = 0; x < 100; x = x + 20){ // 繰り返しで変化する変数 x に関する記述
rect(x, x, 20, 20);
}
}

while, for はどちらを使ってもよいのですが、シンプルな繰り返しには for 文を用いる、くらいの使い分けがよいでしょう。 for 文を使う方が便利な場面の代表格は「N回繰り返す」処理です。 たとえば先ほどの例も「四角形を5回描く」と考えて書くと次のようになります。 i++ は「i を 1 増やす」という意味の省略記法です。 N回繰り返すは for(let i = 0; i < N; i++){...} と書くというのは慣用句のように頻出なので、何回かやるうちに覚えてしまいましょう。

function setup() {
createCanvas(100,100);
fill(0);
for(let i = 0; i < 5; i++){
rect(i * 20, i * 20, 20, 20);
}
}

「変数の値をN増やす/減らす」の様々な書き方

変数値を増減させることは頻出するので、様々な省略記法が用意されています。

i = i + 1; // 普通の書き方
i += 1; // ちょっと短くした書き方
i -= 1;
i++; // 1増やしたいとき専用の書き方
i--; // 1減らしたいとき専用の書き方

そんな数文字をケチるために違う書き方を作らなくてもいいのに…と思うのはもっともです。 自分で書くときはひとつだけ覚えておけばOKですが、他の人が書いたプログラムが読めるように、頭の片隅に入れておきましょう。

実際に手を動かして繰り返しを理解しよう

それでは、繰り返しを使って描画する練習として、9本の縦線を書くプログラムの思考過程を追体験してみましょう。 ここは最初の関門です。 繰り返しの文法はわかるんだけどいざ書こうとするとうまく書けないという人は多いのです。

ステップ 1 / 9
まずは3本ほど線を引いてみるか line(x1, y1, x2, y2) だっけ
function setup() {
createCanvas(100, 100);
background(196);
}
実行結果

慣れてきたら最初から最終形を思い浮かべて書くことができるようになると思います。 すっと理解できたという人は才能の塊です。調子に乗ってどんどん進みましょう。それ以外の人も大丈夫です。何度も練習しているうちにできるようになります。

条件分岐 (conditional)

次は条件分岐の if 文です。 条件が truefalse かで処理を変えることができます。 文法は以下の通りです。

if(条件){
// 条件が true のときにする処理
}
else{
// 条件が false のときにする処理
}

条件が false の場合にしたい処理が特にないときは else{...} は省略することができます。

条件分岐を繰り返しと一緒に利用する

それでは先ほどの縦線の例で条件分岐を利用する実例を見てみましょう。 まずは最初の4本と後の5本で線の太さを変えたいとすると次のように書くことができます。

function setup() {
createCanvas(100, 100);
background(196);
for(let i = 0; i < 9; i++){
let x = i * 10 + 10;
if(i < 4){
strokeWeight(4);
}
else{
strokeWeight(1);
}
line(x, 0, x, 100);
}
}
実行結果

次は太い線と細い線を交互にしてみましょう。「繰り返し回数が偶数」を条件とするとうまくいきます。 この条件は i % 2 == 0 と書くことができます。 「2で割った余りが0と等しい」ですね。

function setup() {
createCanvas(100, 100);
background(196);
for(let i = 0; i < 9; i++){
let x = i * 10 + 10;
if(i % 2 == 0){ // 変わったのはここだけ
strokeWeight(4);
}
else{
strokeWeight(1);
}
line(x, 0, x, 100);
}
}
実行結果

3つ以上に分岐することもできて、以下のように else if を使って書きます。

if(条件1){
// 条件1が true のときにする処理
}
else if(条件2){
// 条件1が false で、条件2 が true のときにする処理
}
else if(条件3){
// 条件1, 2が false で、条件3 が true のときにする処理
}
...
else{
// すべての条件が false のときにする処理
}

else if を使わないで次のように書くと分岐の仕方が変わりますので注意しましょう。

if(条件1){
// 条件1が true のときにする処理
}
if(条件2){
// 条件2 が true のときにする処理(条件1の結果に関わらず)
}
else{
// 条件2 が false のときにする処理 (条件1の結果に関わらず)
}

二重ループ

繰り返しの中にさらに繰り返しを書くということもできます。 次の例では、「縦に10個、四角形を描く」という繰り返しを10回繰り返すことで二次元的に四角形を10 x 10 = 100個描いています。

function setup() {
createCanvas(100, 100);
fill(0);
for(let i = 0; i < 10; i++){
for(let j = 0; j < 10; j++){
rect(i * 10, j * 10, 5, 5);
}
}
}
実行結果

このような繰り返しのことを「二重ループ」と呼びます。 二次元に画面を埋めていくためによく使います。 文法的には何重になってもOKですが、出てくるのは多くても三重ループくらいです。