コンテンツにスキップ

2. 下の土管をつくる

Processingでフラッピーバード風ゲームをつくっていきます。

前回はプレイヤーの動きをつくっていきました。今回は土管、ただし下の土管だけをつくります。

そのまえに

前回までのソースコード

float x = 100;
float y = 200;
float speed = 0;
void setup(){
size(600, 400);
}
void draw(){
background(0);
speed = speed + 1;
y = y + speed;
rect(x, y, 50, 50);
}
void mousePressed(){
speed = -10;
}

前回までのプログラムコードは一旦忘れて、土管用の新しいスケッチで作成して、あとで前回までのソースコードとがっちゃんこしようと思います。

(前回のプレイヤーの動きのソースコードが重なってしまって説明しづらいからです。)

新しいスケッチを作成するには、ファイル>新規を選択しましょう。

2-1 下の土管を表示する

setupとdraw

void setup(){
size(600, 400);
}
void draw(){
background(0);
}

ここは基本です。

下の土管を表示する

void setup(){
size(600, 400);
}
void draw(){
background(0);
rect(400, 200, 50, 200);
}

drawの中でrect(400, 200, 50, 200);とrect命令を書きました。

これは、

  • 横の位置400、たての位置200に表示する
  • 横幅50、たて幅200の大きさで表示する

という命令です。

img01.png

画面のたて幅が400なので、土管を表示する縦の位置200を引いて、たて幅200としています。

変数を使って土管を表示する

前回のプレイヤー同様、変数を使って土管を表示してみましょう。

// 土管の表示位置を変数にした!
float dx = 400;
float dy = 200;
void setup(){
size(600, 400);
}
void draw(){
background(0);
rect(400, 200, 50, 200);
}

土管の表示位置を変数にしました。変数名は土管(DOKAN)のxで「dx」、土管(DOKAN)のyで「dy」としました。

では、「dx」と「dy」を使って土管を表示してみましょう。

float dx = 400;
float dy = 200;
void setup(){
size(600, 400);
}
void draw(){
background(0);
rect(dx, dy, 50, 200);
}

drawの中のrect命令のところを、rect(dx, dy, 50, 200);として、rect命令の横の位置に「dx」を、たての位置に「dy」を伝えるようにしました。

これで下の土管の表示は完了でしょうか?

いえ、高さの問題が残っています。

土管のたて幅を変える

例えば、たての位置の数字が入った変数「dy」の中身を、200から100に変えてみましょう。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
rect(dx, dy, 50, 200);
}

実行すると、下のような画面になります。

img02.png

どうでしょうか?下の土管が宙に浮いてしまいましたね。

なぜ宙に浮いてしまうかというと、たて幅が足りてないからなんですね。

rect(dx, dy, 50, 200);

上のrect命令では、表示位置を変数にしましたが、たて幅が200のままですね。つまり、たての位置である変数「dy」が変わったら、たて幅も変更しないといけません。

どうしたらたて幅を変更できるでしょうか?

そもそも、「たて幅はいくつに設定すべきなのでしょうか?」

土管のたて幅 = 画面のたて幅 - 土管のたての位置

という計算式が頭に浮かんだでしょうか?これはプログラミングとは違う、算数の話ですね。

「画面のたて幅」は、size(600, 400);で命令したように、400です。「土管のたての位置」は、変数になっていて、dyです。

つまり、土管たて幅 = 400 - dyという計算式になりますね!

rect命令に当てはめてみると、

rect(dx, dy, 50, 400 - dy);

こうなりました。

rectの命令のたて幅のところを、「400 - dy」としておくことで、例えば、たての位置の変数「dy」が100になった場合はもちろん、10だろうが300だろうが、土管は宙に浮くことなく表示されます。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
rect(dx, dy, 50, 400 - dy);
}

実行ボタンを押して実行すると、下のような画面になります。土管が宙に浮くことがなくなりました。

img03.png

このように、計算から導き出せる数字もあることを覚えておきましょう。

2-2 下の土管を横に動かす

では土管を動かしていきます。

動かす方向は横、左方向です。また、土管は自動的に、一定のはやさで動きます。

Scratchで考えてみよう

まずはここまでの変数「dx」と変数「dy」を使ったScratchのプログラミングです。

img04.png

「ずっと」の外で変数「dx」を400、「dy」を200に設定しています。(dyの数字は200にもどしてあります)

このままでは土管は動きませんよね。

どうしたらいいでしょうか?

土管は「横」に動かします。ということは、変数「dx」が関わってきますね。

動かす方向は「左」です。数字が小さくなるほど左になりますので、変数「dx」がだんだん小さくなれば良さそうですね!

img05.png

「ずっと」の中で、変数「dx」を「マイナス1」しています。

「ずっと」が繰り返されるたびに、1小さくなりますので、変数「dx」の数字は、399,398,397…とだんだん小さくなっていきます。

変数「dx」をだんだん小さくするプログラムを書く

