マリオのでかい状態からチビマリオへの実装

マリオの大きくなった時から小さくなる時の当たり判定処理(プログラミングでマリオを作る第48回)

今回は、マリオが大きい時から、小さくなった時への当たり判定処理の実装を行いたいと思います。

実行結果をみてみます。

マリオキノコ状態から小さくなるときの当たり判定処理

以前はキノコ状態からでも、敵に当たった場合はそのままgameoverになっていましたが、マリオが小さくなり、少しの間無敵判定を取れるように実装しています。

では、今回の実装内容を確認します。

今回実装する内容

1.でかい状態のマリオが敵と当たり判定を起こした時に、マリオを小さくする

2.小さい → でかい → 小さい →というアニメーションを作る

3.アニメーション中は、マリオの動きを止めて、無敵判定にする

4.少しの間無敵判定を継続する

確か初期のバージョンだと、マリオが当たり判定を起こした時は、全体がストップしていたように思いますが、カットしました。

では、実装に移ります。

実装

const.js

まず、無敵時間を定数として、定義します。

// chapter48
let INVINCIBLE_TIME = 120;

今後全体に実装していくことを考えて、動きを止める処理を管理するフラグを
global.jsに定義します。

global.js

// chapter48
// 時間を止めるフラグ
let gActionStop = false;

続いて、マリオの処理を書きます。

mario.js

まず、無敵処理用とアニメーション用のメンバー変数を定義します。

// chapter48
// 無敵フラグ
this.isInvincible = false;
this.invincibleCnt = 0;
// 小さくなるアニメーションフラグ
this.onSmallAnimation = false;
this.smallAnimationCnt = 0;

続いて、敵と当たり判定を起こした時に呼んでいた関数collisionWithEnemy関数を修正し、でかい状態ならば小さい状態にし、耐えられるようにします。

Mario.prototype.collisionWithEnemy = function(){
	// 無敵状態の時は実行しない
	if(!this.isInvincible){
		// でかい状態なら一回もつ
		if(this.isBig()){
			// 小さくする
			this.height = MAP_SIZE;
			this.textureHeight = MAP_SIZE;
			this.textureOffsetY = 0;
			this.posY += MAP_SIZE;
			this.updateMapPositionY(this.posY);
			this.state = NORMAL_STATE;
			this.isInvincible = true;
			this.onSmallAnimation = true;
			this.keyDisable = true;
			// 全体の動きを止めるフラグ
			gActionStop = true;
		}
		else{
			this.setDeadParam();
		}
	}
}

一度小さくしてマップチップ座標を更新して、小さくなる際のアニメーションフラグを立てます。

キー入力を無効にして、グローバルで定義した動きを止めるフラグをonにします。

gActionStopフラグが立っている時は、マリオの重力をオフにします。

// ジャンプ処理
if(!this.isMapMove && !gActionStop){
	this.jumpAction(gUpPush,mapChip);
}

続いて、無敵時間処理関数と当たり判定時のアニメーション関数
update関数の中で呼びます。

// star処理
this.starAction();
// 無敵処理
this.invincibleAction();
// 小さくする処理
this.smallAnimationAction();

無敵処理用の関数を書きます。

starの処理と同じように通常画像と明るい画像を交互に描画することによって、
チカチカさせているように見せます。

確かこんな感じだったはず。

/**
 * chapter48
 * 
 * 無敵アニメーション処理
 */
Mario.prototype.invincibleAction = function(){
	// 小さくなるアニメーションが終わった後に発動する
	if(this.isInvincible && !this.onSmallAnimation){
		if(this.invincibleCnt++ >= INVINCIBLE_TIME){
			this.isInvincible = false;
			this.invincibleCnt = 0;
			this.starOffsetX = 0;
		}
		// アニメーションさせる
		else if(this.invincibleCnt % 4 == 0){
			this.starOffsetX = this.starOffsetX == 0 ? 256 : 0;
		}
	}
}

続いて、小さくなったり大きくなったりするアニメーション処理を書きます。

/**
 * chapter48
 * 
 * 小さくなる際の処理
 * 小さくなったり大きくなったり
 */
Mario.prototype.smallAnimationAction = function(){
	if(this.onSmallAnimation){
		this.smallAnimationCnt++;
		if(this.smallAnimationCnt % 5 == 0){
			if(this.height == MAP_SIZE){
				// デカくする
				this.height = MAP_SIZE * 2;
				this.posY -= MAP_SIZE;
				this.textureOffsetY = 128;
				this.textureHeight = MAP_SIZE * 2;	
			}
			else{
				// 小さくする
				this.height = MAP_SIZE;
				this.textureHeight = MAP_SIZE;
				this.textureOffsetY = 0;
				this.posY += MAP_SIZE;
			}
		}
		if(this.smallAnimationCnt >= 20){
			// 小さくするアニメーション終了
			this.onSmallAnimation = false;
			gActionStop = false;
			this.keyDisable = false;
			this.smallAnimationCnt = 0;
		}
	}
}

20フレーム経過後にアニメーションを終了して、移動可能な無敵処理に遷移しています。

敵との当たり判定は、collisionWithEnemy関数を呼んでいるので、
これで実装は終了です。

ブロックの破壊フラグが立っていたままになっていたバグの修正

実行中ブロック破壊処理のところにバグを発見したので、
修正します。

/**
 * chapter37
 * ブロックのアニメーション処理
 */
Mario.prototype.animateBlock = function(map){
	// ブロックの数分
	for(var i = 0;i < MAX_MAP_BLOCK;++i){
		// ブロック破壊フラグ
		if(this.isBlockAttack[i]){
			// 上昇させる
			for(var j = 0;j < 4;++j){
				this.blockAttackY[i][j] -= this.blockAttackAddY[i];
			}
			this.blockAttackAddY[i] -= 1;
			// 4つのブロックのアニメーション
			this.blockAttackX[i][0] -= 4;
			this.blockAttackX[i][1] = this.blockAttackX[i][0];
			this.blockAttackX[i][2] += 4;
			this.blockAttackX[i][3] = this.blockAttackX[i][2];
			// (*範囲外処理が走っていなかった)ブロックが画面外に出たらアニメーションを停止する
			if(this.blockAttackY[i][3] >= 512){
				this.isBlockAttack[i] = false;
			}
		}
		// ブロック上昇処理
		else if(this.isBlockUp[i]){			
			this.blockAttackAddY[i] -= 1;
			// 上下運動が終わった場合
			if(this.blockAttackAddY[i] == 0){
				this.isBlockUp[i] = false;
			}
		}
	}
}

ブロック破壊した時の範囲外判定が、間違っており、
フラグがオフになっていなかったので、修正しました。

今回は、以上になります。

まとめ

実装自体はめんどくさいですが、やっていることは、今までやったことと変わらないので、難しくはないはずです。

時間を止める際のフラグ処理は、今後のことも考えて、global定義としました。

クリボなどの敵キャラの動きを止めたい場合は、このフラグを元に管理すればいいですね。

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

感想

今回は、めんどくさくてやってなかった、やられ判定を実装した。

次は、隠しブロック1upキノコの処理の実装をしようかなと思ってます。

あとは、ステージクリア処理が残ってるなぁー。

4,5年やっているのに、まだまだ終わらない。

終わらせないといけないという、よくわからない使命感だけがモチベーション。

苦しい。