LoginSignup
49
45

More than 5 years have passed since last update.

Electron と Kiwi.jsを使ってゲームを作ってみた

Posted at

ElectronKiwi.jsを使ってゲームを作ってみた

前からやってみたかった。ゼロからGame Framerowkを利用したゲームの設計を学びながら作っていくのはしんどかったので、Kiwi.jsのexampleのコードを参考+改造+機能追加する形で作っていった。

GamePlay

静止画だけだと伝わらないのでゲームプレイ動画を撮ってみた。(無音)

ソースコードを管理しているリポジトリ

ゲームのインストール

Mac OS X向けのバイナリ

Mac OS X 向けにはアプリケーションのバイナリイメージを用意した。

always-gravity-1.0.0-darwin-x64.zip

以上のファイルをダウンロードじ、zipファイルを解凍する。

Screen Shot 2015-07-20 at 19.10.04.png

アプリケーションアイコンをダブルクリックすれば、アプリケーションが起動し、ゲームが開始となる。

npmコマンドを使ってインストール

npm リポジトリに登録済みなので、以下のコマンドを実行すればOK

> npm install -g always-gravity

インストール後、always-gravityコマンドを実行すれば立ち上がる。

> always-gravity

ゲームについて

タイトル

元にしていたサンプルゲームが、常に物体への加速度が下方向にかかるもので、
それを元に発展させていった。安直にゲームタイトルをAlways Gravity とした。

game-title.png

操作方法

ここでは、ゲーム開始時のキー操作について書く。
(タイトルなどではどのキーがどの操作が可能か書いてあるので割愛)

screen-shot-my-unit.png

キー 操作
自機が左方向に回転する
自機が右方向に回転する
自機が向いている方向に加速し、前進する
Z 弾を発射する。* 同時に画面上にある弾は3発まで

名前 画像 自機接触時ダメージを負うか 破壊可能か 点数
cube cube.png ノーダメージ 可能 100
cylinder cylinder.png ノーダメージ 可能 200
circle circle.png ダメージ 1 可能 500
rhombus rhombus.png 即死 不可能 -

敵を倒すには

自機から弾を発射し、敵に当てる。

  • rhombusだけは、弾を弾かれてしまう。効かない。

クリア条件

  • なし
  • Game Overになるまでひたすら点数を稼ぐだけ。

ゲームオーバーの条件

gameover.png

体力が0になる

体力が0になった時、ゲームオーバー。
0とはるが、バーで表示されてるためわかりづらいかも。
初期値は5

hitpoint.png

加速しすぎて、加速カウントが0になった時

加速し過ぎるとSLOW DOWN!!の表示が出る。0までカウントダウンされる前に減速しなかった場合、ゲームオーバー。

slow-down.png

なぜ作ったか

  • もともとJavaScript製のGame Frameworkに興味があった
    • Pixi.jsとか、CreateJSとか。
    • JavaScript Game Frameworkについて、学習コストは低そうに感じた
      • 実行環境はブラウザだし。
    • でも、assetsの管理をしなければならなくてめんどくさいなと感じた。
      • S3にassetsを置くべきか?でかいファイルの場合転送時間かかるよな、とかめんどい...
    • Kiwi.jsはTypeScript製で、exampleの数が豊富だったので良いかなと思って選択した。
  • Electronでアプリケーションを作ってみたかった
    • 流行りものだし、いじってみたかった
    • JavaScript Game Frameworkでのassets読み取りの問題については、ローカルから読み出すから大丈夫かなと判断
    • ElectronはChromium + io.jsで構成されている(atom-shell時代からだけど)だから、assetsファイルについてフォーマットどうするとか、考えなくて済みそうだと思った。