Scratchで、変数「dx」をだんだん小さくなるプログラムが書けました。これをProcessingでプログラムしていきましょう。

まずは「//(スラッシュ二つ)」を使ってコメントしていきます。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
// dxを-1ずつ変える
rect(dx, dy, 50, 400 - dy);
}

Scratchでは、「ずっと」ブロックの中で「dxを-1ずつ変える」を書きました。「ずっと」ということは、Processingではdrawですよね。なのでdrawの中に「dxを-1ずつ変える」をプログラミングできれば良さそうですね。

「dxを-1ずつ変える」をプログラミングできますか?

dx = dx - 1;

答えはこうなります。そんなに難しくありませんね。

まとめるとこうなります。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 1;
rect(dx, dy, 50, 400 - dy);
}

実行ボタンを押して実行すると、土管が左に移動するようになったと思います。

土管のはやさを調節する

土管が左に動くようになりました。

しかしなんかトロいですね。。。

もうちょっとはやくしましょう!

はやく動くようにするにはどうしたらいいでしょうか?

dx = dx - 1;

ヒントはこの1行のどこかを変更することです。

「はやく動く」ということは「はやく左に動く」ということ。

「はやく左に動く」ということは「はやく数字が小さくなる」ということ。

つまり、変数「dx」の数字が小さくなるのをはやくすれば良さそうですね!

はやく小さくするにはどうしたらよいでしょうか?

dx = dx - 1;

今は1ずつ小さくなっていますよね?

では、5ずつ小さくしたらどうでしょうか?1ずつ小さくなるより、5ずつ小さくした方がはやいですよね?

5ずつ小さくするには?

dx = dx - 5;

はい、「マイナス5」にすればOKです。

簡単でしたかね?

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
// マイナス5に変更した!
dx = dx - 5;
rect(dx, dy, 50, 400 - dy);
}

実行ボタンを押して実行すると、土管のはやさがアップしたと思います。

2-3 下の土管を繰り返し出現させる

土管の移動ができました。

ですが、左端までいったら画面から消えてしまいますね。

左端までいったら、もう一度右端から出現するようにしましょう。

左端と右端とはなにか

左端とは横の位置が「0」ということです。

では右端とは?

size(600, 400);

size命令により画面の大きさは決めていましたよね。

そう、600です!

Scratchでプログラミング

Scratchで左端にいったかどうかを調べるプログラミングをしてみましょう。

img06.png

「ずっと」の中に「もし〜なら」というブロックを追加しました。

この「もし〜なら」は

「もし画面の左端までいったなら」という風に書きたいですよね。

画面の左端とは「0」ですよね?

何が0になるかを調べるかというと、変数「dx」ですよね。

なので、「もしdxが0より小さいなら」という条件を、ここでは入れています。

では、「もしdxが0より小さなら」なにをすればよいでしょうか?

画面の右端からまた出現させるんですよね?

画面の右端ということは、600です。

なのでこのようになります。

img07.png

「もし〜なら」のブロックの中に、「dxを600にする」というブロックをおきました。

こうすることで、

変数「dx」が0より小さくなったときに、変数「dx」が600になり、「もしdxが0より小さいなら」のブロックの中を通らなくなります。

ですが、変数dxは「ずっと」「dxを-5ずつ変える」ので、また0より小さくなるときがきます。

すると、また右端である600に設定され、-5ずつされ・・・・

と繰り返し繰り返し、永遠と同じ動きをするというアルゴリズムになります。

「もし〜なら」をプログラミングする

Scratchで書いたプログラムを、コメントという形でProcessingに書いてみましょう。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
// もし dx < 0 なら
// dxを600にする
rect(dx, dy, 50, 400 - dy);
}

書けましたでしょうか?

Processingで「もし〜なら」をプログラミングするには、

if(条件){
}

こういった、if条件分岐を使います。

「条件」の中には「dx < 0」といった、「何がどうなったら」という条件を書いてあげます。

ではさっそく当て込んでみましょう。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
//「もし dx < 0 なら」をプログラミングした!
if(dx < 0){
// dxを600にする
}
rect(dx, dy, 50, 400 - dy);
}

drawの中にif(dx < 0)を追加しました。これが「もしdxが0より小さければ」の書き方です。

また、{}という記号があることにも注目してください。

これは「もし〜なら」の開始と終了を示すものです。

例えば、「もし〜なら」の終了を示す}がrect命令に下においてしまったら、、、

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
// もし dx < 0 なら
if(dx < 0){
// dxを600にする
rect(dx, dy, 50, 400 - dy);
}
}

rect命令は、「もし〜なら」の条件のブロックの中にいることになってしまい、「もしdxが0より小さい」ときにしかrect命令は実行されないことになってしまいます。

if条件分岐の終了の}を忘れると、思わぬバグになります。

終了の}を忘れないようにするには、

Processing > 編集 > 自動フォーマット

