以前作成した音ゲーのWEBアプリ版のレシピを公開したいと思います。フレームワークはcocos2d-JS、言語はCoffeeScriptを使用します.cocos2d-JSとは?cocos2d-JSはオープンソースの2DゲームフレームワークのJavaScript版です。JavaScriptでiOS、Androidの両方に対応したアプリが作れます。WEB用にビルドすることでブラウザ上で動作するアプリを作ることも可能です。以下は公式のデモですが、スマホのブラウザ上でもヌルヌル動くことが確認できると思います。コードに変換されるAltJSの一つです。概要は以下の記事を参照してください。blog.bokuweb.meどんなゲーム?『ノート』が曲に合わせて落ちてきます。『ターゲット』に重なるタイミングでタッチしてください。test準備node.jsのインストールCoffeeScriptやGulpを使用するためnode.jsをインストールします。公式ページからダウンロードしインストールしてください。Node.jsMacの場合、Homebrewを使ってインストールすることも可能です。以下のコマンドでバージョンが表示されればインストール完了です。-vGulpのインストールGulpはCoffeeScriptのコンパイルや結合などに使用します。以下のコマンドでインストールできます。-gcocos2d-JSのインストール以下の記事を参考にインストールしてください。現在のcocos2d-JSのバージョンは3.6です。記事はwindows環境での作業になっていますが、macでも同様です。基本的にはcocos2d-JSをダウンロードし、setup.pyを実行すればOKです。記事後半の「coffeescriptへの変換」は不要です。今回作成するサンプルプロジェクトを作成します。以下のコマンドを実行してください。「flava」がプロジェクト名になります。.完了後、「flava」ディレクトリが作成されていると思います。ディレクトリ内のmain.js、src/app.js、src/resource.jsを削除しておいてください。次に、以下のzip解凍し、中身を「flava」ディレクトリへコピー・上書きしてください。https://github.com/bokuweb/cocos2d-JS_skeleton/archive/gh-pages.zip「src」ディレクトリ内にapp.coffee、main.coffee、resource.coffeeが、ディレクトリのトップにgulpfile.coffee、package.jsonが格納されていればOKです。以下のコマンドを実行してください。iその後、以下のコマンドを実行してください。watchify正常に完了すればディレクトリのトップにmain.jsが生成されます。watchを実行することにより、src/*.coffeeに変更があるたびに自動的にmain.jsが生成されます。ctrl+cで終了できます。以下のコマンドを実行してください。webcocs2d-JSはディレクトリトップのmain.jsを実行します。ブラウザが起動し、以下のような画面が起動すれば動作OKです。今後使うことになる画像等のファイルをダウンロードして./resに放り込んでおいてください。ターゲットイメージディレクトリについていろいろディレクトリがありますが基本的には触るのはresとsrcです。ゲームのソースファイルを格納するsrcの中には現在以下のようなファイルがあると思います。アプリ部分ブラウザのキャッシュをOFFにしておく開発中はブラウザのキャッシュをOFFにしておいたほうがよいです。open)にチェックを入れておくとよいです。デベロッパーツールを開いている間のみキャッシュOFFになります。watchを実行しておいてください。変更後に以下のようにしてもOKです。watchifymain.coffeeの変更まずはmain.coffeeを編集します。5行目でapp.coffeeをrequireしています。いつまでもHelloWorldSceneじゃおかしいのでGameSceneにリネームします。同様に14行目もGameSceneにリネームします。次に11行目を次のように変更します。これはどんな解像度の端末であれ「横320px×480pxで描画しますよー」という仮想的な解像度です。この設定により端末による差異をなくし、同じように表示することができます。cc.ResolutionPolicy.SHOW_ALLは縦横比を保ったまま、はみださないように端末にフィットさせる設定です。他にも縦横比を無視して画面にフィットさせる設定や、縦横比を保ったまま短い辺に合わせる(長い辺ははみだす)設定などがあります。13~15行目でリソースのプリロードを行い、プリロード完了のコールバックでGameSceneに切り替えています。cc.directorは名前の通りでゲームのシーンなどを管理します。GameScene()でシーンをGameSceneに切り替えています。21~25行目ではGameSceneを定義し、GameSceneにGameLayerを追加しています。cocos2dでは『シーン』と『レイヤー』の概念があり、『スプライト』や『ラベル』をレイヤーに追加し、1つ以上の『レイヤー』を追加することで『シーン』を構築していくのが基本っぽいです。main.coffeeresource.coffeeの変更resource.coffeeは画像などのリソースを管理します。画像などを使用する場合は必ずこのファイルに追記する必要があります。ついでに今後使用する予定のリソースをすべて追記しておきます。何か独自に使用したい画像などがあれば現在のフォーマットに習って追加すればOKです。resource.coffeeこれで背景画像bg.jpgを使用することができます。app.coffeeの変更main.coffeeと同様HelloWorldSceneをGameSceneにリネームします。1行目でresource.coffeeをrequireしています。5行目のctorはコンストラクタです。GameLayer()としたときに実行されます。(ここでは24行目ですね)。World"を表示するラベルと背景画像の設置を行っています。14行目~19行目が背景画像配置処理です。15行目でアプリサイズの取得。16行目で背景画像bg.jpgを使用したスプライトを生成しています。"画像へのパス"でスプライトは生成できます。17~18行目でスプライトをアプリの中央に配置。19行目でGameLayerに追加しています。addChildの第二引数はzIndexすなわちZ方向の表示順序です。この場合"0"なのでGameLayerの最奥に配置され、"1"や"2"に設定されたスプライトやラベルの方が手前に表示されます。webブラウザが起動して以下のようになればOKです。タイマークラスの実装timer.coffeeの追加cocos2d-JSでは残念ながらBGMの再生時間を取得することができません。自身でタイマを作成し再生時間を計測する必要があります。タイマークラスは以下のようにしました。srcに追加してください。・timer.coffeestart()でタイマーを開始し、get()で開始からの経過時間を取得します。通常のスプライトはタッチ(クリック)しても何も反応しません。今回のゲームはタイミングよく『ノート』にタッチするゲームなのでタッチできるスプライトを作る必要があります。以下のようなTouchSpriteクラスを作成します。touchSprite.coffeeの追加3行目の@_superは継承元(この場合cc.Sprite)の同名のメソッド(この場合ctor)を呼び出しています。cc.Spriteのコンストラクタctorにスプライトに張り付ける画像を渡しています。4~8行目でタッチベントを登録しています。8行目でタッチ開始時に呼ばれるメソッドを登録しています。10~17行目はタッチ時の処理です。11行目~15行目はひとまずおまじないと思ってもらっていいと思います。タッチされた領域が一致しているか比較してるっぽいです。タッチされたときの処理は16行目以降にifの中に書けばいいです。手順3で作成したタッチスプライトを継承し『ノート』クラスを作ってみます。『ノート』の動作は以下です。設定した時間に設定したY座標(ターゲット)に到達するように移動するタッチされず一定時間経過した場合はフェードアウトするタッチされた場合、設定時間とタッチされた時間との差分を計算し、判定を行いリスナーに通知するこれらを実現していきます。5~7行目はコンストラクタです。引数でテクスチャ、第二引数で各種パラメータ、第三引数でタイマーを受け取っています。6行目でTouchSpriteにテクスチャを渡しています。7行目の@_listenersイベント(今回は判定イベント)が発生したときに実行するコールバックを保持する配列です。9~10行目は動作開始メソッドです。@scheduleUpdate()を呼ぶことで、60FPS(すなわち16.6ms毎)でupdateが実行されるようになります。17~32行目は60FPS(すなわち16.6ms毎)で実行されるupdateメソッドです。18~22行目で移動処理を行っています。18行目で現在時間の取得を行っています。19~20行目で現在時間が設定時間以上だった場合設定座標で停止するようにしています。21~22行目で現在時間と設定時間の差分よりY座標を算出し設定・移動しています。24~32行目では現在時間が設定時間+Good判定しきい値を超えた場合にフェードアウトする処理を行っています。25行目は外部に「BAD判定があったよー」と通知しています。26行目で@unscheduleUpdate()を呼び、updateを停止しています。32行目でアニメーションを実行しています。どんなアニメーションかを28行目で設定してます。cc.sequenceは処理を順番に実行していきます。(ちなみに同時に実行するのは38行目のcc.spawnです。thisでコールバックcbを実行します。onとありますので、親要素から自身を削除します。「0.2秒でフェードアウトした後、親要素から自身を削除する」という処理を24~32行目で行っています。34~47行目でタッチ時の処理を行っています。36行目で判定処理をおこなっています。1.5が同時に実行されるようにしています。これにより、0.2秒かけてフェードアウトしながら、1.5倍に拡大するアニメーションが実行されます。49~59行目は判定処理です。現在時間を取得し、設定時間との差分を計算。パラメータで受け取った判定しきい値との比較を行い、判定結果を_triggerメソッドでリスナーに通知しています。61~64行目はイベント通知処理です。@_listenersに格納されたコールバックを実施します。『ノート』クラスを使ってみます。app.coffeeで『ノート』を作成し、動かしてみます。1~3行目で各クラスをrequireします。10~16行目で各種『ノート』のパラメータを設定します。今回は再生時間が4秒の時に目標座標0pxに500px/secで到達するように設定しています。cocos2dの場合、アプリの左下が座標(0,0)になります。±0.2secでGREAT判定、±0.4secでGOOD判定、それ以外はBADとなるよう設定しています。18行~23行目で『ノート』を作成しレイヤーに追加しています。24行目で判定が行われたときに呼び出されるリスナーを登録します。今回は判定が行われると、_onJudge()が呼ばれるよう登録しておきます。25行目で『ノート』の動作を開始します。26行目でタイマーを開始します。28~29行目は上述した、判定時に呼ばれるメソッドで今回は判定結果をコンソールに表示します。webこんな感じになります。判定結果がコンソールに表示されます。現状のままだと高解像度端末で起動した場合、画像がぼやけてしまうらしいです。対策として、画像は解像度が倍(以上)のものを用意する必要があります。そのまま解像度が倍のものを使用すると倍のサイズで表示されてしまいます。2により、画像などの表示サイズを1/2にしてくれます。11行目に追加しています。動かしてみると落ちてくる『ノート』が1/2になっていることが確認できると思います。『ノート』の『ターゲット』を描画します。6~9行目で各種パラメータを設定しておきます。#『ターゲット』のy座標60px43~50行目が描画メソッドです。13行目で呼ばれています。・app.coffee動かしてみると『ターゲット』が描画されているのが確認できると思います。『ノート』を『ターゲット』に向けて落下させるmusic.coffeeの追加まずは再生する音楽に関する情報を記述したmusic.coffeeを用意します。2~5行目にはファイルパスやカバーイメージ、再生終了時間などを記載しておきます。6行目以降は何秒にどの『ターゲット』へ到達させるかを記載しています。keyが0の場合は一番左に、keyが4の場合は一番右に落ちてきます。app.coffeeの変更music.coffeeで設定した内容に沿って動作するよう修正します。11行~14行目で落下速度や判定しきい値を設定しています。35行~46行目で落下してくる予定の『ノート』を作りおきしておきます。ゲーム中にスプライトを作成するとメモリを確保のためにGC(ガベージコレクション)が実行される可能性があるためです。GCが走っている間、プログラムが停止するため、アニメーションがカクカクになったりします。このためメモリはゲーム中に確保したり破棄するのではなく、初期化時に極力確保しておきGCの回数を最小限にする必要があるそうです。このあたりが参考になります。www.html5rocks.comここでは一旦画面外に描画しておき、配列にpushしておきます。手順4でも記述しましたが、updateはscheduleUpdate()が呼ばれることでおよそ60FPSで実行されるメソッドです。動作を開始すべき時間を計算し、必要に応じて動作の開始を行います。・app.coffee動かしてみると『ターゲット』に向かって『ノート』が落下していくの確認できると思います。終了時に表示するゲームオーバーシーンの追加を行います。Over”と表示するのみのシーンです。・gameOver.coffeeapp.coffeeの変更5行目でgameOver.coffeeをrequireします。36行~39行目にゲーム終了処理を追記しています。update内で現在時間がmusic.coffeeに設定したplayTimeを超えていないかモニタし超えていたら終了処理を行います。37行目でゲームオーバーシーンを作成しています。38行目でupdateを停止しています。39行目でゲームオーバーシーンにシーンを切替えています。gameOver)を渡すことで1.2秒でフェードしながらシーンを切替えることができます。Over"と表示され、シーンが切り替わっていることを確認できると思います。スタートメッセージの追加と楽曲の再生を実装するapp.coffeeの変更スタートメッセージをタッチすることでゲームが開始するようにします。主な追加箇所は79~110行目です。_addStartButtonではスタートメッセージの追加を行っています。基本的には画面中央にメッセージを表示するだけなんですが、cc.sequenceとcc.RepeatForeverを使用することで1秒間隔で永久に点滅するようにしています。90~94行目でタッチイベントを登録しています。メッセージがタッチされると、96行目の_onTouchStartが実行されます。_onTouchStartはごちゃごちゃしていますが、ほぼタッチのための処理であり、基本的には102行目の”自身を親から削除する”と103行目の”楽曲を再生する”がメインの処理です。107~110行目の_timerStartIfMusicPlayingではBGMが再生中か判断し、再生中であればタイマーを開始しています。_onTouchStart内で再生していますが、@_music.playMusicから実際に再生が始まるまでタイムラグがあるようなので(環境によるものかもしれませんが)このようにしています。この方法でも多少のずれは発生しますが、無視できるレベルだと思います。スケジュールは27行目でセットしており、タイマ開始とともにうスケジュールをクリアしています。これで一応最低限遊ぶことができるようになりました。スコアの計算と表示を行うapp.coffeeの変更スコアの計算と表示を行います。118~125行目でスコアラベルの追加を行っています。_onJudge()で判定結果に合わせてスコアの計算を行っています。すべてGreat判定の場合100000点、すべてgoodの場合70000点となるように計算しています。判定結果の表示を行います。133~141行目で判定表示ラベルの追加を行っています。_onJudge()で判定結果に合わせて判定表示ラベルの文字列を変更しています。変更後、_showJudgeLabel()により一定期間判定表示ラベルを表示します。seqで0.2秒かけてフェードイン、1秒かけてフェードアウトさせています。楽曲カバーイメージとタイトルの表示を行うapp.coffeeの変更もはや説明不要かと思いますが、カバーイメージとタイトルの表示を行います。149行~163行目の_addCoverImage()、_addTitle()で表示を行っています。・app.coffee以上です。一応音ゲーっぽくはなったと思います。リポジトリは以下です。いじって遊んでみてください。github.comさいごに実はなんですが、もともとAndroid/iOS両対応のアプリを作る記事として書き始めたんですが現状の書き方だと、ネイティブ動作でうまくいかないこが分かり、WEBアプリの記事にピボットしました。ネイティブ用に修正する気力がもてなかったため、公開もやめようと思いましたが、途中まで書いていたこともあり、中途半端ながら公開することとします。『ノート』のタッチイベントの登録のところで上手くいっていないと思います。sampleのTouchesTestも何故かネイティブ動作NGになっていますので多分問題があるのだと思います。iosし、ソースと比較しながらいろいろ試してみてください。Android版とそのソースも公開しているので参考にしてみてください。bmsjs(α版)というWEBサービスをリリースしました。12引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。