マリオの1up処理の実装

マリオの1upキノコ処理の実装(プログラミングでマリオを作る第45回)

今回は簡単だろうことから、即実装した1upキノコの実装を行いたいと思います。

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

マリオ1up処理の実装

1upキノコ用の絵とオブジェクトを作成し、マリオを1upさせます。

キノコオブジェクトはすでに作っているので、キノコを取った後の効果さえ別に書けばすぐに終わります。

あとは、キノコを取得した際の演出作ります。

これには、背景が白くて見えませんが、右端に1up演出用の画像を描いたので、
それを使います。

1upキノコを追加したマップチップ

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

今回実装する内容

1.1upキノコを描く

2.1upキノコオブジェクトを作成する

3.1upキノコを取ったことがわかる演出をする

では、実装していきます。

const.js

// chapter45
let MAX_PLAYER_NUM = 99;
let ONE_UP_CNT = 80;

最大残機と1up演出の最大カウントを定義します。

mario.js

メンバー変数に1up用の変数を追加します。

// chapter45
this.oneUpKinoko = new OneUpKinoko(0,0,LEFT_DIR);
// one upを表示するタイマー
this.oneUpCnt = 0;
this.isDrawOneUp = false;

次に、1up取得後の関数ですが、実装内容にあるように、
1upの画像を演出期間に上に上昇させます。

特に難しいところはないので、関数だけ載せます。

/**
 * chapter45
 * 
 * OneUpKinokoを取得した時
 */
Mario.prototype.getOneUpKinoko = function(){
	if(this.playerNum < MAX_PLAYER_NUM){
		this.playerNum++;
		this.isDrawOneUp = true;
	}
}

/**
 * one upの表示関数
 */
Mario.prototype.oneUpAction = function(){
	if(this.isDrawOneUp){
		if(this.oneUpCnt++ >= ONE_UP_CNT){
			this.oneUpCnt = 0;
			this.isDrawOneUp = false;
		}
	}
}

/**
 * oneupの描画
 */
Mario.prototype.drawOneUp = function(ctx,texture){
	if(this.isDrawOneUp){
		ctx.drawImage(texture,416,480,64,32,this.posX - 16,this.posY - (this.oneUpCnt / 4) - 32,64,32);
	}
}

続いて、頭上判定したマップチップが1up用のマップチップだった場合に
1upキノコを出現させる処理を書きます。

ここも、キノコの時と変わりません。

// one up blockだった場合
if(isOneUpBlock(map[this.upMapY][mapsX[i]])){
	let posX = mapsX[i] * MAP_SIZE;
	let posY = this.upMapY * MAP_SIZE;
	this.oneUpKinoko.activate(posX,posY,LEFT_DIR);
	// ボックスを空にする
	replaceEmptyBoxMap(map,mapsX[i],this.upMapY);
}

続いて、1upキノコ用のクラスを書きます。

挙動はキノコクラスと全くかわらないので、そのままキノコクラスをコピーします。

oneUpKinoko.js

function OneUpKinoko(posX,posY,dir){
	this.posX = posX;
	this.posY = posY;
	this.addPosX = 0;
	this.addPosY = 0;
	this.direction = dir;
	// マップチップ座標
	this.rightMapX = 0;
	this.leftMapX = 0;
	this.upMapY = 0;
	this.downMapY = 0;
	this.height = 32;
	this.state = INACTIVE;
	// chapter37
	// ブロックからの出現アニメーションフラグ
	this.isFirstAnimation = true;
	this.offsetY = 0;
}

/*
	描画関数
	ctx:context
	texture:img class
	scrollX:X軸のスクロール量
*/
OneUpKinoko.prototype.draw = function(ctx,texture,scrollX){
	if(this.state != INACTIVE){
		ctx.drawImage(texture,64,480,32,this.offsetY,this.posX - scrollX,this.posY,32,this.offsetY);
	}
}

