マリオクラスを作る(プログラムでマリオを作る)

今回は、マリオをクラス化します。

クラス化をすることで、処理を独立させ、他のソースと干渉させることを
防ぎ、部品として扱えるようにします。

以後、マリオの処理を書く時には、マリオクラスを経由して処理を書いていきます。
今回は、前回書いた移動処理と描画処理をマリオクラスに追加し、
以前書いた処理をマリオクラスのそれと置き換えます。

クラスとは

クラスとは独立した部品のことです。
クラス内には、メンバー変数と呼ばれる変数を定義することができます。

メンバー変数には、マリオを操作する時に使用する変数を定義します。

今回は位置を保持するposX,posYを定義しますが、
今後、使いたい変数が増えたら逐次追加していきます。

メンバー変数を定義する他に、関数も定義することができます。
このような関数をメンバー関数と呼びします。

メンバー関数には、移動する関数であるmoveXなどを定義していきます。

クラスの関数の定義方法

クラスの関数を定義するには、prototypeキーワードを使用します。

draw関数を定義する際には、

Mario.prototype.draw

と書いていますが、このようにprototypeキーワードを使ってclassの関数を定義していきます。

クラス内メンバー変数はthisを使って参照する

クラス内にはメンバー変数を定義することができますが、
メンバー変数であることを示すためにthisキーワードを使用します。

動画解説

今回は、マリオオブジェクトのクラス化をしたいと思います。
クラス化をすることで、処理を独立させ、他のソースと干渉させることを
防ぎ、部品として扱えるようにします。

まず、mario.jsクラスを作成します。

mario.jsを作成する

function Mario(posX,posY){
	// 表示X座標
	this.posX = posX;
	// 表示Y座標
	this.posY = posY;
}

thisをつけることで、クラス内のメンバーであるということを示しています。
クラスのメンバー変数にアクセスする際はthisという接頭語を記述します。

描画関数

/*
	描画関数
	ctx:context
	texture:img class
*/
Mario.prototype.draw = function(ctx,texture){
	ctx.drawImage(texture,0,0,24,24,this.posX,this.posY,24,24);
}

後から変更するかもしれませんが、とりあえずこの形にします。

つづいて、移動関数です。

Mario.prototype.moveX = function(moveX){
	this.posX += moveX;
}

これで、マリオクラスの定義を終えたので、
処理の変更に対応するためにmain.jsの変更をします。

main.jsを変更する

まず、マリオクラスを定義します

var gMario;

マリオクラスを新しく定義したので、ポジションを保持していた変数を削除します。

続いて、
マリオクラスを生成します。

gMario = new Mario(0,64);

newキーワードによってobjectを生成できます

続いて、draw関数を変更します。

gMario.draw(g_Ctx,gMarioTex);

マリオクラスのdraw関数を使います

続いて、move関数を変更します。

function move(){
	// 左キーが押されている状態
	if(gLeftPush){
		gMario.moveX(-4);
	}
	// →キーが押されている状態
	if(gRightPush){
		gMario.moveX(4);
	}
}

move関数を使って左に移動させます。

index.htmlを変更する

最後にhtmlファイルに作成したmairoクラスを読み込ませます。

<script lang="JavaScript" src=mario.js></script>

mairo.jsを読み込ませます。

では実行してみましょう。
マリオクラスに置き換えても正常に実行できました。

今回変更・追加したソースファイル

mairo.js

function Mario(posX,posY){
	this.posX = posX;
	this.posY = posY;
}

/*
	描画関数
	ctx:context
	texture:img class
*/
Mario.prototype.draw = function(ctx,texture){
	ctx.drawImage(texture,0,0,32,32,this.posX,this.posY,32,32);
}

Mario.prototype.moveX = function(moveX){
	this.posX += moveX;
}

main.js

var g_Canvas;
var g_Ctx;
// fps
var g_LastAnimationFrameTime = 0;
var g_LastFpsUpdateTime = 0;
var g_FpsElement;
var gMarioTex;
// key
var gSpacePush = false;	// space
var gLeftPush = false;  // 左
var gRightPush = false;	// 右
var gUpPush = false;	// 上
var gDownPush = false; 	// 下

// keyの定義
var SPACE_KEY = 32;
var LEFT_KEY = 37;
var RIGHT_KEY = 39;
var UP_KEY = 38;
var DOWN_KEY = 40;

// マリオ
var gMario;

/**
	onload
	最初に呼び出される関数
*/
onload = function () {
    // キャンバスに代入
    g_Canvas = document.getElementById('id_canvas');
    g_FpsElement = document.getElementById("fps");
    // cavasに対応していない
    if (!g_Canvas || !g_Canvas.getContext) {
        alert("html5に対応していないので、実行できません");
        return false;
    }

    g_Ctx = g_Canvas.getContext('2d');          // cox
    loadTexture();
    gMario = new Mario(0,64);
    // キーの登録
    window.addEventListener("keydown",keyDown, true);
    window.addEventListener("keyup",keyUp, true);

    requestNextAnimationFrame(animate);		// loopスタート
};

