マリオコンテニュー処理の実装

マリオのコンテニュー処理の実装(プログラミングでマリオを作る第44回)

一週間も空かずに今回は、コンティニュー処理の実装をします。

実行結果は以下のようになります。

マリオのコンティニューの実装

マリオがやられた時に、残機が一つ減って、ステージ表示画面に入り、
残機が残っている場合は、そのステージから再開させる処理
を書きます。

同時に残機が亡くなったときのゲームオーバー画面も作ります。

その場合タイトル画面に戻りますが、その実装はまた今度やります。

今回実装する内容

1.マリオがやられたら、ステージ表示画面に移動する

2.ステージ表示画面の作成

3.コンテニュー時にマリオと敵とマップ状態の一部を初期化する

4.ゲームオーバー画面の作成

では、実装していきましょう。

const.js

メイン画面にて、タイトル画面・ステージ前画面・ステージ画面・ゲームオーバ画面と描画と処理を分けるために定数を宣言しておきます。

// chapter44
let TITLE = 0
let IN_STAGE = 1;
let PRE_STAGE = 2;
let GAME_OVER = 3;
// マリオがやられてからコンテニュー画面に行くまでの時間
let CONTINUE_CNT = 60;
// ステージ前画面からステージ画面へ遷移するまでの時間
let PRE_STAGE_CNT = 150;

global.js

ステージの状態を記録しておくためのglobal変数を定義します。

// chapter44
let gState = IN_STAGE;
// ステージ数
let gTotalStageNumber = 1;
let gWorldNumber = 1;
let gSubWorldNumber = 1;

gStateにゲームオーバーやステージ中などの状態を保存します。

gWorldNumberは1-2の1の部分で、gSubWorldNumberは1-2の2の部分に該当させます。

mario.js

マリオのメンバー変数にコンテニュー画面に以降する際のカウンタを追加します。

// chapter44
this.continueCnt = 0;			// マリオが死んでからコンテニュー画面に遷移するまでのタイマー時間

コンテニュー後にマリオの位置などを初期化する必要があるので、
初期化関数を用意します。

とりあえず、定数やコンテニュー回数、オブジェクト生成以外は、変数宣言時の条件をそのまま適応させます。

Mario.prototype.init = function(posX,posY){
	this.addPosX = 0;
	this.addPosY = 0;
	this.posX = posX;
	this.posY = posY;
	...
}

続いて、死亡時にコンテニュー画面に遷移するために、deadAction関数を修正します。

Mario.prototype.deadAction = function(){
	if(this.state == DEAD_ACTION){
		this.posY -= this.addPosY;		// 上昇と下降
		if(this.addPosY >= -MAX_GRAVITY)
		{
			this.addPosY -= 1;
		}
		if(this.posY > 480)
		{
			this.state = DEAD;		// 死亡
		}
	}
	else if(this.state == DEAD){
		// 一定時間たったらコンテニュー画面へ
		if(this.continueCnt++ >= CONTINUE_CNT){
			this.playerNum--;
			if(this.playerNum <= 0){
				gState = GAME_OVER;
			}
			else{
				gState = PRE_STAGE;
			}
		}
	}
}

gState変数によって描画や動作場面を分ける処理をmain.jsに書いていくことになります。

main.js

マリオがコンテニューした際にステージ内のマップチップを以前の状態に初期化する必要があるので、最初のマップチップを別の変数に保存します。

javascriptのarrayでは、そのままマップチップを代入すると互いに参照してしまうので、copyして渡すようにします。

let gMapChipCopy = JSON.parse(JSON.stringify(gMapChip));

そして、ステージ前画面からステージ画面に遷移する際に、
最初にコピーした値を参照しているマップチップ配列代入するようにします。

/**
 * stage1用の初期化
 */
function initStage1(){
  // 敵の初期化
  initStage1Enemy();
  // マップ初期化
  gMapChip = JSON.parse(JSON.stringify(gMapChipCopy));
  gBonusMapChip = JSON.parse(JSON.stringify(gBonusMapChipCopy));
  gMario.init(0,384);
}

敵の初期化

initStage1Enemy関数で敵も初期化します。

敵のクラスに状態を初期化するinit関数を実装して、呼んであげましょう。

function initStage1Enemy(){
  // kuribo
  gKuribos[0][0].init(1024,384,LEFT_DIR);
  gKuribos[0][1].init(1088,384,LEFT_DIR);
  // nokonoko
  gNokos[0][0].init(384,396,RIGHT_DIR);
  gNokos[0][1].init(192,396,LEFT_DIR);
}

状態によって処理を分ける

gState変数を元にステージ中、ステージ前などの状態によって、描画や動作などを分けるようにします。

たとえば、描画関数Drawはこんな感じで書きます。

function Draw(){
  switch(gState){
    case IN_STAGE:
      drawInStage();
      break;
    case PRE_STAGE:
      drawPreStage();
      break;
    case GAME_OVER:
      drawGameOver();
      break;
  }
}

ステージ数を表示するシーンである、PRE_STAGE内のmove関数では、
以下のような処理を実装して、一定時間たったら、ゲーム画面に遷移するようにします。

function movePreStage(){
  if(moveStageCnt++ >= PRE_STAGE_CNT){
    // ステージ毎に初期化する
    initStage();
    moveStageCnt = 0;
    gState = IN_STAGE;
  }
}

ステージ表示画面の実装

続いてステージ表示画面の実装をします。

マリオのステージ表示画面では、

1.残機

2.ステージ数

これらを描画する必要があります。

ステージ数の描画では、canvasのfillText関数を使用しました。

マリオの残機を示す×マークは新しくドット絵で書いています。

function drawPreStage(){
  g_Ctx.fillStyle = "#000000";
  g_Ctx.fillRect(0,0,640,480);
  // 小さいマリオを描画
  g_Ctx.drawImage(gMarioTex,0,64,32,32,320 - 32 - 60,240 - 16,32,32);
  // 残機数
  drawCoin(320 + 10 + 60,240 - (17 / 2),gMario.playerNum);
  // multiply
  g_Ctx.drawImage(gMapTex,480,480,32,32,320 - 32,240 - 16,32,32);
  // stage名の描画
  g_Ctx.font = "22px Sans-serif";
  g_Ctx.fillStyle = "#ffffff";
  g_Ctx.textAlign = "center";
  // world
  g_Ctx.fillText("WORLD", 250, 180);
  // world number
  let stageStr = gWorldNumber + " - " + gSubWorldNumber;
  g_Ctx.fillText(stageStr,374,180);
}

詳しくは、各々の標準関数を参照してください。

まとめ

だいたいの実装はこんな感じです。

全てのコードはgithubを参照してください。

感想

one upキノコなど、一度しか出現しないようなオブジェクトは、
個別のフラグで対応しようかと思います。

次は、順番的にいうと、タイトル画面ですが、
ワンクッションおいて、プレイするのはめんどいだろうから、
タイトル画面は最後につくろうかなと。

one up キノコを作ろうかなと思います。

Pocket
LINEで送る

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です