この投稿は、kintone Advent Calendar 2015 12月5日担当分として、はじめてQiitaで投稿してみました。
##なぜおもしろアプリなのか
kintoneを職場で実際に使い始めることを想像してみましょう。導入の担当者は、おそらくパワーユーザーだったり業務の改善に熱心に取り組んでいる人でしょう。
しかし、それ以外の職場の人の中には、「新しいなにか」に対して漠然とした不安を抱いたり、拒否反応を示す場合もあるかもしれません。表面的にはマニュアル通りに操作してくれるかもしれませんが、そういう姿勢ではkintoneのよさを職場全体で生かすことは難しいかもしれません。
そこでお薦めなのが本来の業務アプリの他に おもしろアプリ を用意しておくことです。
###おもしろアプリの一例
- あなたのおすすめグルメ情報アプリ
- 身近に起きた面白い話アプリ
- 現場あるあるアプリ
- 社長さんに直訴アプリ
このような仕掛けを用意してあげることで、自分から積極的にアプリに登録したくなったり、身近な内容でアプリに興味を持って貰うことができます。
###おもしろいといえばゲームです
おもしろい、愉しいといえば何と言ってもゲームです。うん、間違いない。
といって業務に差し支えるほどハードな内容もどうかと思いますので、簡単なミニゲームをkintoneアプリとして作ってみます。ゲームといってもいろんなタイプがありますが、今回はシューティングゲームを選んでいます。ゲームとしての出来にはいささか問題がありますが、kintoneの中でもこんなことができるという一例として笑って読み進めて下さい。
デザイン
はじめにゲームの見た目、プレイ方法について簡単にまとめておきます。
- 自キャラをカーソルキーで操作する
- スペースキーで弾を発射
- 敵キャラは単純に上下運動をしながら弾を発射
- 弾があたったらそれぞれダメージをうける
- 一定のダメージを受けたら負け
- 敵に一定のダメージを与えたら勝ち
- 勝つか負けるかでゲーム終了
単純ですね。見た目はこんな感じになります。
###実装の方針
シューティングゲームのようなインタラクティブな操作をkintoneに実装しようとすると 残念ながら JavaScriptの力にすがるのが早そうです。というか、それしか方法がなさそう。
JavaScriptでゲームを作るときにポピュラーなのが、 enchant.js です。今回はこれを使っていきます。それ、そもそも、kintoneじゃなくてもいいじゃんという声も聞こえてきそうですが、この方針で行きます。
kintoneのアプリは次のような内容にしておけばよさそうです。
- カスタマイズ一覧を用意し、enchant用のDiv要素を定義しておく
- JavaScript/CSSの設定で、enchant.jsとゲーム用のJSファイルを登録する
- 最低限必要なCSSも作成して登録する
それから、JSEdit for kintoneを使えるようにしておくと何かと便利です。
画像をどこに置くか問題
ゲーム用の画像データをどこに配置しておくかをあらかじめ検討して準備しておく必要があります。cybozu.comクラウドにストレージサービスがあれば、話は簡単なのですが、現状としてはAWSのS3や、AzureのWeb Appsあたりを使うのがてっとりばやいです。今回は、最近AWSネタが続いているので、対抗してAzureの方に置いてみました。Azureのポータルが新しくなっててちょっとはまったのは内緒です(^_^;)
今回は、https://kintone-nyartype.azurewebsites.net/images/ に配置しておきます。
画像データはうちの絵師さん作です。
それこそkintoneのアプリに添付ファイルとして入ってる物を使うのが理想ですが、それは先の課題としておきましょう。
##Let's kintone cooking
それでは、実際に実装してみましょう。そもそも、kintoneの環境でenchant.jsが綺麗に動くのかどうかやってみないとわかりませんので、最低限のゲームのコードで試してみることにします。
ゲーム実装部分のJavaScript
まず肝心のJSの部分です。
/*
kintone カスタマイズビュー用お手軽ゲームサンプル用JS
*/
(function () {
"use strict";
var images_url = 'https://kintone-nyartype.azurewebsites.net/images/';
function go_play(e) {
enchant(); // initialize
var game = new Core(800, 600); // game stage
game.preload(images_url + 'kintoun7.png');
game.preload(images_url + 'kintoun8.png');
game.preload(images_url + 'kumo.png'); //
game.preload(images_url + 'doukumo.png');
game.preload(images_url + 'arrow1.png');
//game.preload(images_url + 'pyo1.mp3');
//スピード設定用パラメーター
game.fps = 20;
var kumo_speed = 50;
var neko_speed = 10;
var teki_speed = 20;
var arrow_speed = 30;
game.rootScene.backgroundColor="#a0d8ef";
game.onload = function(){
var yoro1 = new Sprite(312, 282);yoro1.scale(0.5, 0.5);
yoro1.image = game.assets[images_url+'kintoun7.png'];
yoro1.x = 20; yoro1.y = 220;
var yoro2 = new Sprite(312, 282);yoro2.scale(0.5, 0.5);
yoro2.image = game.assets[images_url+'kintoun8.png'];
yoro2.x = 20;yoro2.y = 220;
var kumo = new Sprite(122, 46);kumo.scale(0.7, 0.7);
kumo.image = game.assets[images_url+'kumo.png'];
kumo.visible = false;
var teki1 = new Sprite(176, 67);teki1.scale(1, 1);
teki1.image = game.assets[images_url+'doukumo.png'];
teki1.x = 600; teki1.y = 300;
var arrow1 = new Sprite(167, 52);arrow1.scale(0.5, 0.5);
arrow1.image = game.assets[images_url+'arrow1.png'];
arrow1.x = 600; teki1.y = 300;
game.keybind(32, 'space'); //スペースキーをバインド
kumo.addEventListener('enterframe', function() {
kumo.x += kumo_speed;
if(kumo.x > 800) kumo.visible = false;
});
teki1.addEventListener('enterframe', function() {
teki1.y += teki_speed;
if(teki1.y > 500 || teki1.y < 10) teki_speed *= -1;
});
arrow1.addEventListener('enterframe', function() {
arrow1.x -= arrow_speed;
if(arrow1.x < 0) {
arrow1.x = teki1.x;arrow1.y = teki1.y;
}
});
game.rootScene.addChild(yoro1);
game.rootScene.addChild(kumo);
game.rootScene.addChild(teki1);
game.rootScene.addChild(arrow1);
//矢印キーでメインキャラ移動
game.rootScene.addEventListener('enterframe', function() {
if (game.input.up) {
yoro1.y-= neko_speed;yoro2.y = yoro1.y;
}
if (game.input.down) {
yoro1.y+=neko_speed;yoro2.y = yoro1.y;
}
if (game.input.right) {
yoro1.x+=neko_speed;yoro2.x=yoro1.x;
}
if (game.input.left) {
yoro1.x-=neko_speed;yoro2.x=yoro1.x;
}
});
//スペースキーで雲発射
game.addEventListener('spacebuttondown', function(){
game.rootScene.removeChild(yoro1);
game.rootScene.addChild(yoro2);
kumo.x = yoro1.x + 200;kumo.y = yoro1.y+150;
kumo.visible = true;
});
game.addEventListener('spacebuttonup', function(){
game.rootScene.removeChild(yoro2);
game.rootScene.addChild(yoro1);
});
};
game.start(); // start your game!
}
kintone.events.on('app.record.index.show', go_play);
})();
我ながらあんまり綺麗なコードではありませんが、ご容赦下さい。
ほとんどがゲームのコードで、最後の kintone.events.on で一覧が表示されたときにコードが実行されるようにしていますね。
また、この段階ではあたり判定や終了判定はまだ実装していません。
続いて、enchant.js用に最低限必要なCSSです。
#game{
width: 800px; /* enchant.jsのGameオブジェクトの作成時に設定したゲーム画面サイズ */
margin:0 auto 0 auto; /* HTMLで設定したdiv#gameをセンタリング */
border: 1px solid #000000; /* ゲーム画面を囲う線(任意) */
}
###kintoneアプリの設定
中身は空っぽでかまわないので、kintoneにアプリを1つ作ります。今日まで知りませんでしたが、kintoneのフォームって、フィールドが1つもなくても怒られないんですね(^_^;)
続いて、カスタマイズ一覧を追加しましょう。いたってシンプルです。
<div id="game">
<div id="enchant-stage"></div>
</div>
さらに詳細設定の「JavaScript/CSSでカスタマイズ」で、JavaScriptとCSSを登録しておきます。enchant.jsは公式サイトからダウンロードしてください。
最後に、お約束の「設定完了」をクリックすれば作業終了です。
うまくいけば、次の動画のようにkintoneの中でブラウザゲームが動きだすはずです。終了判定をまだ実装していないので、終わらせたいときはポータルに戻ってくださいね。
##次回への課題
今回は其の1ということで、基本的なキャラクタの操作ができるところまでを組み込んでみました。これで終わってしまってもいいのですが、さすがにパイが飛んできそうなので、さらに次のような仕様を追加していきたいと思います。
- ゲームとしてあたり判定や終了を入れる
- kintoneともっと連携する。例えば、ハイスコアをアプリに記録するなど
- ゲームのパラメータをkintoneのアプリで管理する
最後のゲームのパラメータについて。たったこれだけのゲームでも実は自キャラの移動速度、弾のスピード、敵の動きなろいろいろな数値を調整する必要があります。現状とりあえずゲームっぽく見えるようにはなっていますが、まだ調整の余地はありそうです。ゲームとしての完成度を高めるにはパラメータを調整してテストプレイを積み重ねる必要がありますが、kintoneをうまく使うとそういった作業も楽になるかもしれませんね。
本当に続くのだろうか。