kintoneカスタマイズで画像を使う方法。そしてマインスイーパ。

[kintone Advent Calendar 2017](https://qiita.com/advent-calendar/2017/kintone) 9日目（土）です。

kintoneプラグインなど作っております。[Spica](https://spica.tokyo)と申します。
ゴリッゴリにカスタマイズする方にはちょっと退屈な話かもしれませんが、週末ですしここらでちょっとお遊び向けの話題でもどうでしょう。

テーマはふたつ。『カスタマイズで画像を使う方法』『マインスイーパ』です。
画像の使い方を説明するだけではつまらないので、せっかくだから<font color="Silver">~~俺はこの赤の扉を選ぶぜ~~</font>ゲームを作ってみます。

ご存知の通りマインスイーパは[世界ランキング](http://www.minesweeper.info/worldranking.html)が盛りあがりつづけている大人気パズルゲームです。
最近は中国勢の追い上げがすごい。うおおお。

このゲームを作るのに必要な画像素材は主に11種類。
”１～８の数字”、”爆弾”、”旗”、”はてなマーク”です。
数字はフォントでもいいですが、せっかくなので画像で用意するという前提で、書いていきます。

ちなみにサンプルでこんな感じのkintoneアプリも作ってみました。
![ms_ss1.png](https://qiita-image-store.s3.amazonaws.com/0/67365/3b721822-410e-d977-021e-3b5740df7579.png)

#画像の使い方

kintoneのカスタマイズ方法といえば、アプリへ個別にスクリプトをアップロードする方法とプラグインを適用する方法がありますが、このどちらも画像ファイルを直接使うことはできません。

使うためには、基本はCSSで外部ファイルを指定してやります。
たとえばこんな感じです。

```css
p.splite-bomb {
	background: url( 画像ファイルのURL ) no-repeat;
	width: 20px;
	height: 20px;
	background-size: 20px 20px;
}
```

p要素にsplite-bombクラスを指定してやると、背景にURLから読み込まれた画像が表示されるというすんぽう。

widthとheightは表示サイズを指定してやります。
background-sizeは画像の縮尺を指定します。

でも「スタイルシートで使う程度の画像ファイルごときを外部に用意したくないッス」
というのが正直なところ。アプリ単体、プラグイン単体で画像を使うことはできないものか。

できます。BLOBを指定してやるんです。

BLOBというのは元々はデータベースでバイナリデータを格納するために使われるデータ形式で、ひらたく言えば画像データなんかを文字列データに置き換えたものです。

じゃあどうやって画像データを文字列に置き換えるのか。ここ説明しはじめると<font color="Silver">~~間に合わない~~</font>激しく脱線するので、デモを用意しました。
なんという3分クッキング状態。

[画像ファイルのBLOB変換フォーム](https://spica-git.github.io/gh-projects/demo/img2blob.html)

このフォームで画像ファイルを読み込むと、変換された文字列がゴンヌズバーと出力されます。
コピーして「画像ファイルのURL」のところへ、えぐりこむようにペーストすべし。

##ちょっと応用した画像の使い方

たとえばこんな画像があります。
![number.png](https://qiita-image-store.s3.amazonaws.com/0/67365/1f3cda9d-89e9-1cd5-df16-80c8c51c8bd9.png)

20px * 20px の数字の画像が0～9までくっついて、200px * 20px のひとつの画像になっています。
使いたい数字の部分以外をマスクする感じで使います。

```css
p.splite-number {
	background: url( 数字ファイルのURLまたはBLOB ) no-repeat;
	width: 20px;
	height: 20px;
	background-size: 200px 20px;
}

p.one {
	background-position-x: -20px;
}
p.two {
	background-position-x: -40px;
}
```

p.splite-number
は前項とほぼ同じですが、widthとheightが変わらず20pxなのに対し、画像サイズが 200px * 20px と大きくなっています。はみ出た部分は表示されません。
そしてこのままだと左上を基準点にした範囲の 0 が表示されます。

p要素のclassに "one" を追加してやると、背景画像のx軸表示位置が-20pxずれるので、画像の 1 が表示される。
p要素のclassに "two" を追加してやると、背景画像のx軸表示位置が-40pxずれるので、画像の 2 が表示される。
という感じです。

```html
１を表示
<p class="splite-number one"></p>
２を表示
<p class="splite-number two"></p>
```

同期速度が大事なゲーム開発なんかでは、メモリ上にテクスチャやらドットやらのアニメーションに必要なだけ画像を読み込んでおいて、描画する画像をVRAM上の座標を変えていくことで、パラパラアニメさせるというおなじみの方法ですね。

classを作らずに表示内容を変えたい場合は、background-position-x の値を直接いれてやります。
jQueryを使った例で、たとえばこんな感じに書きます。

```javascript
$(要素のセレクタ).css(
	"background-position-x", 画像の表示開始x座標 + "px"
);
```

#マインスイーパを作ってみる

さて、以上をふまえてkintoneアプリ上にマインスイーパを作ってみます。
画像を使う方法さえおさえてしまえば、あとはjavascriptで作るだけです。かんたんかんたん。

用意する<font color="Silver">~~食材~~</font>オブジェクト。

●基礎パラメータ、ゲーム全体を管理するオブジェクト（function）
　地雷原の横幅、縦幅、爆弾を設置する割合、などを管理。
　ユーザーの操作イベントのハンドラもこのfunctionのprototypeに実装。

●クリックできるセルひとつぶんを管理するオブジェクト（function）
　主に爆弾かそうでないか、クリック済みかそうでないか、などを管理。
　セルを掘った時の処理をこのfunctionのprototypeに実装。

表示はtable要素を使います。tdがクリックできるセルひとつぶん。
基礎パラメータからテーブルを作成して、爆弾をランダムに配置したら見た目を整えて地雷原の出来上がりです。

クリック時の操作は、爆弾セルを押したらゲームオーバー。
爆弾じゃなければ、周囲に配置された爆弾の数を表示。
周囲に爆弾が配置されてなければ、周囲のセルを再帰して開く。

基本の部分は正直これだけで、その他は地雷を踏んだ時やクリアしたときなんかのチェックと、プレイ中の情報を表示したり、難易度などを設定するUIなんかの周辺機能といったこまごまとした部分を作ったら完成。

というわけで、マインスイーパ自体のデモページとソースはこちら。
[マインスイーパ デモ](https://spica-git.github.io/gh-projects/demo/minesweeper.html)
[マインスイーパのjavascript](https://spica-git.github.io/gh-projects/demo/minesweeper/ms.js)
[マインスイーパのcss](https://spica-git.github.io/gh-projects/demo/minesweeper/ms.css)

セルのサイズをいつでも変更できるようにしてみたんですが、そのときに呼ばれる関数を抜粋。
画像の拡縮と表示位置の調整をしています。

```javascript
//MineCellはセルを管理するfunction（コンストラクタ）
MineCell.prototype.ViewResize = function (){
	var cellsize = this.Field.CellSize; //セルの大きさ

	//this.$eはtd要素
	this.$e.css({
		"min-width": cellsize + "px",
		"width": cellsize + "px",
		"height": cellsize + "px"
	});

	//セルに周囲の爆弾数が表示されている状態の場合
	if(this.open && !this.bomb && this.arround_bombs > 0){
		// this.$e.find('p') は画像を表示している要素
		this.$e.find('p').css({
			"width": cellsize + "px",
			"height": cellsize + "px",

			//  200px * 20px の画像をセルの大きさに合わせたサイズに拡縮
			"background-size": (cellsize * 10) + "px " + cellsize + "px",

			//  表示したい数字になるようbackground-position-xを指定
			//  this.arround_bombs は周囲8マスに配置されている爆弾の総数
			"background-position-x": -(this.arround_bombs * cellsize) + "px"
		});
	}
	//
	else{
		this.$e.find('p').css({
			"width": cellsize + "px",
			"height": cellsize + "px",
			"background-position-x": "0px",
			"background-size": cellsize + "px " + cellsize + "px"
		});
	}
};
```

##kintoneアプリにインポートして使ってみる

![ms_import.png](https://qiita-image-store.s3.amazonaws.com/0/67365/497b7a9f-8039-7ba5-d715-7162b8bbad77.png)

スクリプトとCSSをアプリにインポートしてやります。
[kintone_events.js](https://spica-git.github.io/gh-projects/demo/minesweeper/kintone_events.js)の中身はいつものやつです。

```javascript:いつものやつ
(function($){
"use strict";

//必須のひとたち
if($ == null || window.MineSweeper == null){ return; }

//レコード詳細ページの表示イベント
kintone.events.on([
		"app.record.detail.show"
	],
	function(event){

		//マインスイーパ本体のオブジェクトを作る。
		//戻り値はjQueryオブジェクトで、これを配置したいところに設置する。
		var $field = new MineSweeper();

		//ヘッダーメニュースペースにマインスイーパを設置するやりかた
		var $space = $(kintone.app.record.getHeaderMenuSpaceElement());

		//スペースにそのままappendしてもいいけどセンタリングしたり、
		//マインスイーパが複数設置されたりするのを回避するため、divのなかに入れます。
		var $div = $space.find(".mine-container");
		if($div.length === 0){
			$div = $('<div class="mine-container">').css("text-align", "center");
			$space.append($div);
		}
		$div.empty();
		$div.append( $field );

		return event;
	}
);

})(jQuery);
```

###kintoneアプリ化してみました
ランキング機能とか、クリアデータの削除制限とかをkintoneで実装したアプリです。

[![minesweeper.png](https://qiita-image-store.s3.amazonaws.com/0/67365/9de5c788-2903-c819-706d-b3288c017281.png)kintoneアプリ- マインスイーパ](https://spica-git.github.io/gh-projects/demo/minesweeper/kintonems.zip)

今回マインスイーパを作るにあたり、みんな大好きな[いらすとや](http://www.irasutoya.com/)さんの素材を使用させていただきました。
数字だけ自作です。

Spicaは[『機能拡張スタンダード All-In』](https://spica.tokyo/bluelaunch)というプラグインを販売しております。
よければこちらも見たってつかあさい。

そんな感じでーす。