一週間も空かずに今回は、コンティニュー処理の実装をします。
実行結果は以下のようになります。
マリオがやられた時に、残機が一つ減って、ステージ表示画面に入り、
残機が残っている場合は、そのステージから再開させる処理を書きます。
同時に残機が亡くなったときのゲームオーバー画面も作ります。
その場合タイトル画面に戻りますが、その実装はまた今度やります。
今回実装する内容
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 キノコを作ろうかなと思います。