から、プログラムコードをきれいに整形しておくことで、終了の終了記号「}」が思わぬところにないか、チェックすることができます。

日頃からプログラムコードを整形するようにしておきましょう。

もし〜なら何をする?

さて、まだ「もし〜なら」は終わりではありません。

「もし〜なら」なにをするの?

というプログラムを書いていませんからね。

img07.png

Scratchの例を思い出します。

「もしdxが0より小さいなら」 「dxを600にする」

とあります。

Processingでプログラミングしてみましょう。

終了の}を忘れないように!

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
// もし dx < 0 なら
if(dx < 0){
// dxを600にする
dx = 600;
}
rect(dx, dy, 50, 400 - dy);
}

ifの{}で囲まれた行にdx = 600;と書きました。

実行ボタンを押して実行してみましょう。

これで、dxが0より小さいとき、つまり左端にいったときだけ、dxは600、つまり右端に戻るようになりました。

左端を調整する

実行してみると気づくかもしれませんが、左端にいったときに瞬間移動してしまいますよね?

なんかスムーズではない。

なぜでしょうか?

それは「変数dxが土管の左端だから」です。

土管がすっぽり画面からはみ出した瞬間というのは、

正しくは、「dxが0より小さくなった」ときではなく、

「土管の右端が0より小さくなった」ときなんです。

では、土管の右端はいくつでしょうか?

土管の右端 = dx + 土管の横幅

で表せることに気づいたでしょうか?

土管の横幅は、

rect(dx, dy, 50, 400 - dy);

と、rect命令にあるように、50です。

つまり、

土管の右端 = dx + 50

と表すことができます。

これをそのまま、ifの条件に書けばOKです。

すると、

if(dx + 50 < 0)

という条件の書き方となります。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
// 条件を変更した!
if(dx + 50 < 0){
dx = 600;
}
rect(dx, dy, 50, 400 - dy);
}

実行ボタンを押して実行してみると、瞬間移動がなくなり、スムーズに右端から土管が出現するようになったと思います。

2-4 出現するときに土管の高さをランダム(乱数)にする

繰り返し土管が出現するようになりました。

出現するときに、土管の高さがランダム(乱数)、ばらばらだとゲームっぽくなりますよね。

土管の高さを変更するには?

土管を表示するrect命令をもう一度みてみましょう。

rect(dx, dy, 50, 400 - dy)

土管のたて位置は、変数「dy」でした。土管のたて幅は、「400 - dy」でした。

変数「dy」の中の数字を、100や300に変更すれば、土管を表示するたての位置が変わり、土管のたて幅も変わるようにrectには命令してあります。

なので、変数「dy」を変更すれば良さそうですね。

今回は高さをランダムにするので、変数「dy」をランダムにすればOKです。

Scratchでプログラミング

ではまたScratchでプログラミングしてみましょう。

img08.png

「もし〜なら」の条件ブロックの中に、「dyを200から300までの乱数にする」というブロックをおきました。

「dyを600にする」と同時に、「dyを200から300までの乱数にする」をしておけば、右端から出現する際に、土管の高さはランダムになっているはずです。

乱数をプログラミングする

dyを200から300までの乱数にする

このScratchのブロックを、Processingでプログラミングしていきます。

まずはコメントで書いてみます。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
if(dx + 50 < 0){
dx = 600;
// dyを200から300まで乱数にする
}
rect(dx, dy, 50, 400 - dy);
}

コメントを入れました。ランダムにするのは右端から出現するときなので、「dyを200から300までの乱数にする」のはif条件分岐の中、{}にはさまれた行の中で行なっています。

さて、乱数を発生させるには、

random(いくつから, いくつまで)

という命令を使います。

今回は、200から300まで、なので、

random(200, 300)

とします。

では、if条件分岐の中に、random命令を入れてみましょう。

と、ここで、みんながよくやりがちな間違いを先に。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
if(dx + 50 < 0){
dx = 600;
// dyを200から300まで乱数にする
random(200, 300);
}
rect(dx, dy, 50, 400 - dy);
}

if条件分岐の中にrandom(200, 300);という行を追加しました。

実行してみても、高さはランダムになりません。

なぜでしょうか?

それは、「発生させた乱数をdyに入れてない」からです。

random(200, 300);

この行は、「乱数を発生させたけど、作った乱数は放置した」状態です。

せっかく乱数を作っても、作った乱数と変数「dy」はなんの関係もありません。

どうすればいいか?

簡単です。「=(イコール)」で代入すればよいです。

dy = random(200, 300);

これでrandom命令が作ってくれた乱数を、dyに上書きすることができました。

float dx = 400;
float dy = 100;
void setup(){
size(600, 400);
}
void draw(){
background(0);
dx = dx - 5;
if(dx + 50 < 0){
dx = 600;
// dyを200から300まで乱数にする
dy = random(200, 300);
}
rect(dx, dy, 50, 400 - dy);
}

実行すると、右端から出現するたびに、土管の高さが変わっていることが確認できます。