/*
	テクスチャのロード
*/
function loadTexture(){
	gMarioTex = new Image();
	gMarioTex.src = "resource/main.png";
}


function animate(now) {
    // fps
   calculateFps(now);
    move();
    // 描画
    Draw();
    requestNextAnimationFrame(animate);
}

/*
	60fps毎に処理を実行
*/
window.requestNextAnimationFrame =
(function () {
   var originalWebkitRequestAnimationFrame = undefined,
       wrapper = undefined,
       callback = undefined,
       geckoVersion = 0,
       userAgent = navigator.userAgent,
       index = 0,
       self = this;

   // Workaround for Chrome 10 bug where Chrome
   // does not pass the time to the animation function

   if (window.webkitRequestAnimationFrame) {
      // Define the wrapper

      wrapper = function (time) {
        if (time === undefined) {
           time = +new Date();
        }
        self.callback(time);
      };

      // Make the switch

      originalWebkitRequestAnimationFrame = window.webkitRequestAnimationFrame;

      window.webkitRequestAnimationFrame = function (callback, element) {
         self.callback = callback;

         // Browser calls the wrapper and wrapper calls the callback

         originalWebkitRequestAnimationFrame(wrapper, element);
      }
   }

   // Workaround for Gecko 2.0, which has a bug in
   // mozRequestAnimationFrame() that restricts animations
   // to 30-40 fps.

   if (window.mozRequestAnimationFrame) {
      // Check the Gecko version. Gecko is used by browsers
      // other than Firefox. Gecko 2.0 corresponds to
      // Firefox 4.0.

      index = userAgent.indexOf('rv:');

      if (userAgent.indexOf('Gecko') != -1) {
         geckoVersion = userAgent.substr(index + 3, 3);

         if (geckoVersion === '2.0') {
            // Forces the return statement to fall through
            // to the setTimeout() function.

            window.mozRequestAnimationFrame = undefined;
         }
      }
   }

   return window.requestAnimationFrame   ||
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame    ||
      window.oRequestAnimationFrame      ||
      window.msRequestAnimationFrame     ||

      function (callback, element) {
         var start,
             finish;


         window.setTimeout( function () {
            start = +new Date();
            callback(start);
            finish = +new Date();

            self.timeout = 1000 / 60 - (finish - start);

         }, self.timeout);
      };
   }
)
();


function move(){
	// 左キーが押されている状態
	if(gLeftPush){
		gMario.moveX(-4);
	}
	// 右キーが押されている状態
	if(gRightPush){
		gMario.moveX(4);
	}
}

/*
	Draw
	描画
*/
function Draw(){
	g_Ctx.fillStyle = "rgb(255,0,0)";		// 赤に設定
	g_Ctx.fillRect(0,0,640,480);			// 塗りつぶす
	// context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
	gMario.draw(g_Ctx,gMarioTex);
}

/*
	fpsの計算
*/
function calculateFps(now) {
        // 1秒間に何回実行されているか
	var fps = 1000 / (now - g_LastAnimationFrameTime);
	g_LastAnimationFrameTime = now;

        // 1秒経過
	if (now - g_LastFpsUpdateTime > 1000) {
  		g_LastFpsUpdateTime = now;
                // 要素にfps値を代入する
		g_FpsElement.innerHTML = fps.toFixed(0) + ' fps';
	}
}

/*
	キーを押した時の処理
*/
function keyDown(event){
	// どのキーが押されたか
	var code = event.keyCode;
	switch(code){
		case SPACE_KEY:
		// スクロールさせないため
		event.returnValue = false;
		event.preventDefault();
		gSpacePush = true;
		break;
		
		// 左キー
		case LEFT_KEY:
			gLeftPush = true;
			break;

		// 右キー
		case RIGHT_KEY:
			gRightPush = true;
			break;
		// 上キー
		case UP_KEY:
			// スクロールさせないため
			event.returnValue = false;
			event.preventDefault();
			gUpPush = true;
			break;
		// 下キー
		case DOWN_KEY:
			// スクロールさせないため
			event.returnValue = false;
			event.preventDefault();
			gDownKey = true;
			break;

	}

}

/*
	キーを離した時のイベント
*/
function keyUp(event){
	// どのキーが押されたか
	var code = event.keyCode;
	switch(code){
		case SPACE_KEY:
		gSpacePush = false;
		break;
		
		// 左キー
		case LEFT_KEY:
			gLeftPush = false;
			break;

		// 右キー
		case RIGHT_KEY:
			gRightPush = false;
			break;
		// 上キー
		case UP_KEY:
			gUpPush = false;
			break;
		// 下キー
		case DOWN_KEY:
			gDownKey = false;
			break;

	}
}

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="main.css">
<script lang="JavaScript" src=mario.js></script>
<script lang="JavaScript" src=main.js></script>
</head>

<body>
<div id="fps"></div>
<div id="canvas_wrapper">
	<canvas id="id_canvas" width="640" height="480"></canvas>
</div>
</body>
</html>