ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2018年11月15日

Create React AppでWeb Workerを使うには

「Web Worker」を使うと、ブラウザ上のJavascriptでも複数のスレッドを使うことが出来るようになります。


Web Worker を使用する - Web API | MDN https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Using_web_workers#Web_Workers_API


これによって、CPU負荷の高い計算処理などを別スレッドで実行することで、画面の反応が鈍くなったりするのを防ぐことが出来ます。


今回は、Create React Appで作成したプロジェクトでWeb Workerを使う方法を調査しました。


1. publicフォルダにWorkerのJSファイルを配置して読み込む。


⇒ 最も基本的で単純な方法です。publicフォルダに配置したファイルはそのままデプロイされるので下の方法で素直に読み込んで使うことが出来ます。

const myWorker = new Worker("worker.js");
myWorker.postMessage("hoge");


短所としては、TypeScriptやES2017などを使っている場合は別途自分でビルドしてpublicフォルダに配置する必要があることが挙げられます。


2. ejectしてからWebPackの設定に worker-loader または worker-plugin を追加する。


⇒ worker-loaderを使うと、別スレッドで動かしたい処理をModuleとして作成しておいて、使いたい箇所でそれをimportしてnewするだけでWeb Workerとして動作するようになります。コード例は、下のようになります。

import HelloWorker from './hello.worker.js';
const helloWorker = new HelloWorker();
helloWorker.postMessage('hoge');

