スポンサーリンク
当サイトにはプロモーションが含まれています
※広告で得た収益はすべて利用者さんに還元しています。

ゲーム開発室通信 Vol.5 ~避けゲーのプログラミング解説その⑤~

ゲーム開発室
スポンサーリンク

第四ステージの障害物の動作編

ご訪問いただき誠にありがとうございます。

今回は第四ステージの障害物の処理について解説いたします。
第四ステージは2つの障害物に前後を挟まれてしまいます。挟まれた後は、一定間隔で障害物が移動しますので、障害物に触れないように動きを合わせる必要があります。障害物は途中で止まったり、戻ったりと複雑な動きをしますので、障害物の動きをじっくり観察しましょう。障害物の動きを見切ってステージの端まで辿り着いたらクリアです。


実際の「避けゲー」で遊びたい方は以下よりアクセスしてください。


ソースコードはGitHub上にアップしていますので適宜参考にしてください。



第四ステージの制御

第四ステージはenemy6関数とreenemy6関数で制御しています。enemy6は往路時の障害物の制御、reenemy6は復路時の障害物の制御を行っています。第三ステージ同様、enemy6、reenemy6の両関数ともほぼ同じ内容となっていますのでこちらも1つの関数にまとめられそうですね。

第四ステージの障害物の動きはかなり複雑ですので先に整理しておきます。

第四ステージの障害物のシーケンス
  • 仕掛け発動前
    スタート時

    200pxの位置に右側の障害物を表示

  • 仕掛けの発動
    仕掛けの発動

    自キャラのx座標が100pxを超えた(右側の障害物に近づいた)ら仕掛けが発動

  • 仕掛け発動後
    0秒~2.5秒間

    150pxの位置に左側の障害物が出現(自キャラを挟み込む)

  • 2.5秒~12.5秒間

    左右の障害物が並んで右に移動
    障害物は移動しながら左右の間隔が少し開いていく

  • 12.5秒~14.0秒間

    障害物が一旦停止する(次にどうなるのかのドキドキ感を演出)

  • 14.0秒~14.5秒間

    障害物が左に移動(スピードを少し速くしてユーザを焦らせる)

  • 14.5秒~15.0秒間

    障害物が右に移動

  • 15.0秒~15.5秒間

    障害物が左に移動

  • 15.5秒~18.0秒間

    障害物が右に移動

  • 18.0秒~19.75秒間

    右の障害物は一旦停止
    左の障害物は右に移動し追いかけてくる(障害物間の距離が縮まりさらにドキドキさせる演出)

  • 19.75秒~23.0秒間

    右の障害物が消える(ここまで生き延びれば出口が見えてホッとさせる)

  • 23.0秒~

    早いスピードで左の障害物が追いかけてくる(油断しているユーザを翻弄)

ではアクティビティ図と実際のソースコードを見ていきます。アクティビティ図はenemy6の方だけ載せています。障害物は派手に動いていますが、処理はものすごく地味です。画像が小さくて見にくい場合は画像をダウンロードしてフォトなどの画像ビューアツールで拡大して見てください。

enemy6関数のアクティビティ図

enemy6関数のコード(avoider.jsから抜粋)
let e6 = false;		//障害物の仕掛けの稼働許可をクリア
let ex6 = 200;	//移動する障害物のベースとなるx座標
let em6 = 150;	//移動する障害物の演出用のカウンタ
let et6 = 0;	//障害物の仕掛け用のカウンタ
function enemy6() {
  	ctx.beginPath();

	//右側の障害物の表示(仕掛けの発動から20秒以降は障害物を表示しない)
  	if (et6<400) {
    	ctx.rect(ex6, 0, 20, 320);	//初期の障害物表示
  	}

	//左側の障害物の表示
	if (et6<20&&et6!=0) {
    	ctx.rect(ex6-em6+10-et6/2, 0, et6, 320);	//左側の障害物を徐々に表示させる演出
  	}else if (et6>=20) {
    	ctx.rect(ex6-em6, 0, 20, 320);	//2秒後以降は障害物を移動させながら常に表示させる
  	}
  	ctx.fillStyle = "yellow";
  	ctx.fill();
  	ctx.closePath();

	//自キャラが障害物に近づいたら仕掛けを稼働
  	if (x>=100) {
    	e6=true;	//障害物の仕掛けを稼働させる
   	}

   	if (e6) {
    	et6=et6+0.2;	//障害物の仕掛け用のカウンタをアップ
   	}

   	if (et6<50) {			//仕掛けの稼働後の0秒~2.5秒まで
		//処理なし(仕掛けとめる)
   	}else if (et6<250) {	//仕掛けの稼働後の2.5秒~12.5秒まで
    	ex6=ex6+0.5;	//障害物を右に移動(スピードは遅め)
    	em6=em6+0.1;	//左右の障害物の間隔を徐々に広げる
  	}else if (et6<280) {	//仕掛けの稼働後の12.5秒~14.0秒まで
		//処理なし(仕掛けとめる)
	}else if (et6<290) {	//仕掛けの稼働後の14.0秒~14.5秒まで
    	ex6=ex6-1.5;	//障害物を左に移動(スピードは少し早め)
  	}else if (et6<300) {	//仕掛けの稼働後の14.5秒~15.0秒まで
    	ex6=ex6+1.5;	//障害物を右に移動(スピードは少し早め)
  	}else if (et6<310) {	//仕掛けの稼働後の15.0秒~15.5秒まで
    	ex6=ex6-1.5;	//障害物を左に移動(スピードは少し早め)
  	}else if (et6<360) {	//仕掛けの稼働後の15.5秒~18.0秒まで
    	ex6=ex6+1.5;	//障害物を右に移動(スピードは少し早め)
  	}else if (et6<395) {	//仕掛けの稼働後の18.0秒~19.75秒まで
    	em6=em6-1;	//左側の障害物だけ右に移動
  	}else if (et6<460) {	//仕掛けの稼働後の19.75秒~23.0秒まで
  	}else{	//仕掛けの稼働後の23秒以降
    	ex6=ex6+4;	//障害物を右に移動(スピードは早め)
  	}
 
	//右の障害物との衝突判定
  	if (et6<=400&&x>ex6-20) {
    	gameover=true;	//障害物にぶつかったと判定し、ゲームオーバーの判定をセットする
  	}
	//左の障害物との衝突判定
	if (et6!=0&&x<ex6-em6+40) {
    	gameover=true;	//障害物にぶつかったと判定し、ゲームオーバーの判定をセットする
  	}
}