作ってみた結果

  • 込み入ったものを作るとなると、MonoGame(C#)などの型保証のある言語で書きたくなる。
    • MonoGameはFEZが作られたことで有名。XNA互換。
  • TypeScriptで書けばよかったかも、と思った
    • Kiwi.d.tsファイルがあるのでインタフェースについては問題ない

コードの進化

初期のコードはひどかった!

以下のtagのコードを見て欲しい。かなりひどい。

Always Gravity - v0.1.0

言い訳になるが、最初Kiwi.jsのexampleコードを参考にし、
GameStateオブジェクトに対してどんどんattributeを追加していく形で
コードを肥大化させてしまった!

もはや構造が見ても頭のなかに入ってこない。
ゲームとしては動作しているのだけど、機能拡張はおろか、
昨日追加した内容が思い出せなくなるんじゃないの!?ってぐらいひどい。
どのエネミーとどれが対応付けされてるんだっけ?とかわけがわからん。
HUDは正常に表示されなくなったらどこをいじればいいんだっけ!?とか。

コードを綺麗にしなければ...

Babelを利用してES6使って構造化するか

Babelを利用し、ES6構文を使えるようにすればclassが使える、ということは
構造化できるんじゃないだろうか?ということで早速導入してみることにした。

Always Gravity - v0.2.0

gulpfile.jsでBabelをpipeするtaskを書き、吐き出す。

ソースの構造は少しはマシになった。(modelとあるけどゲームでモデルというとこれは合ってるのかどうか...)

構造化できたしテストかけるのでは...?

構造化ができたとすれば、全てではないけどテストはかけるんじゃないだろうか。
ということで取り掛かることにした。

Always Gravity - v0.3.0

この時、power-assertespower, karmaなどを利用するようになった。

karma.conf.js

少しは前進していった。

ESLintでコードチェック

ESLintを初期からインストールするよう、package.jsonに追記していたのだけど
利用していなかったので利用するようにした。

Always Gravity - v0.5.0

JavaScriptのコーディングスタイルは、Airbnbのスタイルを利用し、
eslintについてもJSXを除いたものを拝借したが、フロントエンド向きではなかったので、
いくつかのerror警告をwarningに変更することにした。

ESLintを通して、letばっかりだったのがconstになったり、
if elseのブロックの書き方の癖を直したり。以下はダメ。

if (true) {
  // consequence
}
else {
  // alternative
}

これが正しい。とか。大量にエラーがでた。

if (true) {
  // consequence
} else {
  // alternative
}

あとObjectやArrayの最終要素にケツカンマ必須とか...

const status = [
  {
     name: 'hoge',
     score: 500,  // これとか
  }, // これとか
];

あれこれあって

Always Gravity - v1.0.0

tag v1.0.0までには、Rhombusに対する跳弾の実装とかもした。
すでにコードを構造化していたので、実装の追加、新しいassetの追加が楽だった。

跳弾の実装コミット

ES6化したことで、だいぶ良くなった。

スタンドアロンアプリケーションとしてビルドする

Electronアプリ作成のためのビルド用ツールはいろいろなものがあるけど、ビルドして他の環境でも問題なく動くバイナリが作れるものは、electron-packagerだった(Kobito for WindowsをMacで動かす方法を参考)。

electron-packagerを利用し、パッケージ化するスクリプトを書いた。

var path = require('path');
var packager = require('electron-packager');
var packageJson = require(path.resolve(__dirname, '../app/package.json'));
var option = {
  'dir': path.resolve(__dirname, '../app'),
  'name': packageJson.name,
  'platform': 'darwin',
  'arch': 'x64',
  'version': '0.30.0',
  'out': path.resolve(__dirname, '../release'),
  'icon': path.resolve(__dirname, '../always-gravity.icns'),
  'app-version': packageJson.version,
  'overwrite': true,
  'asar': true,
};

packager(option, function (err, appPath) {
  if (err) {
    console.log('BuildError! :' + err);
    process.exit(1);
  } else {
    console.log('Build to -> ' + appPath);
  }
});

このスクリプトを利用するよう、package.jsonのscriptsに書いておいて、
以下のコマンドを実行する。

> npm run package

とコマンドを打つと、パッケージとしてreleaseディレクトリ以下に実行可能な形のバイナリを作成してくれる。
アイコン指定(Mac OS Xの場合はicnsファイル)も可能で楽だった。

Windowsとか

Mac OS X上でしか動作確認を今はしてないが、Windowsでも
環境が整ったらアプリケーションビルドを試してみたい。

開発に関連したツール・サービス

URL 説明
http://www.bfxr.net/ 効果音を作成。弾の発射音などをこれで作った
http://www.piskelapp.com/ ドット絵を作成するのに利用。スタンドアロン版アプリもある。
https://iconverticons.com/online/ icnsファイル(Mac OS X向けのアプリアイコン作成)を作成する
http://opengameart.org/ 今回ゲームで利用したBGMはここで探してきた。
https://soundation.com/ 今回は利用しなかったが、オンライン上でパターンを登録することでBGMを作成することができるサービス 

参考にしたブログ記事など

README.mdにまとめておいた。

技術的なこと

Electron

  • Github社がリリースしたAtomというエディタの基盤となっているatom-shellが2015年4月頃リネームしたプロダクト。
  • Chromium + io.jsな環境が手に入る。性格上、Chromiumが動作する環境(Windows, Mac OS X, Linuxディストリ...)で動作するクロスプラットフォームアプリが作れる
    • これが便利で、1つの環境のことを気にかければ良いのでアプリ開発が楽。
  • HTML + CSS + JSでアプリをかけるのでサクッとアプリを作れる環境がある
  • Gulpなどのビルドツールとの連携を行うことにより、アプリケーションのスタンドアロン版を気軽にリリースできる。

Kiwi.js

Babel

  • ES6(ES2015)をサポートしていない、ES5をサポートしているJavaScriptの環境上で、ES6の構文をES5に変換する。
  • Babelを利用して、ES6に先行して実装されているfor..of構文やReflect APIを利用できる。

Gulp

  • 各処理をタスク化する便利ツール。
  • Gruntとよく比較されてる。
  • Gruntと異なる点は、Streamを利用し、method chainしている(pipe)中でstreamの受け渡しをすることで処理を書ける点。
  • 今回はgulp-karmaを利用したかったのでgulpを選択した。

power-assert

  • 前から気になっていた、アサーションでチェックするテスト用のライブラリ。
  • 今回はmochaと組み合わせて利用してみた。
  • 今回のアプリではテストの量が少なかったが、テスト書いてて楽しかった。
    • ゲームのテストってどれぐらいのものを書くべきなのだろうか...(Modelはいいとして、Viewというかスプライトの変化のテストとかどこまでやるか)
  • 今回はBabelとの併用だったので、espower-babelを使った。

Karma

雑感

  • Electronを使った〜と言っても実質ゲームをレンダリングするための Stageとしか利用していない。もう少しレンダリング側ガッツリ書くべきかも。
  • フロントエンドのJavaScriptのテストでは、すでにオブジェクトがロード済みである前提テストを書かなければならないことが多い、というのがあるのでどうしたものかとおもったが、Karmatestemを利用して、ブラウザを立ち上げて実行するのだな〜というので勉強になった
  • remote moduleを利用すれば、レンダリング側に実装を寄せることが可能なので、 そうなるとまた違ったアプローチで実装が必要になるのかなと考えたりした。
  • ipc moduleはアプリを終了するときにしか利用していないが、うまくやれば既存のWebアプリケーションに対してAPI呼び出しをElectronアプリのメインプロセスで実行して、 通常のブラウザを利用しているのとは異なるユーザ体験を与えることは可能なんじゃないかと考えたりした。
  • 当たり前なのだけど、アプリの実装は学びの段階であるプロトタイプで作成したものをリリースできるようにするために頑張って構造化して、形にしていく作業は骨が折れる。
  • ゲームの実装はexampleを参考にしつつ1.5週間ぐらい(1日1-2時間程度つかって)でできたものの、構造の見直し・テストを書く・ES6ではアレってどうかけるのかとかやってるほうが時間かかった。かける時間は少なかったとはいえ1ヶ月以上かかってしまった。楽しかったけど。
  • ポーズ機能つけとけばよかったな
  • デバッグ時、無敵状態とかそういうデバッグ用の工夫をしなかったので動作検証で死にまくって大変だった。
49
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
45