はじめに
目的
初心者向けProcessing講座の総まとめ、実践編ということで、ブロック崩しを作成しようと思います!
初心者向け講座の一覧は、以下のプログラミングコンテンツから確認してみてください!

実際のプログラムはどのような感じかを掴んでいただいて、自分でも何かしら作れるようになっていただけると幸いです!
コードを一つ一つ意図なども説明するので、考え方なども参考にしてみてください!
※時短したい方は各記事最後のコードを丸コピでもOKです。
最終成果物
最終成果物として、以下のようなブロック崩しが初心者でも作れます!
プログラムを1行ずつ解説するので4partに分かれますが、誰でも理解できます!
前回のpart 1,2,3もぜひご参照ください!
-
-
ブロック崩しを作ろう!part1 〜図形描写〜
2024/12/15
ブロック崩しを作成しよう!プログラミング講座の最終章part1!
今回は図形描写まで!
すべてのコードに説明があるからわかりやすい!
-
-
ブロック崩しを作ろう!part2 〜ブロック・弾の移動〜
2024/12/22
はじめに 目的 初心者向けProcessing講座の総まとめ、実践編ということで、ブロック崩しを作成しようと思います!初心者向け講座の一覧は、以下のプログラミングコンテンツから確認してみてください! ...
-
-
ブロック崩しを作ろう!part 3 〜反射処理〜
2025/1/2
ブロック崩し作成part3!
今回は弾と壁、操作ブロック、障害物との反射処理を扱います!
1行ずつ解説するから理解しながら作れます!
今回やること
part 1:図形の描写
part 2:ブロック、弾の移動
part 3:反射処理
part 4:シーン遷移、各種調整

今回は最終のpart 4です!
ゲーム性向上の処理を追加します!
今回使う知識
各解説のリンクを貼っているので、詳細を知りたい方は解説記事もご覧ください!
ブロック崩し制作 part 4
STEP 0 ファイルを開く
前回作ったファイルを開いてください
ファイル > 開く (最近開いたファイルでもOK)

STEP 1 障害物の消去
目的
前回までは障害物に当たると、弾が反射するのみでした。
ブロック崩しなので、弾が当たった障害物は消えなければなりませんね。
なので、弾が当たった障害物を消去する処理を追加します!
考え方
弾が当たった障害物の描写処理を飛ばせば、障害物は描写されませんね。
なので、弾が当たったのかを保存する配列を作成します!
↓現在のコード


実装!
配列作成
まず、障害物が弾に当たったかどうかを管理する配列を作成します。
当たったかどうかの2値なのでboolean型で、要素数は障害物の数にしましょう!
23行目に記載している obs_status を追加します。

障害物の位置を決めている部分で obs_status の初期化も行います。

描写系への実装
実際の処理で行いたいことは以下の2つです。
- 弾が当たった障害物は描写しない
- 弾が当たった障害物は当たり判定もしない
なので、弾が当たった障害物に関する処理はスキップする方針で実装しましょう!
反射したら、obs_status 配列の値をfalseにして、描写する必要なしという状態にしておきます。(81行目)
そして、obs_status の値がfalseなら、continue文で、その障害物の処理ごとスキップします!

STEP 2 弾の反射のランダム性
目的
操作ブロックに弾が当たった場合、y方向のみ方向が変わるようにしていました。
ですが、それだと弾の移動角度が変わらず、弾が当たる範囲が限られてしまいます。
なので、弾が操作ブロックに当たった時、弾の移動方向を散らせる処理を追加します!
考え方
ベクトルの考え方を用います。
弾のスピードは、ball_speed変数で設定していました。
そしてball_speed変数とは別に、float型のspeedという配列を作成してもらいましたね。

ball_speedはx,yそれぞれの移動速度なので、ベクトルとしての大きさは、ball_speed×√2 の大きさになります。
(下の画像のaの値がball_speedに対応します。)

なので、弾が移動するベクトルの大きさをball_speed×√2 に統一すれば、方向を散らしても速度としては同じになります!
実装!
定数宣言
まず、√2の値を定数として宣言します。
定数とは、一度宣言すれば値を変えられない変数です。変更を想定していない変数は定数にするとバグが少なくなります。
Processingでは、型の前に「final」をつけると定数となります!
また、定数だとわかるように、先頭にkをつけておきます。(ドイツ語で定数を意味する konstant の先頭文字)

