ブラウザゲームを作ってみよう(その4)

#はじめに
[その3](https://qiita.com/noji505/items/44d87dfe4404551b9268)ではキー入力を行いました。
今までは1つのJavascriptファイルに全て記述していたのですが、コード量が増えてくるとソースの可読性が悪くなりメンテナンスが難しくなってくるため用途毎に分割したいと思います。

#ファイル分割イメージ
以下のように分割しました。

| ファイル用途 | フォルダイメージ | ファイル説明 |
|:-:|:-:|---|
| 共通ファイル  | ![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/2d4c51eb-33ac-79a2-469b-4efcc945f6be.png)  | [index.html](#indexhtml)<br>ユーザがアクセスするページファイル<br><br>[main.js](#mainjs)<br>実行するメインファイル |
| ライブラリファイル(commmonフォルダ)  | ![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/d167335f-079f-e511-6018-f4c52524d9fb.png)  | [common/define.js](#commoncanvasjs)<br>キャンバス処理用ファイル<br><br>[common/define.js](#commondefinejs)<br>ライブラリ定数用ファイル<br><br>[common/shape.js](#commonshapejs)<br>図形表示用ファイル<br><br>[common/text.js](#commontextjs)<br>テキスト表示用ファイル |
| ゲームファイル(gameフォルダ)  | ![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/740996/dbd3e95d-1b72-9100-4d5b-945b6f878a29.png)  | [game/define.js](#gamedefinejs)<br>ゲーム用定数ファイル |

それぞれのファイルの中身は以下になります。
#index.html
```html:index.html
<html>
<head>
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'>
<meta http-equiv='Content-Style-Type' content='text/css'>
<meta http-equiv='pragma' content='non-cache'>
<meta http-equiv='cache-control' content='non-cache'>
<meta http-equiv='expires' content='0'>

<!-- 共通ファイル -->
<script src='./common/define.js' type='text/javascript'></script>
<script src='./common/canvas.js' type='text/javascript'></script>
<script src='./common/text.js' type='text/javascript'></script>
<script src='./common/shape.js' type='text/javascript'></script>

<!-- ゲーム固有ファイル -->
<script src='./game/define.js' type='text/javascript'></script>
<script src='./main.js' type='text/javascript'></script>

<title>ファイル分割サンプル</title>
</head>
<body onLoad='init()'>
</body>
</html>
```

#main.js

```javascript:main.js
var timerHandle = 0;

var keyPress = 0;	// キープレス
var keyTrg = 0;		// キートリガー

var mouseX = 0;				// マウスX座標
var mouseY = 0;				// マウスY座標
var mouseButton = false;	// マウスクリック
var mouseTrg = false;		// マウストリガ


var xPos;
var yPos;

function init(){

    window.document.onkeydown = onKeyPress;		// キープレス
    window.document.onkeyup = onKeyUp;          // キーアップ
    window.document.onmousemove = onMouseMove;	// マウス移動
    window.document.onmousedown = onMouseClick;	// マウスクリック
    window.document.onmouseup = onMouseUp;		// マウスリリース

    initCanvas();

    xPos = SW_HALF - 8;
    yPos = SH_HALF + 8;

    timerHandle = setInterval( run, 60 );
}

function run(){
	// 画面クリア
	drawFill( 0, 0, SC_W, SC_H, 0x00FFFF, null );

	if( (keyPress & KEY_UP) != 0 ){
		yPos -= 5;
	}
	if( (keyPress & KEY_DOWN) != 0 ){
		yPos += 5;
	}
	if( (keyPress & KEY_LEFT) != 0 ){
		xPos -= 5;
	}
	if( (keyPress & KEY_RIGHT) != 0 ){
		xPos += 5;
	}

	if( mouseTrg ){
		xPos = mouseX;
		yPos = mouseY;
	}

	// 表示
	drawText( "■", xPos, yPos, '#FF0000', 16, TEXT_NONE );

	drawText( "カーソルキーで動くよ", 0, 0, '#000000', 12, TEXT_NONE );
	drawText( "マウスでクリックすると、その位置に移動するよ", 0, 20, '#000000', 12, TEXT_NONE );


	keyTrg = 0;
	mouseTrg = false;
}

// キープレス
function onKeyPress( e )
{
	var keyCode = null;
	var domObj;

	keyCode = e.which;

	switch( keyCode ){
	case 38:	// 上キー
		if( (keyPress & KEY_UP) != 0 )		break;
		keyPress |= KEY_UP;
		keyTrg |= KEY_UP;
		break;

	case 40:	// 下キー
		if( (keyPress & KEY_DOWN) != 0 )	break;
		keyPress |= KEY_DOWN;
		keyTrg |= KEY_DOWN;
		break;

	case 37:	// 左キー
		if( (keyPress & KEY_LEFT) != 0 )	break;
		keyPress |= KEY_LEFT;
		keyTrg |= KEY_LEFT;
		break;

	case 39:	// 右キー
		if( (keyPress & KEY_RIGHT) != 0 )	break;
		keyPress |= KEY_RIGHT;
		keyTrg |= KEY_RIGHT;
		break;

	case 32:	// スペースキー
		if( (keyPress & KEY_SPACE) != 0 )	break;
		keyPress |= KEY_SPACE;
		keyTrg |= KEY_SPACE;
		break;

	case 13:	// エンターキー
		if( (keyPress & KEY_RETURN) != 0 )	break;
		keyPress |= KEY_RETURN;
		keyTrg |= KEY_RETURN;
		break;
	}
}

// キーアップ
function onKeyUp( e )
{
	var keyCode = null;

	keyCode = e.which;

	switch( keyCode ){
	case 38:	keyPress &= ~KEY_UP;		break;	// 上
	case 40:	keyPress &= ~KEY_DOWN;		break;	// 下
	case 37:	keyPress &= ~KEY_LEFT;		break;	// 左
	case 39:	keyPress &= ~KEY_RIGHT;		break;	// 右
	case 32:	keyPress &= ~KEY_SPACE;		break;	// スペースキー
	case 13:	keyPress &= ~KEY_RETURN;	break;	// エンターキー
	}

}

// マウス移動
function onMouseMove(){
	mouseX = window.event.clientX + document.body.scrollLeft;	// マウスX座標
	mouseY = window.event.clientY + document.body.scrollTop;	// マウスY座標
}

// マウスクリック
function onMouseClick( e )
{
	if( !mouseButton ){
		mouseButton = true;
		mouseTrg = true;
	}
}

// マウスリリース
function onMouseUp( e )
{
	mouseButton = false;
}
```

#common/canvas.js

```javascript:common/canvas.js
//=======================================================================================
//
//  キャンバスファイル
//
//=======================================================================================
var canvas;     // キャンバス
var g;          // グラフィックコンテキスト


//-----------------------------------------------------------
// initCanvas : キャンバス初期化
//       引数 : 無し
//     戻り値 : 無し
//-----------------------------------------------------------
function initCanvas()
{
  var objBody = document.getElementsByTagName( "body" ).item(0);

  canvas = document.createElement( 'canvas' );
  canvas.id = "img_canvas";                 // ID
  canvas.name = "img_canvas";               // 名前
  canvas.width = SC_W;                      // 横幅
  canvas.height = SC_H;                     // 縦幅
  canvas.style.position = 'absolute';       // 配置フラグ
  canvas.style.left = 0;                    // X座標
  canvas.style.top = 0;                     // Y座標

  objBody.appendChild( canvas );            // 追加

  g = canvas.getContext( "2d" );            // グラフィックスコンテキスト設定
}
```

#common/define.js

```javascript:common/define.js
//=======================================================================================
//
//  共通定数ファイル
//
//=======================================================================================
// キー定数
var KEY_UP = 0x00000001;
var KEY_DOWN = 0x00000002;
var KEY_LEFT = 0x00000004;
var KEY_RIGHT = 0x00000008;
var KEY_SPACE = 0x00000010;
var KEY_RETURN = 0x00000020;
```

#common/shape.js

```javascript:common/shape.js
//=======================================================================================
//
//  図形ファイル
//
//=======================================================================================

//-----------------------------------------------------------
// drawFill : 塗りつぶし矩形表示
//     引数 : X座標、Y座標、横幅、縦幅、表示色、透明度
//   戻り値 : 無し
//-----------------------------------------------------------
function drawFill( x, y, w, h, c, alpha )
{
	c = hexToRGB( c );   // 色を16進数へ変換

	if( alpha == null ){  // 透明度なし
		c = "rgb( "+c.r+", "+c.g+", "+c.b+" )";
	} else {				// 透明度あり
		c = "rgba( "+c.r+", "+c.g+", "+c.b+", "+alpha+" )";
	}

	x += SC_X;
	y += SC_Y;

	g.fillStyle = c;           // 色
	g.fillRect( x, y, w, h );  // 描画
}

//-----------------------------------------------------------
// hexToRGB : RGB変換
//     引数 : 色
//   戻り値 : RGB
//-----------------------------------------------------------
function hexToRGB( hex )
{
	var rgb24 = hex;

	if( isNaN(hex) ){	// 数値型ではない
		if( hex.substring(0, 1) == "#" ){	// 先頭が#だったら、取り除く
			hex = hex.substring( 1, hex.length );
		}
		rgb24 = Number( "0x"+hex.toString(16) );	// 16進数へ変換
	}

	var r = rgb24 >> 16;
	var g = (rgb24 ^ (r << 16)) >> 8;
	var b = (rgb24 ^ (r << 16)) ^ (g << 8);
	return { r:r, g:g, b:b };
}
```

#common/text.js

```javascript:common/text.js
//=======================================================================================
//
//  テキストファイル
//
//=======================================================================================
// テキスト表示位置フラグ定数
var TEXT_NONE = 0x0000;
var TEXT_HCENTER = 0x0001;
var TEXT_HRIGHT = 0x0020;
var TEXT_VCENTER = 0x0010;
var TEXT_VBOTTOM = 0x0004;


//-----------------------------------------------------------
// drawText : 文字列描画
//     引数 : 文字,X,Y,色,サイズ,表示位置フラグ
//   戻り値 : 無し
//-----------------------------------------------------------
function drawText( text, x, y, c, size, flg )
{
  var textWidth;            // テキスト横幅
  var textHeight = size;    // テキスト縦幅

  g.font = ""+size+"px ＭＳ ゴシック";  // フォント
  g.fillStyle = c;                      // 文字色

  // 表示位置フラグによって、表示位置を変更
  if( (flg & TEXT_HCENTER) != 0 ){  // 中央
    textWidth = g.measureText( text ).width;    // 横幅取得
    x = SW_HALF - (textWidth >> 1);
  } else if( (flg & TEXT_HRIGHT) != 0 ){  // 右寄せ
    textWidth = g.measureText( text ).width;    // 横幅取得
    x = SC_W - textWidth;
  }

  if( (flg & TEXT_VCENTER) != 0 ){          // 中央
    y = SH_HALF - (textHeight >> 1);
  } else if( (flg & TEXT_VBOTTOM) != 0 ){   // 下寄せ
    y = SC_H - textHeight;
  }

  y += textHeight;  // テキストの高さ分、下にずらす

  g.fillText( text, x, y );  // 表示
}
```

#game/define.js

```javascript:game/define.js
//=======================================================================================
//
//  ゲーム用定数ファイル
//
//=======================================================================================
var SC_X = 0;       // ウィンドウ表示X座標
var SC_Y = 0;       // ウィンドウ表示Y座標
var SC_W = 300;     // ウィンドウの幅
var SC_H = 200;     // ウィンドウの高さ
var SW_HALF = 150;
var SH_HALF = 100;
```

#最後に
ファイルを用途毎に適切に分割して管理するというのはチーム開発を行う場合特に必要になると思います。
そもそも既存のフレームワークを使った場合、規則があるので自動的に分割しないといけないといったこともありますが、
自分で分割して管理出来るようになるとより良いと思います。