/*
	動かす役割

	moveNum:移動量
*/
OneUpKinoko.prototype.move = function(mapChip,moveNum){
	this.updateMapPosition();
	// 向きにより加算量を調整する
	moveNum = this.direction == LEFT_DIR ? -moveNum : moveNum;
	// 加算量を代入する
	this.addPosX = moveNum;
	// マップチップとの当たり判定
	this.collisionX(mapChip,this.posX + this.addPosX);
	this.posX += this.addPosX;
	// 移動したのでマップ座標更新
	this.updateMapPositionX(this.posX);
}

/**
  重力動作
  mapChip:対象のマップチップ配列
*/
OneUpKinoko.prototype.gravityAction = function(mapChip){
  // 重力を加算
  this.addPosY += GRAVITY_POWER;
  // 落下量調整
  if(this.addPosY >= MAX_GRAVITY){
    this.addPosY = MAX_GRAVITY;
  }
  // Y軸方向の当たり判定(地面に接触している場合は、addPosYは0になる)
  this.collisionY(mapChip,this.posY + this.addPosY);
  this.posY += this.addPosY;
}

/**
	x軸方向のマップチップ座標の更新

	posX : マップチップ更新対象となるx座標
*/
OneUpKinoko.prototype.updateMapPositionX = function(posX){
	// x座標
	this.leftMapX = Math.floor(posX / MAP_SIZE);
	this.rightMapX = Math.floor((posX + MAP_SIZE - 1) / MAP_SIZE);

	// 配列外チェック
	if(this.leftMapX >= MAX_MAP_CHIP_X){
		this.leftMapX = MAX_MAP_CHIP_X - 1;
	}
	if(this.leftMapX < 0){
		this.leftMapX = 0;
	}
	if(this.rightMapX >= MAX_MAP_CHIP_X){
		this.rightMapX = MAX_MAP_CHIP_X - 1;
	}
	if(this.rightMapX < 0){
		this.rightMapX = 0;
	}
}

/**
	Y軸方向のマップチップの更新
*/
OneUpKinoko.prototype.updateMapPositionY = function(posY){
	// y
	this.upMapY = Math.floor(posY / MAP_SIZE);
	this.downMapY = Math.floor((posY + MAP_SIZE - 1) / MAP_SIZE);

	// 配列外チェック
	if(this.upMapY >= MAX_MAP_Y - 1){
		this.upMapY = MAX_MAP_Y - 1;
	}
	if(this.upMapY < 0){
		this.upMapY = 0;
	}
	if(this.downMapY >= MAX_MAP_Y - 1){
		this.downMapY = MAX_MAP_Y - 1;
	}
	if(this.downMapY < 0){
		this.downMapY = 0;
	}
}

/**
	マップチップ座標を更新する
*/
OneUpKinoko.prototype.updateMapPosition = function(){
	this.updateMapPositionX(this.posX);
	this.updateMapPositionY(this.posY);
}

/**
	オブジェクトとの当たり判定X
*/
OneUpKinoko.prototype.collisionX = function(map,posX){
	this.updateMapPositionX(posX);
	// キノコの右側
	if(isObjectMap(map[this.downMapY][this.rightMapX]) || isObjectMap(map[this.upMapY][this.rightMapX])){
		// (加算される前の)中心点からの距離を取る
		var vecX = Math.abs((this.posX + HALF_MAP_SIZE) - ((this.rightMapX * MAP_SIZE) + HALF_MAP_SIZE));
		this.addPosX = Math.abs(MAP_SIZE - vecX);
		this.direction = LEFT_DIR;
	}
	// キノコの左側
	else if(isObjectMap(map[this.downMapY][this.leftMapX]) || isObjectMap(map[this.upMapY][this.leftMapX])){
		// (加算される前の)中心点からの距離を取る
		var vecX = Math.abs((this.posX + HALF_MAP_SIZE) - ((this.leftMapX * MAP_SIZE) + HALF_MAP_SIZE));
		this.addPosX = -Math.abs(MAP_SIZE - vecX);
		this.direction = RIGHT_DIR;
	}
}