※sqrt(a)で、√aの値を意味します。
弾の速度をランダムにする
弾の速度にランダム性を持たせることが目的なので、
x方向の速度をランダムにしてみましょう!
速度の最大は、ベクトルの大きさに依存するので「0 ~ ball_speed × √2」 であれば良いです。
ですが、もし乱数が ball_speed × √2 の値を取った時、
真横に飛んでしまうので、操作ブロックとの衝突判定が何回も起こったりしてしまいます。
なので、x方向は「0 ~ ball_speed 」の範囲にしておきましょう!
y方向については、ベクトルとしての全体の大きさがball_speed ×√2 であれば良いことから、
x方向の値を使って三平方の定理を使って計算します!

※random(a, b)で、a~bまでの乱数をランダムに取ります。
※pow(x,2) でxの2乗を表します。
STEP 3 画面遷移
目的
ブロックを全て消した時、または弾が操作ブロックの下に行ってしまった時、特に何も起こらず
それ以上進めなくなってしまいます。
なので、ゲームクリア処理、ゲームオーバー処理を作成します!
考え方
ゲームクリア時
ゲームクリアの時は、障害物が全て消えた時ですね。
なので、描写されている障害物の数を数えて、その数が0になった場合、ゲームクリアとしましょう!
ゲームオーバー時
ゲームオーバーの時は、弾が操作ブロックの下に行ってしまった時ですね。
なので、弾の位置が操作ブロックの下(ウインドウの下側)に行ってしまったらゲームオーバーとしましょう!
実装!
シーン切り替えの処理の準備
シーン(ゲーム中、ゲームクリア、ゲームオーバー)の切り替えができるように、シーン管理用の変数を作成しましょう!

そしてシーンを明確に切り替えるために、先ほど宣言したsceneの値で分岐処理をします。
今まで書いていたゲーム中の処理を、switch文のなかに丸ごと埋め込みましょう!
ゲーム中の処理とは、draw関数の中身全てです!
やり方
1.まずdraw関数の先頭に、switch文と、条件(case 0:)を挿入します。