詳細はこちら。
Add support for WebWorker with worker-loader (#3660) by iansu · Pull Request #3934 · facebook/create-react-app 

worker-plugin を使う場合はこんな感じ。

const worker = new Worker('./worker', { type: 'module' });
worker.postMessage('hoge');

詳細はこちら。
Add support for WebWorker with worker-plugin by Krivega · Pull Request #5464 · facebook/create-react-app  




3. ejectせずに react-app-rewired を使ってWebPackの設定に worker-loader または worker-plugin を追加する。


⇒ 下のブログ記事にejectせずに上の2と同じ事をする方法が書かれていました。

How to use web workers with react-create-app and not ejecting in the attempt

ただ残念ながら、react-app-rewiredは最近のVersionには非対応なのでこの方法は現在は使えないようです。


4. WorkerのJSファイルをBlobとして読み込んでからWorkerスレッドを生成する。


⇒ 下のissueへのコメントで紹介されている方法です。

Is it possible to use load webworkers? · Issue #1277 · facebook/create-react-app

関数をtoString()で文字列化して、さらにそれをBlobにします。こうすると、WorkerのコンストラクタにObjectURLとして渡してWorkerインスタンスを生成出来るようです。

let code = worker.toString();
code = code.substring(code.indexOf("{")+1, code.lastIndexOf("}"));
const blob = new Blob([code], {type: "application/javascript"});
const worker = new Worker(URL.createObjectURL(blob));

少々トリッキーですが、試してみたところ少なくともMacのChrome/Safari/FireFoxでは問題なく動いています。

この方法なら、WebPackの設定を変更する必要がありません。また上記1のようにWorkerのJSファイルだけ自前でビルド・配置したりする必要もありません。

具体的なコード例は後述します。


5. 公式にサポートされるまで待つ。


⇒ 上の2で挙げた方法がPull Requestとして上がっており公式にも検討されているみたいですが、どうやらブラウザ互換性の問題(?)があるようで2018年11月時点ではまだマージされるには至っていません。将来的には公式にサポートされる可能性はあるので、気には止めておいた方が良いかも知れません。

Add support for WebWorker with worker-loader (#3660) by iansu · Pull Request #3934 · facebook/create-react-app 

Add support for WebWorker with worker-plugin by Krivega · Pull Request #5464 · facebook/create-react-app 



今回は上記4の方法を試してみたので、以下に紹介しておきます。

WorkerのJSファイルをBlobとして読み込んでからWorkerスレッドを生成するサンプル


index.jsと同じフォルダにcreateWorker.js, myWorker.jsの2つのファイルを作成します。

createWorker.js

---

myWorker.js

---


createWorkerとmyWorkerをimportして、下のようにすることでWorkerスレッドが使用可能になります。

import createWorker from './createWorker';
import myWorkerFunc from './myWorker';
const myWorker = createWorker(myWorkerFunc);


index.js

---









2018年11月4日

React/TypeScriptでリバーシゲームを作る (2) - ゲームロジック

前回はボード上に石を配置するまでを実装しましたが、今回はさらにリバーシのルールを実装して実際に遊べるようにしたいと思います。


実際に動いているものはこちらで試すことが出来ます。
https://reversi-d1kqojbar.now.sh/




ゲームのルールを実装するには、大まかに分けて次の4つの処理が必要になります。


  1. 「次に石を配置可能な場所」の判定と表示
  2. 挟まれた石を裏返す
  3. 石を置ける場所が無い場合の「パス」処理
  4. ゲーム終了判定と勝敗の表示


1. 「次に石を配置可能な場所」の判定と表示


各手番が始まったときに、ボード上の全てのCellをループして次の処理を行います。


  • 現在のCellからタテ・ヨコ・ナナメ全ての方向に向かってひとつずつ次のCellの色を確認する。
    • 相手の石が置かれていたら、一時的な配列(arr)にそのCellの座標をプッシュして、さらにその方向の次のCellを確認。
    • 自分の石が置かれていたら、その方向への探索を終了。
    • 空白なら、その方向への探索を終了。
    • 座標がボードの範囲外に出た場合は、その方向への探索を終了。
  • 上の処理で最後に自分の石が見つかった場合で、かつ配列arrに要素が含まれている場合は、「挟める対象の相手の石が見つかった」ということなので、そのCellを「自分の石を配置可能なCell」としてマークしておく。(cell.placeableプロパティにtrueをセットする。)


Cellコンポーネントでは、対応するCellオブジェクトのplaceableプロパティが属性として渡されてくるので、その値がtrueの場合は、小さな黄色の円をSVGのcircleタグを使って表示するようになっています。


2. 挟まれた石を裏返す


プレーヤーがCellをクリックしたときには、CellコンポーネントのhandleClick()メソッドが実行され、さらにstoreのsetStone()メソッドにクリックされたCellの座標が渡されます。

store側では、Cellのplaceableプロパティがtrueでない場合は、そこには石を配置出来ないので何もせずにreturnしています。

そうでない場合は、クリックされた場所およびそこを起点として裏返し対象になるCellのcolorプロパティを全てプレーヤーの石と同じ色に変更します。

「裏返し対象のCell」というのは、あらかじめ上の「次に石を配置可能な場所」の判定処理の中で見つかったCellの座標一覧をcell.turnableCellsというプロパティに格納してあるので、それをループすることで取得できます。

裏返し対象のCellのcolorプロパティを全て変更する処理が終わった後、getNextTurn()メソッドを呼んで次のプレーヤーに手番を交替しています。

最後に'board_changed'イベントを発行して、各コンポーネントの描画を更新しています。



3. 石を置ける場所が無い場合の「パス」処理


現在の手番で石を置ける場所が見つからない場合は、ボードの placeableCount プロパティが 0 になります。

その場合は、ひとまず alert()でメッセージを表示した後、storeのskipTurn()メソッドを呼んで手番を「パス」します。

この処理はゲームの進行に関わる処理なので、Gameコンポーネント内で行っています。

skipTurn()メソッドの中では、ボードの状態を変更せずに、単に現在の手番を示す turn プロパティを更新して'board_changed'イベントを発行しています。



4. ゲーム終了判定と勝敗の表示


Gameコンポーネントでstoreから'board_changed'イベントを受け取った時に、finishedプロパティがtrueであればゲーム終了なのでその旨をalert()で表示します。

finishedプロパティがtrueになる条件は、以下の通りです。

  • 空白のCellが無くなった。
  • 黒または白のどちらかが全滅した。
  • 両者とも置く場所が無くなった。


ボードの状態を更新する際にこのいずれかに当てはまるかどうかを毎回チェックしてfinishedプロパティの値をセットしています。



以上でリバーシのルールを実装して実際に遊べる状態にまで持って行くことが出来ました。

次回は、コンピュータの思考ルーチンを作成してコンピュータとの対戦機能を追加したいと思います!


今回の時点でのソースコードはこちら:

https://github.com/mikehibm/reversi-react/tree/blog-2




React/TypeScriptでリバーシゲームを作る


(1) - ボードの描画と石の配置
https://blog.makotoishida.com/2018/10/reacttypescript.html

(2) - ゲームロジック
https://blog.makotoishida.com/2018/11/reacttypescript-2.html

(3) - 思考ルーチンその1
https://blog.makotoishida.com/2019/01/reacttypescript-3-1.html

(4) - 思考ルーチンその2
https://blog.makotoishida.com/2019/03/reacttypescript-3-2.html

(5) - アニメーション
https://blog.makotoishida.com/2019/03/reacttypescript-5.html










 

2018年10月20日

React/TypeScriptでリバーシゲームを作る (1) - ボードの描画と石の配置

実は2011年に「Androidでオセロゲームを作る」というのをやった事があります。5回のブログ記事に分けて少しずつ作り込んで行ったのですが、このシリーズが今でも結構アクセスされているみたいです。

これを読み返しながら、ふと「今だったら、ReactでWeb版を作っておけばネイティブアプリ化も結構簡単に出来るし色々と応用が効くのでは?」と思い付きました。

そこで、作り始めて見ました。

現時点で動作しているものはこちらで試せます。
https://reversi-d1kqojbar.now.sh/


タイトル画面


ゲーム画面

ソースコードはこちら

https://github.com/mikehibm/reversi-react/tree/blog-1


まだ画面をクリックして石を置くことが出来るようになっただけでゲームのルールが何も実装されていません。

しかし、リバーシゲーム作りは楽しいですね!


以下、ここまでに書いたコードの大まかな説明です。

コンポーネント階層


今のところ下のような階層になっています。

  • App (アプリケーションの大枠)
    • Menu (メニュー画面)
    • Game (ゲーム画面)
      • Board (ボードの背景や枠線などを描画するコンポーネント)
        • Cell (一つのセルを描画するコンポーネント)



状態管理


アプリケーションの状態は store.ts ファイルで一括管理しています。

ここでエクスポートしている Store クラスが EventEmitter を継承しており、状態に変更があったときにイベントを発生(emit)することでそれを各コンポーネントに通知して、各コンポーネントが画面を更新するという流れになっています。

例えば、メニュー画面でスタートボタンが押されたときには、setPage()メソッドが呼ばれて Store クラス内で保持している page 変数の値が変更されるので、その際に 'page_changed' というイベントを発生させています。

このイベントは App コンポーネントが監視していて、イベント発生時に最新の page の値を受け取って メニュー画面を表示するのかゲーム画面を表示するのかを切り替えています。





ボードとセルの描画


Boardコンポーネントでは、SVGを使ってボードの緑色の背景や縦と横の罫線などを描画しています。

また重要なのは、Cellコンポーネントの配列を作成してそれを子コンポーネントとして描画していることです。ボード上には 8 x 8 で64個のCellコンポーネントが配置されることになります。

Cellコンポーネントでは、propsとして渡された座標値にしたがってSVGのRectangleを描画します。このRectangleがマウスのクリックイベントを受け取る入り口になります。

また、propsのひとつとしてcolorが渡されますが、これはそのセルが空白なのか、黒の石が配置されているのか、または白の石が配置されているのか、を示します。

Cellコンポーネントではその値によって黒または白の円を描画する処理を行っています。

あるセル上でマウスがクリックされた時は、次の流れで最終的にセルに石が描画されるようになっています。


  1. handleClick()メソッドが呼ばれる。
  2. Storeクラスの setStone()メソッドが呼ばれる。
  3. クリックされた場所に石が無ければ、その場所の colorプロパティを現在の手番の色に変更する。
  4. Storeクラスが'board_changed'イベントを発生する。
  5. Boardコンポーネントがイベントを受け取り、ボード全体を再描画する。
  6. クリックされた場所に対応するCellコンポーネントが石を描画する。


こんな感じで、ひとまずボードの描画と石の配置までは出来るようになりました。

次回は、リバーシゲームとしてのルールを実装して実際に遊べるようになるまでを作ってみたいと思います!





React/TypeScriptでリバーシゲームを作る


(1) - ボードの描画と石の配置
https://blog.makotoishida.com/2018/10/reacttypescript.html

(2) - ゲームロジック
https://blog.makotoishida.com/2018/11/reacttypescript-2.html

(3) - 思考ルーチンその1
https://blog.makotoishida.com/2019/01/reacttypescript-3-1.html

(4) - 思考ルーチンその2
https://blog.makotoishida.com/2019/03/reacttypescript-3-2.html

(5) - アニメーション
https://blog.makotoishida.com/2019/03/reacttypescript-5.html









2018年8月10日

ReactでCSVファイルを読み込んでクライアント側で帳票を出力する

やりたかった事


あるWebサービスからCSV形式でデータをエクスポート出来るのですが、そのデータからちょっとした帳票を印刷したいという要望がありました。

そこでなるべく手っ取り早く、ブラウザだけで動くものが出来ないかと考えて作ってみたのが今回のプログラムです。

大まかな流れは、
  1. ローカルフォルダにあるCSVファイルを指定するとブラウザ上のJavaScriptでその内容を読み込む。
  2. 読み込んだ内容からHTMLで帳票を生成して表示する。
  3. ブラウザの印刷機能を使って手動で印刷またはPDFとして保存する。
という感じになります。


CSVファイルの例







作成したい帳票


実際に動いているもの

https://mikehibm.github.io/react-csv-example/




1. プロジェクトを作成する


プロジェクトは前回のエントリーと同様に Create React App + TypeScriptで作ることにします。
npm install -g create-react-app
create-react-app my-app --scripts-version=react-scripts-ts
cd my-app/
npm start

FileSelect.tsx と Report.tsx というファイルを新規作成して、App.tsx からこれらを呼び出して使うようにします。

最初は FileSelect コンポーネントを表示してCSVファイルの選択を行い、ボタンがクリックされたらファイルの内容を読み取って、Report コンポーネントに渡します。




Report コンポーネントが表示されている時は FileSelect コンポーネントは非表示になるようにします。




2. JavaScriptでCSVファイルの内容を読み込む


HTML5のFile APIを使います。FileReaderでの読み込みは非同期処理なので、処理をラップしてPromiseを返す関数を作りました。







3. ファイルの内容をパースしてオブジェクトの配列に変換する


上の readFileAsText() からファイル全体の内容が文字列として返ってくるので、それをパースしてオブジェクトの配列に変換します。

具体的には、まず改行文字で区切って行単位の配列に分け、さらにその各行について「,」で区切って列単位の配列に分けます。つまり最終的には string[][] (文字列の配列の配列)になります。




ここでは簡単に改行文字(\n)と「,」で区切っているだけですが、実際には改行文字が違う、値に「,」が含まれている、などさまざまなケースがあり得るのでCSVのパース処理はちゃんとやろうとすると実は結構大変です。

なので必要であれば下の記事で紹介されている csv-parser などを使った方が良いかもしれません。

ブラウザ上でCSVファイルをパースする
https://qiita.com/ledsun/items/e38ee0dff8f26bf8d930




その後、文字列の配列(の配列)から、今度は帳票を生成する時に扱いやすいように、WorkItemというクラスのインスタンスの配列に変換します。

この時に、文字列型から日付型への変換や、CSVファイルに無い項目の値(duration)の計算なども同時に行っています。





4. HTMLで帳票を生成する


WorkItemオブジェクトの配列を受け取って、tableタグで表形式のHTMLに変換します。

本当は帳票のタイトル、見出し、明細行などをそれぞれ別のコンポーネントに分けた方が良いのだと思いますが、今回はあまり時間がなかったので1コンポーネントで作ってしまいました。





あとは画面に表示された帳票をブラウザの印刷機能で印刷するだけです。デフォルトではおそらく余計なヘッダーやフッター(ページタイトルやURL)も印刷されてしまいますが、印刷時の設定画面でこれらをオフにしておけばきれいに出力されるはずです。


今回作ったアプリの全ソースコードは下記にあります。

https://github.com/mikehibm/react-csv-example







2018年8月8日

Create React App + TypeScript で最初につまずいたこと

ちょっとしたサンプルプログラム的なものを Create React App + TypeScript で作ろうと思ったのですが、思わぬところでいきなり詰まってしまいました。


Create React App(以下CRA)で TypeScript と言えば、

https://github.com/wmonk/create-react-app-typescript

が定番だと思います。


さて、下の通りにプロジェクトを作成して実行しました。

npm install -g create-react-app
create-react-app my-app --scripts-version=react-scripts-ts
cd my-app/
npm start


もちろんここまでは全く問題なし。早速VS Codeでプロジェクトのフォルダを開いて、アプリケーションを作り始めました。



ところが、あるタイミングでふと

console.log("hogehoge");

を入れて実行してみようと思ったところ、いきなりコンパイルエラーになってしまいました。


「Calls to 'console.log' are not allowed.」というエラーですが、これにはちょっと驚かされました。console.logを入れただけでアプリケーションが動かなくなるなんて。。。


これですが、デフォルトで設定されているTSLintのルールがめちゃくちゃ厳格なものになっているのが原因なようです。


厳格すぎてこれでは使いづらいという声も多く、GitHubのIssuesでも議論になっているようですね。


オリジナルのCRA作者のDan Abramov氏からも、「この厳しいルールはCRAを作った時の思想(初心者でもすぐに動くものを作り始められる。本当にクリティカルなもの以外はワーニングのみでエラーにはしない。)と合わないから、ゆるくした方が良いんじゃない?」 という趣旨のIssueをわざわざ上げられているぐらいです。


create-react-app-typescript リポジトリ作者のWill Monk氏によると、「最初は自分が職場で使っているルールをコピーして使っていたのだけれど、後で一般的に推奨されているプリセット("tslint:recommended", "tslint-react"など)を使うように変更した」との事。「それでもまだルールが厳しすぎるなら、誰かがオリジナルのCRAと同等のゆるいルールの設定を作ってPRしてくれたら喜んでそれをマージするよ。」とも言っているので、彼自身は特にどうしても厳しいルールを他の人に強制する気は無いようです。


その後このIssue上で議論は進んでいるようですが、このブログ執筆時点の最新版ではまだルールの変更には至っていないようです。


さて、なにはともあれとりあえず console.log を使えるようにする方法ですが、上のIssueでも言及されているのですが、Microsoftによる下記のページにわかりやすくはっきりと記載されていました。

https://github.com/Microsoft/TypeScript-React-Starter


プロジェクトのルートフォルダにある tslint.json ファイルを下のように書き換えればOKです。(赤字を削除、青字を追加。)

{
-  "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
+  "extends": [],
+  "defaultSeverity": "warning",

"linterOptions": {
     "exclude": [
       "config/**/*.js",
       "node_modules/**/*.ts"
     ]
   }
 }

これで問題なく開発が続けられるようになりました!






 

2018年5月1日

JavaScriptで "B" + ["a", "a"].join("aaa" - 1) がBaNaNaになる理由

Ethereum開発者のVitalik氏がこんなツイートをしていたのを見ました。


試しにChromeのDeveloper Toolsを開いて実行して見ると、確かに「BaNaNa」と表示されました。
"B" + ["a", "a"].join("aaa" - 1)
"BaNaNa"

不思議に思ったので少しずつ変えながら何パターンか試してみます。

"B" + ["a", "a"].join("aaa") → "Baaaaa"
"B" + ["a", "a"].join()  → "Ba,a"
"B" + ["a", "a"].join("")  → "Baa"
"B" + ["a", "a"].join("" - 1)  → "Ba-1a"
"B" + ["a", "a"].join(1)  → "Ba1a"
"B" + ["a", "a"].join("x" - 1)  → "BaNaNa"

なるほど、分かってきました。

"aaa" - 1 の部分が「NaN」として出力されて、それがArray.join()の引数になるので
B + a + NaN + a
で 「BaNaNa」というわけですね。

なるほど!

 

2018年3月1日

TypeScriptを使うならstrictNullChecksオプションが便利

TypeScript の strictNullChecks オプションというものを知ったのでメモしておきます。

tsconfig.json の compilerOptions に

"strictNullChecks": true

を追加しておくと便利。

これをすることで、string や number などに null や undefined を代入することが出来なくなる。

let name: string;
name = null;                ← ここでコンパイルエラー

let count: number;
count = undefined;      ← ここでコンパイルエラー


null や undefined を代入したい場合は、型指定時に明示しておけばOK。

let name: string | null;
name = null;                ← これはOK

let count: number | null | undefined;
count = undefined;      ← これはOK


「ユニオン型」というらしいですね。
http://d.hatena.ne.jp/m-hiyama/20180117/1516157225

色々なシチュエーションで、「この変数の値は null/undefined になる可能性がある(もしくは無い)」という事を明示出来るのでnullチェック忘れや過剰なnullチェックを減らす事が出来ます。




 


2017年6月19日

Angularでサーバーから取得したPDFを表示する


Angularでサーバーから取得したPDFを表示するサンプルを作ってみました。


実際に動くものはこちら

全ソースコードはこちら。



サーバーからPDFファイルを取得する部分



サンプルではGitHub Pagesからスタティックなファイルをgetしていますが、Web Apiから動的に生成されたファイルを取って来る場合でも全く同じです。



受け取ったPDFファイルをクライアント側で表示する部分




URL.createObjectURL(blob) でblobからデータURLを生成して、それをHTML側で object タグの data 属性にバインドして表示しています。

ただそれだけだとAngularのセキュリティ制限でエラーになるので、DomSanitizerのbypassSecurityTrustResourceUrl()というメソッドを呼ぶ必要がありました。


あ、それからこのサンプルは現時点ではIEでは上手く動かない様です。

ChromeとFirefoxでは問題無く動作しました。

MacのSafariではobjectタグでの表示はされましたが、Downloadのリンクをクリックしても別タブで表示されるだけでダウンロードはされませんでした。

ブラウザ objectタグでの表示 aタグでのダウンロード
Chrome
Firefox
Safari
Edge ×
IE11 × ×











2017年5月18日

Node, express, nginx で動くWebサイトへのファイルアップロードでサイズ超過エラーが出る時の対策

Node/Express.js で作ったWebアプリケーションをテストしていて、CSVファイルのアップロード時に一定のファイルサイズを超えると「413 Request Entity Too Large」というエラーになる事に気付きました。


調べた結果、

  1. ExpressのbodyParserミドルウェア
  2. MultiPartフォームデータの処理を簡単にしてくれるミドルウェアのMulter
  3. そしてリバースプロキシとして使っているNginx 

の3箇所で許可するファイルサイズの最大値を設定する必要があることが分かりました。


1. Expressの設定


app.use(bodyParser.json({ limit: '20mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '20mb' }));


2. Multerの設定


const upload = multer({
  limits: {
    fileSize: 1024 * 1024 * 20, // 最大20MBまで許可
  }
});



3. Nginxの設定


location / {
proxy_pass http://127.0.0.1:3000;
... (略) ...
client_max_body_size 20M;
}


最初1と2の設定を付けたのに上手く行かなくて、3の設定が必要な事に気付くまでに時間がかかってしまったので、メモしておきます。







 

2016年8月10日

Visual Studio Code で .js.map など特定のファイルを非表示にする

VS Codeで TypeScript のソースコードを書いていて Webpackやtscコマンドでトランスパイルした時にデフォルトでは .map ファイルも自動的に作成されます。

このソースマップファイルをエディタ上のファイル一覧には表示したくないので、その設定方法のメモです。

{
    "files.exclude": {
        "**/*.js.map": true,
        "**/*.js": {"when": "$(basename).ts"},
        "**/.git": true,
        "**/.DS_Store": true
    }
}


"**/*.js": {"when": "$(basename).ts"}

と書くと、「同じ名前で拡張子が .ts のファイルが存在する場合だけ .js を非表示にする」という事が出来るとの事。

これは便利。



【VSCode】Visual Studio Code 0.5.0の新機能まとめ - Tumbling Dice http://outofmem.hatenablog.com/entry/2015/07/20/154030

[VSCode]ファイルツリーの表示制御|杏z 学習帳
https://anz-note.tumblr.com/post/126106378651/vscode%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%84%E3%83%AA%E3%83%BC%E3%81%AE%E8%A1%A8%E7%A4%BA%E5%88%B6%E5%BE%A1






2016年7月17日

Azure Blob StorageにJavaScriptから直接アップロードする

Azure Blob Storageにブラウザ(JavaScript)から直接アップロードする必要があったので、簡単なサンプルを作ってみました。

https://github.com/mikehibm/AzureDirectUploader


JavaScriptでHTML5のFile APIを使ってフォルダ内のファイル一覧を表示し、「Upload」ボタンを押すと選択されたファイルを一つずつAzure Storageにアップロードします。


ブラウザから直接アップロードするとは言っても、最初に一度だけサーバー側の処理を呼び出す必要があります。ASP.NET WebAPIはそのためだけに使っています。

これはAzure Storageアカウントのアクセスキーをクライアント側に渡さずに、一時的に「Shared Access Signature(共有アクセス署名)」というものを生成してそれをクライアント側で使える様にするためです。

一度SASを取得してしまえば、有効期限が切れるまでそれを使ってブラウザからAzure Storageにアップロード出来ます。サンプルでは有効期限が切れたら再度SASを取得し直してアップロードを継続する様になっています。

クライアント側から直接アップロード出来ればサーバー側の負荷が減って便利ですね。






.

2015年9月18日

React.jsの練習に◯☓ゲームを作ってみた。

ある日の深夜1時にふと思い立ってどうしても実際にReactで何か作りたくなり、朝5時までかかって大体の部分を作りました。

終了判定の部分がバグっていたりしたので2時間ほど眠ってからまた3時間ほどかけてデバッグ。翌日にまた約1時間かけて調整をして一応出来ました。

合計8時間もかかってますね。 汗)   
何せ初めてのReactなので。

クリックすると別ウィンドウでRunstant Liteが開きます。
https://goo.gl/Psx5fl

使ったツール


@phi_jp さん作成の「Runstant Lite」素晴らしいです。
ソースコードをエンコードしてURLに保存するというアイデアには脱帽しました。



初めてReact.jsで書いてみた感想


実は今AngularJSも勉強中ですが、それと比べるとReactの方が覚えないと行けない事が圧倒的に少ないので精神的に楽です。

まず親コンポーネントをとりあえず作って四角いDIVを表示する事が出来れば、そこに一つずつ子コンポーネントを付け足して行く感じでUIの作成はそれほど苦労せずに出来ました。

今回は

  • Board (盤面の背景を描画)
  • Cell  (個々のセルを描画)
  • StatusPanel (「X」の番です、などゲームの状態を表示)
  • ResultPanel (「X」の勝ち!、引き分け、などの結果を表示)

の4つのコンポーネントを作成しました。

最初は個々のCellにStateを持たせてクリック時の表示の更新をCellの内部で行っていました。でもそれだとBoard全体としての処理(終了判定など)との連携が面倒になる事に気付いて、Reactの世界の外側に作ったAppクラスで全ての状態を管理する様にしました。

ReactはあくまでView担当なので、Modelは自分で作らないとダメなんですよね。

そうなると今度は
1. Reactのコンポーネント内で発生するイベント(クリックされた、など)をModel側に伝える方法
2. 逆にModel側で発生するイベント(勝敗が着いた、など)をReactコンポーネント側に伝える方法
が必要になります。

前者の場合は単純にonClickなどのハンドラからAppクラスの関数を直接呼ぶ事も出来るのですが、後者のやり方が分からずにちょっと悩みました。

そこでFlux関係の解説を読み漁って、「とにかくPubSubで疎結合にしろ」という事なのかな〜と理解しました。

その結果、自分なりにEventEmitterもどき(?)を作って、Reactコンポーネントとモデルが必ずそれを経由して対話する様な形になりました。よくFluxで言われる「単方向のフロー」にはなっていない様な気もしますが、一応「PubSubで疎結合」にはなっているのではないかと思います。

こういうのはやっぱり自分で作って試行錯誤して見ないと腹の底から理解する事は出来ないですね。

単純な◯☓ゲームでしたがとりあえず作ってみて、Reactへの理解を少し深める事が出来たと思います。Android版のReact Nativeも出て来た事だし、これからもさらにReactに時間を投資して見ようかなと思います。


※追記 


きちんとFluxに則って作られたきれいな◯☓ゲームを見つけました。コードも見た目もきれいです。これは参考になります。

https://github.com/hackhat/tic-tac-toe-flux

ついでに、Reactで作られたゲームの一覧もありました。

36 Game Examples with ReactJS
http://react.rocks/tag/Game



React.jsの勉強のために読んだ本


入門 React ―コンポーネントベースのWebフロントエンド開発



参考になったサイト


reactjs - Reactデザインパターン - すべてがeになる - Qiita
http://qiita.com/shunjikonishi/items/ba6d981880e316131836

Flux – Dispatcher【日本語訳】と実装のポイント | mae's blog
http://mae.chab.in/archives/2738

一人React.js Advent Calendar 2014 - Qiita
http://qiita.com/advent-calendar/2014/reactjs

チュートリアル | React
https://facebook.github.io/react/docs/tutorial-ja-JP.html




.

2015年6月27日

JavaScriptでスマホゲームアプリを作る - Unity編

前回のCocos2d-JS編からだいぶ間が空いてしまいましたが、今回はUnity編です。

Unityのバージョンも5になって、新しいGUI機能が追加されていたりして2dゲームの作成もかなりやり易くなっている様です。

今回Unity5を使って出来たものはこんな感じです。



https://github.com/mikehibm/15puzzle-unity


● 新GUI(uGUI)の使い勝手


定番のNGUIを使った事が無いので比較できないのですが、uGUIは特に問題無く使えそうです。
気になるマルチ解像度への対応も簡単。どんな画面サイズでもボタンを常に右上に置きたい、という場合でもアンカーのプロパティ設定だけで行けるので気に入りました。今回のアプリではタイトル画面のメニュー、ゲーム画面のホームボタンとリスタートボタンをuGUIで作成しています。



● Tweenライブラリが色々あって迷う


パズルのピースを移動する時などにアニメーションをするためのTweenライブラリですが、いろいろと選択肢があるので結構迷います。

今回は LeanTween というのを使ってみました。他に iTween, Hotween なども良さそうでした。

LeanTweenは単体のアニメーションを実行するだけなら使い易いのですが、複数のアニメーションを組み合わせて順次実行し、それをさらにループさせたいという様な場合にコールバックを多段に繋がないと行けなくなるのでちょっと面倒です。


● UnityのJavaScriptは普通のJavaScriptではない


これは一番困った点かも知れません。UnityのJavaScriptは、C#にJavaScriptの皮を被せた様な妙な言語でした。。。

Unity の JavaScript でよくわからないことメモ

こんなヘンテコな言語に慣れるぐらいならいっそC#で開発した方が良いと思います。次にUnityで作る時は迷わずC#ですね。


● Cocos2d-JSと比較した感想


GameObjectとComponentの関係、GameObjectにスクリプトを貼り付けて行くやり方を分かった瞬間に、「Unityええやん!」と思ってしまいました。

Coco2d-JSと比べても、もしかするとUnityの方が生産性が高いかも。。。
今まで1年以上もCocos2d-x/JSで開発して来た私ですが、「もっと早くUnity始めてれば良かった」という気もちょっとして来ました。

次はUnity/C#でもうちょっと複雑なものを作って見ようかなと思います。






以上!

2015年1月13日

Cocos2d-JSでAdMob広告を表示する手順 (iOS, Android)

2015年1月12日現在の情報です。

開発環境


- Mac OS X 10.9.5
- Cocos2d-JS v3.2
- Cocos Code IDE v1.1.0
- Xcode v6.1.1
- Eclipse (ADT) 23.0.2


cocos newコマンドでプロジェクトを作成


cocos new -p com.example.CocosJSAdmob -l js CocosJSAdmob

(もちろんCocos Code IDEのメニューから新規プロジェクトを作成する事も出来るのですが、そうすると生成されるファイルが微妙に違ったりするので私はcocosコマンドを使う事にしています。)


Cocos Code IDEでプロジェクトを開く


現状ではcocos newコマンドで作成したプロジェクトをそのままCocos Code IDEにインポートしようとしても「プロジェクトが見つからない」というエラーになるので、まずプロジェクトのルートディレクトリに下の内容で .project ファイルを作成する必要があります。

<name>CocosJSAdmob</name>の部分だけ適宜変更して下さい。

.projectファイルを作成した後、Cocos Code IDEにインポートします。





Cocode Code IDE上で実行出来る事を確認しておきます。



ネイティブコードサポートを追加する。


Cocos Code IDEのCocos Toolsメニューから「Add Native Codes Support」を選択します。
これによってプロジェクトの下に frameworks/runtime-srcというディレクトリが出来ます。



Xcodeでプロジェクトを開く


下のディレクトリにXcode用のプロジェクトファイルがあるのでそれを開きます。

  /(プロジェクトルート)/frameworks/runtime-src/proj.ios_mac/



ターゲット(実機またはシミュレータ)を選んでそのまま実行するとビルドされて実行出来ます。ただ初回は全てのソースファイルをコンパイルするので数分かかるかも知れません。



iOSでのAdMob対応


下を見ながらAdMob SDKを組み込みます。

https://developers.google.com/mobile-ads-sdk/docs/admob/ios/quick-start

AdMob SDKのファイルをプロジェクトに追加。



ライブラリを追加。

下の青枠内が今回追加したもの。特に「GameController」はGoogleのドキュメントでは記載漏れになっているみたいなので要注意です。



-ObjCリンカフラグを追加。


*追記:
AdMobの最新版(v7以降)では-ObjCリンカフラグは不要になったそうです。


ソースコードを変更。


AppController.h
AppController.mm

dealloc関数の後ろ、@endの前に以下を追加。

JavascriptからObjective-Cのコードを呼ぶ。


詳細は下記を参照して下さい。
 How to call Objective-C functions using js on iOS/Mac

app.jsの適切な場所に下のコードを追加します。今回は HelloWorldLayer の ctor関数の一番最後に追加しました。

jsb.reflection.callStaticMethod("AppController", "ShowAdView");


iOSでAdMob広告が表示されたところ

広告を隠したい時はHideAdViewを呼べばOKです。

jsb.reflection.callStaticMethod("AppController", "HideAdView");

iOSでの対応は以上で終わりです。




Eclipse(ADT)でプロジェクトを開く


下のディレクトリにEclipse用のプロジェクトがあるのでそれをインポートします。

  (プロジェクトルート)/frameworks/runtime-src/proj.android/

インポートした後、プロジェクトのディレクトリで

cocos compile -p android

を実行して全てのソースコードをコンパイルしておきます。これも初回は数分ぐらいかかるかも知れません。


AndroidでのAdMob対応


次のドキュメントに従って Google Play Serviceを組み込んで下さい。

https://developers.google.com/mobile-ads-sdk/docs/?hl=ja


Java側にAdMob広告を表示する処理を追加。


AppActivity.javaに以下の変更を加えます。

1. AppActivityクラスの先頭に変数を追加。

private static Activity activity = null;
private static AdView adView = null;


2. onResumeでactivityを変数に保持。

@Override
protected void onResume() {
super.onResume();
activity = this;
};

3. AppActivityクラスに3つのメソッドを追加。

public static void createAdView()
public static void showAdView()
public static void hideAdView()

変更後のAppActivityクラスは下の状態になります。
(xxxxxxxxxxxxxxxx...xxxxxxの部分は適宜置き換えて下さい。)

JavascriptからJavaのコードを呼ぶ


app.jsを下の様に変更します。


Android上でAdMob広告が表示されたところ


Androidでの注意点


現状、Google Play Serviceへの参照をプロジェクトに追加して一旦Eclipse上でRunしてしまうと、それ以降は cocos run, cocos compile, cocos deployなどのコマンドが下の通りエラーで中断してしまいます。


-package-resources:
     [aapt] Creating full resource package...
     [aapt] invalid resource directory name: /Applications/android-sdk-mac_x86/extras/google/google_play_services/libproject/google-play-services_lib/bin/res crunch

BUILD FAILED
/Applications/android-sdk-mac_x86/tools/ant/build.xml:932: The following error occurred while executing this line:
/Applications/android-sdk-mac_x86/tools/ant/build.xml:950: null returned: 1

Total time: 43 seconds
Error running command, return code: 1


原因はADTとAntでビルドの方法(一時ファイルの場所など)が異なる事にあるみたいです。
( http://stackoverflow.com/questions/19746319/how-to-solve-invalid-resource-directory-name-resource-crunch )


対策としては、Eclipseを完全に終了するかまたはEclipseの自動ビルドをオフに設定してから cocosコマンドを実行すればエラーにならない様です。



以上、Cocos2d-JS v3.2でAdMob広告を表示する方法でした。

jsb.reflection.callStaticMethod という関数を使えばJavascriptからObjective-CやJavaのメソッドを簡単に呼び出せるので便利ですね!

これを使えば広告の表示制御だけでなく、
  • ブラウザで任意のURLを開く
  • AppStoreに飛ばす
  • Facebook, Twitterなどに投稿する
など色々な事が簡単に出来そうです。









 .

2014年12月30日

Azure, Node.jsとsocket.ioで作る早押しクイズ大会アプリ

先日の忘年会のネタに早押しクイズ大会アプリを作りました。




1.主催者がアプリのURLをブラウザで開き、クイズ大会を開始。
2.画面にパスコードが表示されるのでアプリのURLとパスコードを参加者に伝える。
3.各参加者はスマホのブラウザでアプリのURLを開いてパスコードを入力。
4.人数が集まったら主催者が「開始」ボタンを押す。
5.参加者の画面に問題が同時に表示される。
6.最初の正解者が3点、2番目の正解者が2点、3番目が1点獲得。不正解はマイナス1点。
7.5〜6を繰り返す。
8.全ての問題が終わったら結果発表画面を表示。

という流れで動きます。


ソースコードはこちら。
https://github.com/mikehibm/EnkaiQuiz

ホスティングはもちろんAzure Web Sites。

コーディングは全てVS Online上で行いました。Azure Web SitesとVS Onlineを組み合わせるとNode.jsアプリの開発はローカルに環境を一切作らなくても出来るのでとても便利です。

快適にコーディング出来たVS Onlineの画面

一応、同時に複数のクイズ大会をホスト出来る様になっています。

今回初めてsocket.ioを使ってサンプルのチャット以外のアプリを作りましたが、思っていたよりスムーズに作る事が出来ました。

とは言っても、サーバー側からクライアントにメッセージを送るのに io.emit() を多用してしまっているのはまずいかも。本来なら特定のクイズ大会に参加している人のみに送信するべきですね。

リファレンスを見ると、「Custom namespaces」を使う方法と「Rooms」を使う方法の2種類あるのかな(?)
http://socket.io/docs/rooms-and-namespaces/

実は他にも色々と改善が必要な点が。(笑)

- サーバー(Node)のプロセスが落ちたら全部消える。→ サーバー側のデータはDBに永続化しないと。
- クライアント側のデータも出来ればLocalStorageに格納したい。
- エラー処理をもっとちゃんとしないと。
- 単体テストは...  ...


見直せば見直すほどいろいろ出て来ますが、また余裕が出来たら改善しようかと思います。今回の忘年会では参加者10人で問題なく動いてくれたのでとりあえずいいかなと。



残念ながら今年の忘年会シーズンはほぼ終わってしまいましたが、新年会で使えるかも(?)

クイズの問題と解答のデータは public/js/data.js にJSON形式で入っているので変更するのも簡単です。

実行時に動的に外部からデータだけ読み込める様に改造するのも良いですね。

動かしてみたい方はぜひGitHubのソースをForkして試してみて下さい!







 
.

2014年10月17日

JavaScriptでスマホゲームアプリを作る - Cocos2d-JS編

前回のPlatino版に続いて、Cocos2d-JS版が出来た。



ソースコードはこちら。https://github.com/mikehibm/15puzzle-cocos2d


● 困ったところ


う〜ん、特になし。

あえて言えばy座標の指定方法(上下)がこれまでと逆になるのでそれを変更する必要があったぐらい。

あとそういえば、ifで座標が一致するかどうかを比較して分岐している箇所があって、

 if (piece.x !== new_pos.x || piece.y !== new_pos.y){
   ...処理...
 }

と書くと処理が実行されなくて困った。(浮動小数点数なので)
下の様に差分の絶対値で比較する様に変更した。

 if (Math.abs(piece.x - new_pos.x) > 1 || Math.abs(piece.y - new_pos.y) > 1){
   ...処理...
 }



● 良かったところ


アニメーションの作り方やシーン遷移の方法など、これまでに得たCocos2dの知識が活かせるので楽が出来た。

実機での実行速度も申し分無し。

デバッグが簡単。Cocos Code IDEからデバッグ実行しておくとJSファイルを修正して保存した瞬間にデバッグアプリ内でコードが再ロードされるのですぐに確認出来る。もちろんブレークポイントを設定する事も可能!


● アニメーション実行中にさらにアニメーションを実行する場合


MoveToを使ってピースを移動するアニメーションを実行中に、場合によってはさらに次のアニメーションを開始する必要がある場合がある。連続でタッチして同時に複数のピースを動かした場合などだ。

その場合、同じSpriteに対して同時に複数のMoveToアニメーションを実行してしまうと最終的な位置が思わぬ所にずれてしまう事になる。

これについては、2つめのMoveToアニメーションを実行する前に

 piece.stopActionByTag(ACTION_TAG);

を実行して前のMoveToを中止する事で解決出来た。


● Mac版デバッグアプリの設定変更


Cocos Code IDEから実行するとデフォルトだとMac版のデバッグ用アプリが立ち上がって来てその中でアプリが実行される。

このデバッグ用アプリの画面サイズ(Zoom設定)が、起動する度に100%になってしまいMBAだとiPhone5の縦画面がディスプレイに収まらないので、そこがちょっと使い辛かった。

これは下のページの通りに設定を変更する事で50%表示にする事が出来た。これでとても快適になった。
Lua - Cocos Code IDEのシミュレーターのデフォルト設定 - Qiita 



● ソースコード


今回のソースコードはこちら。
https://github.com/mikehibm/15puzzle-cocos2d




● 感想


さすがに注目度急上昇中のフレームワークだけあって Cocos2d-JS は使い易い。デバッグも素早く出来るので気分良く開発が出来る。

またXcodeやEclipseからもプロジェクトを開いて実行出来るので、広告表示やアプリ内課金などのネイティブ連携も頑張ればなんとかなりそうだ。

パーティクルエディタやシーンエディタなどのツール群も充実しているので、やっぱり一番安心して実戦で使えるのはこれかなあと思う。

あと次はUnityにチャレンジしてCocos2d-JSを上回るメリットがあるかどうかを試したい。








 .

2014年10月14日

JavaScriptでスマホゲームアプリを作る - Titanium + Platino編

ようやく15パズルのTitanium+Platino版が出来た。

Platino Game Engine


ソースコードはこちら。 https://github.com/mikehibm/15puzzle-platino


● Platinoプラグインの導入


プラグインの導入は至って簡単。トライアル版をダウンロードして解凍して所定のフォルダにコピーすればTitanium側で認識してくれた。

あとはプログラムの先頭に

var platino = require('co.lanica.platino');

をいれてガンガン使って行くだけだ。
BGMや効果音を鳴らす時は

var ALmixer = platino.require('co.lanica.almixer');

も必要。
さらにapp.jsなどで最初に一回だけ

ALmixer.Init(0,0,0);

を呼んで初期化しておく必要がある。最初この初期化をしてなかったせいで音が出なくて30分ほど悩んだ。


● まずはサンプルのソースを確認


Herlockと同じくPlatinoもドキュメントを見てもいまいち分からないので、最初はまずサンプルプログラムを全てダウンロードしてソースを熟読するのが良いと思う。


Flappy Birdや戦車ゲームのサンプルが面白いけれど、多分一番参考になったのは「Codestrong Shooter」というシューティングゲームのサンプルだ。


● マルチ解像度対応


サンプルプログラムのマルチ解像度対応がちょっと気に入らなかったので、自分なりに試行錯誤する必要があった。背景の画像などは全てiPhone5sの640x1136のサイズを基準にして作っておいて、端末の解像度に合わせて横幅を画面一杯にして縦が短い場合は上下が切れる様に調整した。ApplicationWindow.js の updateScreenSize 関数でその辺りをやっている。

スプライトなどを画面に配置する時は、横幅は常に640として固定値で指定して構わないのだけれど、縦の高さは端末によって変わるので、真ん中に寄せたいもの(パズルのピースなど)については中央からの相対指定として、上端または下端に合わせたいものはそれぞれ上端(y=0)または下端(y=画面サイズ)からの相対指定とする様にした。


● タッチイベント


画面に配置したSpriteのタッチイベントを取得するのが少々面倒だった。まず画面(Sceneオブジェクト)のタッチイベントを書いて、その中でスケールを考慮して座標を変換した上で Sceneの forwardTouchToSprites 関数を呼んでやる必要がある。


● 連続でアニメーションを実行


アニメーションは下の様な感じでTransformオブジェクトを生成して実行する形になる。
var transform  = platino.createTransform({
  duration: 500,
  alpha: 0,
  easing: platino.ANIMATION_CURVE_CUBIC_IN,
  autoreverse: true,
  repeat: -1
 });
sprite.transform(transform);
ただ、連続で複数のアニメーションを実行するための簡単な方法が標準では用意されていない。「Codestrong Shooter」のサンプルにもある通り、アニメーション終了時のcompleteイベントを拾って次のアニメーションを開始すれば良いのだけれど、非常に面倒だ。

そこで調べたところ下のモジュールを見つけた。


これを使えば例えば6個のアニメーションを連続で実行する場合でも下の通り簡潔に書くことが出来るのでとても便利だった。
highScoreTranstion = new Transition([
  { duration: 1000, x: CENTER_X-highScoreLabel.width/2, easing:   platino.ANIMATION_CURVE_QUAD_OUT }
  ,{ duration: 500, alpha: 0, easing: platino.ANIMATION_CURVE_CUBIC_OUT }
  ,{ duration: 1000, alpha: 1, easing: platino.ANIMATION_CURVE_CUBIC_OUT }
  ,{ delay: 500 }
  ,{ duration: 1000, x: game.screen.width*-2, easing: platino.ANIMATION_CURVE_QUAD_IN }
  ,{ duration: 1, x: game.screen.width*2 }
]);
highScoreTranstion.animate(highScoreLabel, -1);


● Androidでのフルスクリーン表示


Androidで実行すると画面の上部にタイトルバーが表示されてしまって困った。
とりあえず tiapp.xmlファイルに

<fullscreen>true</fullscreen>
<statusbar-hidden>true</statusbar-hidden>
<navbar-hidden>true</navbar-hidden>

と書いてみたけれども Android 4.4 では効果なし。調べると新しいAndroidではプログラム内で

   window.activity.actionBar.hide();

を呼ぶ必要があるという事だったのでその通りにしたら無事フルスクリーンで表示された。


● iOSとAndroidで微妙に挙動が違う所


TextSpriteを配置した時にiOSとAndroidで位置がずれてしまって困った。
どうもAndroidだとwidth,heightが自動では計算されていない(0,0になっている)みたいだ。これは platino.createTextSprite 関数を呼ぶ際に明示的に widthとheightをセットする事で解決した。

他に、Androidだと微妙に処理が遅いのか少しカクカクした感じがしてしまう。上にアップした動画でも雲の動きがちょっとカクカクしているのが分かるけれど、iOSだともっとスムーズに動いた。Titanium + Androidでは前にも、「今ひとつどうもな〜」という経験をした事があるので、今回もやっぱりそうかという感じはある。


● ソースコード


今回のソースコードはこちら。
https://github.com/mikehibm/15puzzle-platino


● 一応の結論


Titaniumでのアプリ開発をずっとやっている人で、「ゲーム開発にもTitaniumを使いたい!」と言う場合には良い選択肢かも知れない。

あとドキュメントの中の下のページはすごく参考になった。

http://docs.lanica.co/docs/#!/guide/javascript_best_practices
配列をクリアする時は
myArray = []; 
ではなく
myArray.length = 0;
と書いた方が良い、とか。







 

2014年9月5日

JavaScriptでスマホゲームアプリを作る - Herlock編

さて今回はHerlockを使って15パズルを作ってみた。



● Herlockとは


http://herlock.do/faqs/1304
株式会社ソニックムーブが提供するスマートフォンネイティブアプリ開発環境となります。
http://herlock.do/faqs/1338
AndroidとiOSに対応したワンソースでネイティブアプリを同時に開発することができます。
また、Web IDEを使用して開発、デバッグからビルドまで行うことができ、開発時のアプリはクラウド上で保存するだけで簡単に動作確認が可能です。
さらに、JavaScriptでOpenGLによる実装ができ、ゲームのようなハイパフォーマンスのアプリケーションを簡単に開発することが可能となります。




● 作ったもの


PhoneGap編Monaca編と基本的に同じ15パズル。

ただ今回は画像を使ったので見た目が大分違っている。
Herlockのサンプルにあった「ねこ穴」というアプリのソースコードを元にして作ったので、勢いで一部の画像をそのまま使わせて頂いてしまった。


ソースコードはこちら。(とても自慢出来たものではないですが。笑)

tmlib.jsには含まれているシーン管理やアセット管理の部分をHerlockでは自分で作り込まなければならないのが結構大変だった。(結局ねこ穴のソースから大部分をコピペして使った。) 逆に言うと自分で好きなように作りこんだりどこかのライブラリを使ったり、自由度が高いと言えるかも知れない。

アニメーションについてはTweenJSで簡単に出来たので助かった。



● Herlockの素晴らしいところ


WebViewではなくネイティブのOpenGL機能を使っているのでアニメーションのパフォーマンスは申し分ない。JavaScriptだけでこれだけの事が出来るというのは本当に素晴らしい。

サウンドの方もiOS, Androidともに全く問題無しだった。PhoneGapやMonacaではAndroidで音が出ない問題に悩まされただけに、これは嬉しい。

Web IDEが提供されているのでローカルに開発環境を用意する必要が無い。これもさくっとアプリを作りたい時には大変有難い。



● 苦労したところ


1.実行するまでエラーが分からない

これは特にHerlockだからという訳ではないけれども、JavaScriptでの開発は静的型付けのある言語と比べると意外と大変だなあと改めて感じてしまった。ちょっとしたスペルミスや引数の間違いなどでもその箇所が実行されるまでなかなか気付く事が出来ない。この事から感じるストレスは開発が進めば進むほど大きな負担になって来る。
今回Herlockで開発しながら特にこの点が気になったのは、おそらく次に挙げる要因が関係していると思う。

2. Herlock Viewerでアプリのリロードに時間がかかる

Web IDE上でプログラムの変更を保存してから、それを端末上のデバッグ用アプリ(Herlock Viewer)で再読み込みするのに結構時間がかかる。自分の環境(米国からのアクセス)だと最低でも15秒、サーバー側が重くなっている様に感じられる時だと30秒から40秒程度待たされる事もある。

原因は、最初に全ての画像や音声のデータを読み込む様なプログラムになっているからという事もあると思う。ただ、サーバー側が重くなっている時などはプログラムの実行を開始する前にJSファイル(require.jsなど)の読み込みの段階でタイムアウトしてしまう事もよくある。

この辺りは、Herlock Viewerの方でJSファイルや画像・音声などのデータをキャッシュしてくれて、任意のタイミングで開発者がキャッシュの更新を指示出来る様に(もしくは変更されたものだけ自動で読み込む様に)してもらえれば改善される様な気がしないでもない。

また、Android版はまだマシだったが、iOS版のHerlock Viewerではアプリのリロード処理中にタイムアウトしてしまう確率がかなり高かった。(特にサーバー側が重い時)

3. アプリ内でのシーン管理やアセット管理のための標準的な方法が分からない

APIドキュメントには基本的なコードしか載っていないので、最初は戸惑った。
結局サンプルアプリの「ねこ穴」のコードを見よう見まねでいじりながらなんとか形にはなったけれど、シーン管理やアセット管理のためのクラスは標準で存在した方が有難い。その上でtmlib.jsのチュートリアルの様な平易なドキュメントがあればなお嬉しい。




● これからHerlockに期待したいこと


1. TypeScript対応

Web IDEがJavaScriptだけでなくTypeScriptにも対応してくれれば、上に書いた「実行するまでエラーが分からない」という問題はほぼ解決すると思う。


2. Herlock Viewerでの再ロード時間の短縮

キャッシュ対応やサーバー側のスペック向上などでなんとか「数秒程度」でリロードが完了する様にしてもらえないものだろうか。
再実行に15秒以上かかる様だと「すぐに実行して動作確認出来る」というJavaScriptの利点がほぼ相殺されてしまうので、なんとも惜しい気がする。


3. ローカル開発用SDK

コマンドラインのみのSDKで良いので提供してもらえれば、Web IDEを使わずにローカルで好きなエディタを使って開発出来る様になる。もしこれが可能ならぜひ実現してもらいたい。
PhoneGapで言う「phonegap serve」コマンドの様なものを実行するとPCが接続待ち状態になり、そこにHerlock Viewerで同一LAN内から接続出来る様になれば多分リロードに時間がかかるという問題も解消されるだろう。

ローカルにソースコードがあればGitなどでの管理も自由に出来るし、理想的な開発環境になるのではないだろうか。

4. ドキュメントの充実

HerlockのAPIドキュメントは見た目はなかなか良いのだけれど、内容はまだまだ充実させる余地があると思う。



● 今日の結論


- JavaScriptだけでネイティブアプリの開発が出来るHerlockはアプリ開発者ならぜひ試して見る価値あり。

- ただデバッグ実行時のリロード時間の短縮はなんとしても実現して欲しい。

- なんだかんだ言っても結局のところ非常に面白いし将来有望な開発環境だと思うので、これからのより一層の改善に期待!



以上!







 .

2014年8月24日

JavaScriptでスマホゲームアプリを作る - Monaca + tmlib.js編

● Monaca とは


Monacaの機能と特徴 | HTML5を使ったクロスプラットフォーム型モバイルアプリ開発 




● PhoneGap/Apache Cordovaとの違い


内部的にはPhoneGap/Cordovaを使っているので、ソースコードはPhoneGap/Cordova用に作ったものをほとんど変更せずに使える。

一番大きな違いは、Monacaだとローカルに開発環境を用意する必要が無い事だ。ブラウザさえあればどこからでもすぐにプロジェクトを開いて開発を続けられる。


先にPhoneGap用に作った15パズルのソースコードをMonacaのIDEにアップロードすると何の問題も無く実行出来た。

実機でのデバッグ実行は「Monacaデバッガー」というアプリをインストールして行う。


PhoneGap Developerのアプリと同様こちらもとても便利。さらに端末上でログ出力が確認出来たり、開発者のIDEに画面のハードコピーやメッセージを送ったりも出来るみたいだ。





● 結論


開発環境の違いはあるがソースコード的にはPhoneGap/Cordovaと同じものをほぼそのまま動かせる。

なのでAndroidではWebAudioを使うとかなりの確率で音が出ないという問題も同じだ。この問題があるのでゲームアプリを作るのには残念ながらちょっと適さない。 (追記参照)




● 追記


Monacaデバッガーの「ハイパフォーマンス版(4.0以降のみ対応)」というのがあったのでこちらで試した見た所、Androidでも音が鳴った。BGMも効果音も問題無しだ。やったー!
とは思ったものの、デバッガー経由でなくビルドしたものを端末でダウンロードしてインストールしてから動かすと、やっぱり音が鳴らない。これはビルド時の設定か何かで直るものなのだろうか。それともMonaca側がまだ最新の環境に対応していないのだろうか。良く分からないけれども何となく期待は持てそうだ。


Monacaサイトの説明によると、ハイパフォーマンス版のデバッガーは

  • 最新のChromiumベースのブラウザーエンジン(インテルのCrosswalk)を用いており、高速なレンダリングと高度なCSS、JavaScript APIを利用可能です。

との事なので、Chromiumのバージョンに関係がありそうだ。となると、ビルドして配布する場合はユーザーの端末に入っているChromiumブラウザーのバージョンに依存するという事だろうか。



● さらに追記 (2014年9月7日)


下のブログ記事を見つけたのでAndroid用のビルド設定を「ハイパフォーマンス (Crosswalkエンジン)」を使う様に変えて試してみた所、ビルドしたアプリでも問題無く音が鳴る様になった。

Crosswalkエンジンを組み込んでMonacaで作るAndroidアプリを一気に高速化させる。 : アシアルブログ 

APKファイルのサイズが19MBに大きくなったけれども、まあ許容範囲だろう。ともかくこれでAndroidでも音が出る様になったという事で、スッキリした。
Monaca素晴らしい!







 

 

2014年8月23日

JavaScriptでスマホゲームアプリを作る - PhoneGap + tmlib.js編

● tmlib.jsを使ってみた


tmlib.jsが面白そうなので初めて触ってみた。

このチュートリアルを読めば大体の使い方がつかめる。素晴らしい!
tmlib.js で今流行のフラットデザインを使ったタッチゲームを作ろう – Step00 ゲームのひな形を作ろう | TM Life 




● スマホでも結構使えそうな感じ


作ったものは平凡だけれどとりあえずこんな感じの15パズル。
動画はNexus7(2012)上で動かしたもの。




ブラウザ上でPlay出来るバージョンはこちら。



開発環境は、最初はVisual Studio 2013に
Multi-Device Hybrid Apps (Preview) for Visual Studio 
という拡張機能を入れて使おうと思ったのだけれど、入れてみるとまだちょっと使いづらい部分があったので今回はパス。

結局  Brackets というエディタとTerminalだけで十分だった。Bracketsには「ライブプレビュー」というボタンがあってそれを使うとすぐにChromeを開いて実行出来るのでこれがとても役に立った。

実機で実行したい時は、Terminalでプロジェクトのフォルダから

phonegap serve

を実行しておく。そして実機(Android, iOS, Windows Phone)上でPhoneGap Developerアプリを実行し、PCのIPアドレスを入力すればOK。実機とPCが同一ネットワーク内にあれば良いのでUSBケーブルをつなぐ必要が無いのが有り難い。
この状態でソースコードを変更して保存すれば1〜2秒で自動的に実機側のアプリを再起動してくれる。これは実際に使って見るとものすごく便利だ。複数の端末を同時に接続出来るので、Android、iPhone, iPad, Windows Phoneを全て接続してそれらが同時に更新されるのを眺めているとそれだけでも面白い。


ちなみにビルドしてパッケージ化されたものを動かしたい時は、実機をUSB接続した上で、

 phonegap run android --device  または phonegap run ios --device

を実行すればOK。



ソースコードはこちら。
https://github.com/mikehibm/15puzzle-phonegap







● あれ、Androidで音が出ない。。。


iPhone,iPadでは全く問題無くBGMと効果音が再生されるのだけれど、手元のGalaxy S3とNexus7では全く音が出ない。それどころか、ローディング画面が出たまま止まってしまう。デバッグすると、WebAudioのcontextがnullになっている。これは悲しい。色々試したけどAndroidではどうも音声はダメそうなので、tm.sound.WebAudio.isAvailableが trueでない時は一切音を鳴らさない様に変更した。この問題が無ければ十分使えると思っただけに、残念。



● 結論


- アニメーションはあまり複雑な事をやらなければスマホでも結構使える。
- PhoneGap Developerアプリを使えば開発が捗る。
- でもAndroidでは端末によってWebAudioへの対応がまちまちで、かなりの確率で音が出ないのがなんとも残念。
- iOSまたはPCをターゲットにして作るなら十分選択肢の一つになり得ると思う。
- tmlib.jsは便利!これからも使いたい。