/**
	オブジェクトとの当たり判定Y
*/
OneUpKinoko.prototype.collisionY = function(map,posY){
	this.updateMapPositionY(posY);

	// キャラの下側と接触した場合
	if(isObjectMap(map[this.downMapY][this.rightMapX]) || isObjectMap(map[this.downMapY][this.leftMapX])){
		// (加算される前の)中心点からの距離を見る
		var vecY = Math.abs((this.posY + HALF_MAP_SIZE) - ((this.downMapY * MAP_SIZE) + HALF_MAP_SIZE));
		// Yの加算量調整
		this.addPosY = Math.abs(MAP_SIZE - vecY);
		// 地面についた
		this.posY += this.addPosY;
		this.addPosY = 0;
	}
}

/*
  マリオとの当たり判定

  map:マップチップ配列
  mario:Marioクラス
*/
OneUpKinoko.prototype.collisionWithMario = function(map,mario){
  if(!mario.isDead()){
    // x軸
    if(mario.moveNumX < this.posX + 32 && mario.moveNumX + 32 > this.posX)
    {
      // マリオの上とキノコの下(キノコは32*32で切り取られているので、最下部は32+される)
      if(mario.posY < this.posY + 32){
        // マリオの下とキノコの上
        if(mario.posY + mario.height > this.posY + (32 - this.height)){
          // マリオを大きくする処理を書く
          this.state = INACTIVE;
          // マリオone up
          mario.getOneUpKinoko();
        }
      }
    }
  }
}

/*
	chapter27
	キノコの更新処理
*/
OneUpKinoko.prototype.update = function(map,mario){
	if(this.state != INACTIVE){
		// chapter37初回出現アニメーション
		if(this.isFirstAnimation){
			this.appearingAnimation();
		}
		else{
			this.move(map,2);
			this.gravityAction(map);
		}
		this.collisionWithMario(map,mario);
	}
}

/**
  キノコを出現させる処理

  posX:出現X座標
  posY:出現Y座標
*/
OneUpKinoko.prototype.activate = function(posX,posY,dir){
  this.posX = posX;
  this.posY = posY;
  this.state = NORMAL_STATE;
  this.dir = dir;
  this.isFirstAnimation = true;
  this.offsetY = 0;
}

/**
 * きのこの出現時のアニメーションを行う
 * 
*/
OneUpKinoko.prototype.appearingAnimation = function(){
	this.posY -= 1;
	this.offsetY += 1;
	if(this.offsetY == 32){
		this.isFirstAnimation = false;
	} 
}

/**
 * chapter37
 * ブロックの上にのっていた時にきのこを上昇させる処理 
 * 
 * blockPosX : ブロックのX座標
 * blockPosY : ブロックのY座標
*/
OneUpKinoko.prototype.blockUpAction = function(blockPosX,blockPosY){
	// キノコが上にあった場合キノコを上昇させる
	if(this.state == NORMAL_STATE){				
		// Y座標チェック
		if(blockPosY == this.posY + MAP_SIZE){
			// x座標チェック
			if(blockPosX < this.posX + MAP_SIZE  && blockPosX + MAP_SIZE > this.posX){
				this.addPosY = BLOCK_UP_ADD_Y;
			}
		}
	}		
}

oneUpKinoko.jsを読み込むことを忘れないでください。

あとは、main.jsにて、1upキノコの描画と更新関数を呼べば完成です。

まとめ

基本的にいままでやってきたことと変わらないので、
大丈夫でしょうか。

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

感想

1upキノコを取得する時の演出がめんどくさいなぁと思ったけど、
適当なものでごまかしました。

次は、隠しブロックの実装か、キノコ状態から敵に当たった時に小さくする
処理を実装してなかったので、その実装を行いたいと思います。