計算で図形を描く

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

目次

変数

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

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

function setup() {
  createCanvas(100,100);
  let s;  // s という名前の変数を宣言する
  s = 20; // s に 20 を代入する
  rect(10, 10, s, s);
  rect(90, 90, s, s);
}

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

let 変数名;  // 変数の宣言
let x, y, z; // カンマ区切りで複数まとめて宣言してもOK

変数名 = 値;     // 変数に値を代入
let 変数名 = 値; // 宣言と代入を同時にしてもOK

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

定数

定数」もあります。変数との違いは、再代入できないことです。 定数も変数と同じように宣言してから使用します。

const 定数名 = 値;
定数名 = 新しい値; // 再代入しようとするとエラー

変更する予定がない値は定数として宣言しておくと、誤って変更しようとしたときにエラーが出て気付くことができます。命名規則は変数と同じです。

演算

足し算・掛け算・割り算など揃っています。 プログラム中で本格に使う前にデベロッパーツールのコンソール上で一通り使ってみましょう。 デベロッパーツールの使い方は覚えていますか? ちなみにコンソールでも変数や定数を定義して使うことができます。

  

以下のプログラム例のように、計算結果を変数に代入することもできます。 同じ point(x, 46) という命令でも、 x の値が変化しているので実行結果が変わります。

function setup() {
  let x = 21;
  point(x, 46);
  x = x - 2;     // x - 2 を計算して、その結果を x に再代入するという意味になります。つまり x の値が 2 減ります。
  point(x, 46);
  x = x + 9;     // 「x と x + 9 が等しいって?そんなバカな!」って最初は戸惑うと思います。
  point(x, 46);
}

条件の演算

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

条件の演算もコンソールを開いて一通り使ってみましょう。

コンソールの出力を見たらわかるように、条件演算の結果は true (条件成立) または false (条件不成立) のどちらかになります。 変数に代入するときは = で、等しいか調べるときは == (イコール2つ) で間違えやすいので注意しましょう。 以上・以下・等しくない、どれも2文字ですが、イコールは2文字目です。

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

A && B は「AかつB」の意味で A と B がともに true の場合にのみ true になります。 A || B は「AまたはB」の意味で A と B がともに false の場合にのみ false になります。 1 < x < 3 というようなまとめた書き方はできないので注意しましょう。 ちなみに &| というように1文字だと異なる演算になりますのでこれも注意しましょう。

繰り返し section2-1

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

while 文

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

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

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

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

x = 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ですが、他の人が書いたプログラムが読めるように、頭の片隅に入れておきましょう。

条件分岐

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

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

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

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の結果に関わらず)
}

二重ループ section2-3

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

for(let i = 0; i < 10; i++){
  for(let j = 0; j < 10; j++){
    console.log(i, j); // 補足: i,j の変化がわかりやすくなるように入れています
    rect(i * 10, j * 10, 5, 5);
  }
}

コンソールの出力もあわせて確認するとどのように繰り返しているかがわかりやすいかと思います。 i, j の2つの変数を使っていることに注意しましょう。 i と書くべきところに j と書いてしまうバグをよくやらかすので気を付けましょう。

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

練習として条件分岐を書き足して「偶数『行』の四角形の色を変える」「偶数『列』の四角形の色を変える」ようにしてみましょう。