reenemy6関数のコード(avoider.jsから抜粋)
let re6 = false;		//障害物の仕掛けの稼働許可をクリア
let rex6 = 900;	//移動する障害物のベースとなるx座標
let rem6 = 150;	//移動する障害物の演出用のカウンタ
let ret6 = 0;	//障害物の仕掛け用のカウンタ
function reenemy6() {
  	ctx.beginPath();
  	if (ret6<400) {
    	ctx.rect(rex6+Rex, 0, 20, 320);
  	}else if (ret6<420) {
    	ctx.rect(rex6+10-(420-ret6)/2+Rex, 0, 420-ret6, 320);
  	}

  	if (ret6<20&&ret6!=0) {
    	ctx.rect(rex6+rem6+10-ret6/2+Rex, 0, ret6, 320);
  	}else if (ret6>=20) {
    	ctx.rect(rex6+rem6+Rex, 0, 20, 320);
  	}
  	ctx.fillStyle = "yellow";
  	ctx.fill();
  	ctx.closePath();
  	if (x<=1000) {
    	re6=true;
   	}
   	if (re6) {
    	ret6=ret6+0.2;
   	}
   	if (ret6<50) {
   	}else if (ret6<250) {
    	rex6=rex6-0.5;
    	rem6=rem6+0.1;
  	}else if (ret6<280) {
  	}else if (ret6<290) {
    	rex6=rex6+1.5;
  	}else if (ret6<300) {
    	rex6=rex6-1.5;
  	}else if (ret6<310) {
    	rex6=rex6+1.5;
  	}else if (ret6<360) {
    	rex6=rex6-1.5;
  	}else if (ret6<395) {
    	rem6=rem6-1;
  	}else if (ret6<460) {
  	}else{
    	rex6=rex6-4;
  	}
 
  	if (ret6<=400&&x<rex6+40) {
    	gameover=true;
  	}
  	if (ret6!=0&&x>rex6+rem6-20) {
    	gameover=true;
  	}
}

分周

今回のプログラムでは基本となる分解能は10msecとしています。最小の分解能で制御しているとチック数(カウント数)が大きくなってしまいます。そういった場合は適度な大きさに分周すると扱いやすくなります。

et6=et6+0.2

第四ステージではチック数をカウントするカウンタ用の変数et6を用意していますが、これは10msecに1回の割合で0.2ずつ増やしています。つまり10msecを1/5に分周していることになり、et6を整数でカウントするときは50msecとして扱うことができます。10msecでカウントするときよりも低い分解能で制御することができます。


最後までご覧いただきありがとうございます。

今回は第四ステージの障害物の動きを見ていきました。第四ステージでは前後に障害物が現れ、2つの障害物が、ある期間はバラバラの動きを見せたり、ある期間は同期を取って動いたりとさまざまな演出を加えて、ユーザを楽しませる工夫をしています。

細かいタイムテーブルに従って処理を行っているため、if文での分岐が多くなっていますが、処理としては障害物のx座標を足したり、引いたりしているだけの凄く地味なコードです。「システム開発」とか「ソフトウェア開発」って聞くと華やかな仕事であるような印象を持たれるかもしれませんが、プログラマはこのような地味なコードを1行1行書くことが仕事なんです。

さらに今回は「分周」についても取り上げました。ベースとなる分解能を分周して低くすることで扱う数値の桁を下げたり、分かりやすい値にしたりといった工夫についても見ていきました。システム開発ではこういった制御がそこかしこで使われています。

次回は第五ステージの障害物の動きについて見ていきます。第五ステージは天井と床から先のとんがった障害物が上下に動いて行く手を阻んでいます。タイミングを見計らって進んでいくステージとなります。

次回のブログにもお付き合いいただけると嬉しいです。

【PR】FXを始めるなら【DMM FX】!