2.draw関数の最後に、「}」を追加します。
1.で書いた { に対応しています。

3.見やすくするために、switch文のcase 0の処理(ゲーム処理)を全て選択して、Tabキーを押します。
↓選択範囲


↓Tabキーを押した結果(スペースが空いて見やすくなった)

ここまでできれば、シーン切り替えの準備は完了です!
ゲームクリアの処理
ゲームクリアの判定をするために、残っている障害物の数を数える変数を宣言します。

続いて、障害物を描写する回数を数えれば良いので、障害物を描写する処理の直後に、
先ほど宣言した変数の値を +1 するようにします。

ただ、1フレーム(draw関数1回分)で何個障害物が残っているかをカウントしたいだけなので、
draw関数の先頭で、数えた障害物の数を0に戻します。

最後に、数え上げた障害物の数が0になっていればクリアなので、その判定処理を追加してシーン変数を切り替えます!

ゲームオーバーの判定
弾の位置が画面下に行ってしまったら判定をすれば良いので、その判定をしてシーン変数を切り替えましょう!
クリア判定の下にゲームオーバー判定を追加します。

シーン切り替え
最後の工程です!
scene が 1の時、ゲームクリアの処理。
scene が 2の時、ゲームオーバーの処理。
としたいので、switch文での処理を追加します!

switch文は、break;を入れないと、条件を満たしていなくても次のcaseの部分も処理してしまうので、
break;は忘れないようにしましょう!
また、ゲームクリア時には、画面中央に「Clear」と表示するようにしました。
textSize()で文字の大きさ、
text("Clear",横位置, 縦位置);でテキストを描写します。
完成品
part 1~ part 4までの全ての処理を入れると、ブロック崩しゲームが完成します!
宣言したパラメータ等を変えれば動画以外のレイアウトや設定も可能なので、ぜひ色々お試しください!
終わりに
お疲れ様でした!
今回は最終回のpart4ということで、ゲーム性を持たせるための処理を追加しました。
かなり事細かに書いたので、全てを熟読することは難しいかもしれません。。
ですが、コーディングの理由や背景を記載しているので、プログラムを書く上で参考になれば幸いです!
これで初心者向けプログラミング講座シリーズも一旦終了です!
ここまで見てくださった方に感謝を申し上げます!!

Take It Easy!Take It Breezy!
本当にお疲れ様でした!!
最終的なコード
/* TAKEのIT風万記
ブロック崩し サンプルコード
by Processing
*/
//定数
final float kRoot2 = sqrt(2);
//操作ブロック
int block_pos_x; //ブロックの横位置
int block_pos_y; //ブロックの縦位置
int block_width = 100; //幅
int block_height = 10; //高さ
//弾
int ball_pos_x; //弾の横位置
int ball_pos_y; //弾の縦位置
int ball_radious = 5; //弾の半径
int ball_speed = 4; //弾のデフォルトスピード
float[] speed = {ball_speed, ball_speed}; //実際の弾の速度用配列(ゲーム性のため)
int ball_move_x_flg = 1; //横方向フラグ:1で右方向、-1で左方向
int ball_move_y_flg = -1; //縦方向フラグ:1で下方向、-1で上方向
//障害物
int obs_num = 10;//障害物の数
int obs_radious = 20; //障害物の半径
int[][] obs_pos = new int[obs_num][2]; //障害物の位置 [障害物No][0:x、1:y]
boolean[] obs_status = new boolean[obs_num]; //障害物の存在有無
int obs_left_num = 0; //残っている障害物の数を数える
//場面管理
//0:ゲーム中、1:クリア、2:ゲームオーバー
int scene = 0;
void setup(){
size(500,500);
//操作ブロック初期位置
block_pos_x = width/2;
block_pos_y = height - block_height;
//弾初期位置
ball_pos_x = width/2;
ball_pos_y = block_pos_y - block_height;
//障害物初期位置
for (int i = 0; i < obs_num; i++) {
obs_pos[i][0] = int(random(0, width));
obs_pos[i][1] = int(random(0, height * 7/10));
obs_status[i] = true;
}
}
void draw(){
switch(scene){
case 0:
/* 初期化処理 */
obs_left_num = 0;
/* 反射処理 */
//壁反射
ball_move_x_flg = FlgByHitWall(ball_pos_x, width -ball_radious,ball_radious, ball_move_x_flg);
ball_move_y_flg = FlgByHitWall(ball_pos_y, ball_pos_y ,ball_radious, ball_move_y_flg);
//操作ブロックとの反射
if(FlgByHitWall(ball_pos_x, block_pos_x + block_width, block_pos_x, 0) == 0 &&
FlgByHitWall(ball_pos_y + ball_radious, block_pos_y + block_height, block_pos_y, 0) == 0){
ball_move_y_flg = -1;
/*弾速度計算
x方向:0 ~ ball_speedまで
y方向:x^2 + y^2 = (ball_speed * √2)^2 を y = に変形して計算。
*/
speed[0] = random(0,ball_speed); //x方向
speed[1] = sqrt(pow(ball_speed * kRoot2, 2) - pow(speed[0], 2)); //y方向
}
//座標更新
block_pos_x = mouseX - block_width/2;
//弾位置計算
ball_pos_x += speed[0] * ball_move_x_flg;
ball_pos_y += speed[1] * ball_move_y_flg;
/* 描写系 */
background(0);
//操作ブロック
rect(block_pos_x, block_pos_y, block_width, block_height);
//弾
ellipse(ball_pos_x, ball_pos_y, ball_radious*2, ball_radious*2);
//障害物ブロック
for(int i = 0; i < obs_num; i++){
if (obs_status[i] == false){
continue; //障害物が弾に当たっていたなら処理をスキップ
}
//反射判定
if(IsObjRefrection(ball_pos_x,ball_pos_y,ball_radious,obs_pos[i],obs_radious)){
ball_move_x_flg *= -1;
ball_move_y_flg *= -1;
obs_status[i] = false;
}
ellipse(obs_pos[i][0], obs_pos[i][1], obs_radious*2, obs_radious*2); //描写
obs_left_num++; //描写されている障害物の数をカウント
}
if (obs_left_num == 0){ //描写されている障害物が0ならば、
scene = 1; //クリアシーンへ
}
if (ball_pos_y > height){ //弾が画面の下に行ってしまったら
scene = 2;
}
break;
case 1: //ゲームクリア処理
textSize(20);
text("Clear", width/2, height/2);
break;
case 2: //ゲームオーバー処理
textSize(20);
text("Game Over", width/2, height/2);
break;
}
}
//壁反射フラグを返す関数
int FlgByHitWall(int ball_pos, int upper, int lower, int currentFlg){
if(lower <= ball_pos && ball_pos <= upper){
return currentFlg;
}else if(ball_pos < lower){
return 1;
}else if(upper < ball_pos){
return -1;
}
return currentFlg;
}
//障害物との反射判定
boolean IsObjRefrection(int ball_pos_x, int ball_pos_y, int ball_rad, int[] obs_pos, int obj_rad) {
return pow(obs_pos[0] - ball_pos_x, 2) + pow(obs_pos[1] -ball_pos_y, 2) < pow(obj_rad + ball_rad, 2);
}