cocos2d-JS Liteとenchant.jsのパフォーマンスを比較してみた
最近enchant.jsとcocos2d-JSの両方使ってるんだけど。cocos2d-JSのほうが比較的さくさく動くと感じたので実際どれくらい差があるのか比較してみた。
比較対象は以下
cocos2d-JS Lite v3.5
enchant.js v0.8.2
結果は以下のページを参照。グラフはc3.jsで書きました。スプライト数をどんどん増やしていってFPSがどう変化するか比較しました。高負荷環境においてcocos2d-JSはenchant.jsの倍程度のFPSがでていました。cocos2d-JSの方がenchant.jsより学習コストも高く、小回りが効かない印象がありますがパフォーマンスは出やすいようです。自分はenchant.jsも好きなので用途に応じて適材適所で。
Cocosjslite vs enchant by bokuweb

比較ソースは以下。
enchant.js(coffee)
cocos2d-JS(JS)
enchant.jsでゲーム内にhtmlを埋め込む方法
以下のようにすることでhtmlがゲーム内に埋め込めます。他のspriteなどと同様に扱えます。 以下の例ではinput要素が1pxずつ右に移動します。
html = new Entity() html._element = document.createElement 'div' html._element.innerHTML = '<input type="text" name="" value="text">' html.width = 200 game.rootscene.addChild html html.addEventListener "enterframe", ()-> @x += 1

- 作者: 蒲生睦男
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2013/01/19
- メディア: 単行本(ソフトカバー)
- クリック: 2回
- この商品を含むブログ (4件) を見る
AngularJS+enchant.js+CoffeeScriptで音ゲーを作ってGitHubPages上で公開しました
これなに?
ブラウザ上で遊べる音ゲーです。 ノートと呼ばれるオブジェクトが曲に合わせて落ちてくるのでターゲットに重なるタイミングでキーボードのZ,X,C,V,Bを押していくゲームです。 楽曲はCreativeCommonsのものを使用しています。
なんで作ったの?
Vue.jsやReactやAngularJSやAurelia.JSなどフレームワークが乱立するなか何か一つ使えるようにならないといけない気がしたので勉強用に始めました。(あとcocos2d-JSのAndroidデバッグが嫌になりましたので気分転換に)。最初はAngularJSより学習コストの低そうなVue.jsを触ろうかと思ったのでですが、Vue.jsにはルーティング機能がついていないということで結局AngularJSを触ってみることにしました。(書籍も買っちゃってたし。)
本来ゲームであればenchant.jsでシーンの切替え等を行い、AngularJSなどを使わないってのが普通な気がしますが、今回のような曲選択ページなんかはゲーム側でjavascriptで記述するよりhtmlとして表示してやったほうが簡単だとおもったので・・。実際cocos2d-JSで同じようなメニューページをjavascriptで書くのは少し面倒に感じました。
後はおもけに以下のことを試したみました。
- webstorage使ってみる(ハイスコアの記録に使っています)
- GitHubPages上で公開してみる(無料だし独自ドメイン使えるし)
リポジトリ
はまったこととか、こんな風にやってみましたってこととか
AngularJSは情報が多いとはいえ、やはり手さぐりでいろいろ調べながら試しました。『こうすべきだ!』ってのがあったらご教示いただけると幸いです。
GitHub Pagesで独自ドメインを使用する
以下のページが非常に参考になりました。というかまんまです。
GitHub Pages でWebサイトをホスティングする(独自ドメイン使用) - Think Big Act Local
AngularJSでルーティングする
以下のページが参考になりました。
inter-arteq :: interaction between art and technology » Blog Archive » AngularJSで画面遷移するときに便利なng-view
index.htmlに以下のように記述しておき
<div ng-view id="view"></div>
JS側で以下のように振り分けています。
flavaApp.config ($routeProvider)-> $routeProvider .when '/', templateUrl: 'splash.html' .when '/select', templateUrl: 'select.html' .when '/help', templateUrl: 'help.html' .when '/game/:id', controller: 'GameCtrl' templateUrl: 'game.html' .otherwise redirectTo: '/'
また、URLのパラメータを取得するのには以下のページを参考にさせていただきました。
$routeParamsを使用して、URLのパラメータを取得する | 集の一期一会
具体的には
flavaApp = angular.module('flavaApp', ['ngRoute']) flavaApp.config ($routeProvider)-> $routeProvider .when '/game/:id', controller: 'GameCtrl' templateUrl: 'game.html' flavaApp.controller 'GameCtrl', ($scope, $routeParams)-> console.log $routeParams.id
とするこでhttp://prototype.flavabeats.net/#/game/1にアクセスすることで$routeParams.idが1になります。これを使って、ゲームプレイ時はすべてgame.htmlに遷移しつつ、idによってどの楽曲をプレイするか選択しました。
外部ライブラリのコールバックの中等で $scope の値を変更したい場合
以下の記事が参考になりました。
AngularJSでデータバインドが効かないときは $scope.$apply - Yuta Watanabe's Blog
リンク先の記事でも紹介されている、2秒後に$scope.messageを書き換える例ですが以下の記述では動作せず。
function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function () { $scope.message = "Timeout called!"; // AngularJS unaware of update to $scope }, 2000); }
以下のように各必要があるようです。
function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function () { $scope.$apply(function () { $scope.message = "Timeout called!"; }); }, 2000); }
HTMLとして文字列をバインドする
以下の記事が参考になりました。
3分で分かるAngularJSセキュリティ - teppeis blog
今回はangular-sanitize.jsを使用しました。
以下のようにレベルをhtmlで表示しています。
html
<span class="level" ng-bind-html="m.levelIcon"></span>
JS側
flavaApp = angular.module('flavaApp', ['ngSanitize']) flavaApp.controller 'GameInfoCtrl', ($scope)-> level = 'Level ' for i in [0..9] when i < $scope.music.level level += '<i class="fa fa-star-o level"></i>' $scope.music.levelIcon = level
topページでスマホ用にメッセージを出す
性懲りもなくスマホには対応していないので、スマホユーザには非対応メッセージを出すようにしました。
userAgentを判定して、ng-hideとng-showで要素を表示・非表示にして対応してます。
html
<div class="row" ng-controller="SplashCtrl"> <div class="col-md-8 col-md-offset-2 text-center inner" ng-hide="isMobile"> <a ng-href="#/select"><h1><img src="img/logo.png" ></h1> <span class="click-message">Click here to start.</span></a> </div> <div class="col-md-8 col-md-offset-2 text-center inner" ng-show="isMobile"> <h1><img src="img/logo.png" class="sp-logo"></h1> <span class="mobile-message">Sorry, This site is designed specifically for PCs.</span> </div> </div>
JS側
flavaApp.controller 'SplashCtrl', ($scope)-> agent = navigator.userAgent if agent.search(/(iPhone|iPod|Android|Mobile)/) isnt -1 then $scope.isMobile = on else $scope.isMobile = off
webstorageを使う
こんな感じで保存
storage = localStorage storage.setItem key, value
こんな感じで読み出し
storage.getItem key
できるっぽいです。簡単。
さいごに
インクリメンタルサーチとかソートとかさくっと実装できて楽しかった。もっと勉強してVue.jsやAurelia.JSも触ってみたい。あと、せっかくなんだからBrowserify使ってみればよかった。
※追記
ゲームへの使用例が以下の記事に掲載されていました。
TimelineFX買ったった!ので、サンプルエフェクトをenchantで表示するまでを記録しておく
ゲームのエフェクトを作るためにTimelineFX買いました。サンプルのスプライトシートをenchant.jsで動かすまでを記録しておきます。
TimelineFX?
価格:£29.99
画像から色や速度のなどのパラメータを変更することでいい感じのエフェクトがさくっと作れちゃうEditorです。サンプルが非常に豊富で基本的にはサンプルを元に手を入れたり、参考にすることで効率よく目的のものが作れそうです。どんなサンプルがあるか貼っときます。
Auras and Halos Effects Library for TimelineFX - YouTube
Readouts Effects Library TimelineFX - YouTube
Explosions Library for TimelineFX - YouTube
Lightshows Effects Library for TimelineFX - YouTube
サンプルエフェクトをスプライトシートとして書き出してみる
- サンプルエフェクトをダウンロードしておく
http://www.rigzsoft.co.uk/effects-libraries/
ここから好きなエフェクトをダウンロードしておきます。
- 立ち上げてフォルダマークをクリック

- 落としたエフェクトを選択 使いたいエフェクトを選択し「OK」を押す

- 使用するエフェクト選択

ここで"Attributes"を変更したりすることでサンプルをカスタマイズできるっぽいんですが、まだよくわかってないので分かり次第記事にします。今回はそのまま使用するため、右クリック->『Animation Properties』を選択。

- スプライトシートにエクスポート 右下のボタンを押すと『Export Options』が表示されるのでOKを押す。

- こんなのができる

enchant.jsでクリック時にエフェクトを表示する
enchant.jsでクリック時にエフェクトを表示する
・main.coffee
enchant() game = new Core(480,480) game.fps = 60 game.preload("http://jsrun.it/assets/r/K/E/a/rKEaB.png") game.start() game.rootScene.addEventListener Event.TOUCH_START, (e)=> wave = new Sprite(128, 128) wave.x = e.x - 64 wave.y = e.y - 64 wave.image = game.assets["http://jsrun.it/assets/r/K/E/a/rKEaB.png"] game.rootScene.addChild(wave) wave.addEventListener "enterframe", ()=> if wave.frame < 22 then wave.frame++ else game.rootScene.removeChild(@)
デモ
クリックしたらエフェクトが表示されます。
その他
日本語の情報がないのがきついけど、結構好感触。もう少しカスタマイズできるように勉強する。
【メモ】enchant.jsで表示順序を制御する方法
備忘録です。enchant.jsでzindexをどうやって設定するかわからなかったんですが、普通に_styleで設定するみたいです。
15.03.25追記 どうやら現在のverでは下記方法は使用できないようです。 insertBeforeが実装されているため、そちらで順序を制御することになります。
var sprite = new Sprite(4, 16); sprite.image = game.assets["sprite.png"]; sprite._style.zIndex = 2; game.rootScene.addChild(sprite);
参考記事
ASCII.jp:enchant.jsで懐かしのインベーダーゲームを作ろう (1/4)|古籏一浩のJavaScriptラボ
100行で書けるブラウザで動作するyoutube音ゲーの作り方
はじめに
【WEBサービス】youtubeを使った音ゲー×タッチタイピングサービスを作ってみた【つくってみた】 - ぼくのかんがえたさいきょうのうぇぶさーびす
上記の記事で書いた「typebeats」のゲーム部を簡素化して説明してみます。 削っていくと100行に入りそうだったので詰め込んでみました。 かなり簡素化していますが、基本的な考え方は共通です。
ゲーム概要
リズムに合わせて降ってくるアイコンをタッチ/クリックするゲームです。 画面をタッチ/クリックするとゲームが開始します。
こいつが

こいつに

重なるくらいがタッチ/クリックするタイミングです。
スマホでの動作は確認していません。たぶん動かない気がします。
言語はCoffeeScript、フレームワークはenchant.jsです。 触れたことのない方はどっとインストールも参考にしてください。
CoffeeScript入門 (全13回) - プログラミングならドットインストール
enchant.js入門 (全12回) - プログラミングならドットインストール
リポジトリとデモ
リポジトリ
bokuweb/youtube_music_game · GitHub
デモ
http://bokuweb.github.io/youtube_music_game/
準備するもの
今回必要なものは以下のようになります。
- ブラウザ(chromeを推奨)
- エディタ(CoffeeScriptのシンタックスハイライトができるものがおすすめ)
用語
| 項目 | 説明 |
|---|---|
| ノート | リズムに合わせて落下してくるオブジェクト |
| ラベル | 文字を表示する部品 |
| シーン | タイトル画面、ゲーム画面、ポーズ画面、ゲームオーバー画面など、「○○画面」と呼ばれる単位 *1 |
| FPS | Frame Per Sec 1秒間に何回画面が更新されるか |
*1 http://www.atmarkit.co.jp/ait/articles/1304/01/news034.htmlより ここではゲーム画面しかないのでシーンと言ったらゲーム画面のことです。
ゲーム詳細
ノートがタッチ/クリックされるべきタイミングを_timingに格納しておき、この時間から逆算して、ノートをシーンに追加したり、動かしたりさせています。
enchant.jsの機能で指定したFPSの周期で動作させるメソッドを登録できますので、この登録したメソッド内で何を行うべきなのか判定し、実行するという形になります。
ゲームクラス定義部
ここではclass内で共通して参照する変数などを定義しておきます。
class @Game # ゲームクラスを宣言します。 # class名に@を付けることでglobalスコープに配置され、別ファイルから参照することができるようになります。 YOUTUBE_ID = 'HNYkOJ-T63k' # 再生する動画のIDを設定します。 _game = null # echant.jsのゲームコアを格納しておくprivate変数です。 _yt = null # youtube API制御classのインスタンスを格納しておく変数です。 _judge = null # タイミング判定を表示するラベルを格納しておく変数です。 # 落下してくるオブジェクトのタッチ/クリックタイミングを定義します。単位は[sec]です。 # ここに定義してある時間と実際にタッチ/クリックした時間を比較し、「GREAT」、「BAD」などの判定を行います。 _timing = [6.14,7.486,8.155,9.977,10.377,11.611,12.062,12.765,13.583,13.945,14.223,14.707,15.059,16.241,16.577,17.425,20.186,20.917,21.593,22.313,22.449,23.123,24.297,24.965,25.113,25.464,26.148,26.635,27.294,28.103,30.910,31.601,32.305,33.024,34.054,34.786,35.360,36.140,37.028,38.402,38.829,39.129,40.354,41.051,41.553,42.233,43.043,43.729,44.261,45.705,46.448,47.416,48.407,50.158,51.310,52.363,53.031,54.417,55.288,55.640,56.472,57.190,58.110,59.095,59.648,60.776, 61.993, 62.370, 63.072, 63.808, 64.493, 65.111, 65.688, 66.414, 67.192, 68.891, 69.209, 69.918, 70.056, 71.111, 71.744, 72.428, 72.861, 73.263, 73.755, 74.099, 74.639, 75.090, 75.426, 75.941, 76.472, 76.992] _timingIndex = 0 # _timing配列用のindexです _status = "stop" # ゲームのステータスです _endTime = 80 # ゲームの終了時間を定義します。 # ここでは再生時間が80secを超えたら終了処理に入ります。
コンストラクタ
クラスからnewでオブジェクトを作成した際に、自動的に実行されます。 このサンプルでは一番最後の行でnewしていますのでその時に実行されます。 constructor内では以下の処理を行っています。
- ゲーム本体の生成
- FPSの設定
- 事前にロードするリソースの設定
- ゲームの開始
constructor : (parms)-> enchant() # enchant.jsを使うためのおまじないです _game = new Core(800, 600) # ゲーム本体を生成します。Coreの引数はゲームのwidthとheightです。 # ここでは800×600のゲームを生成しています。 _game.fps = 30 # frame per sec つまり1秒当たり何回描画するかを設定します。 _game.preload("icon.png", "shadow.png") # 使用する画像ファイルをpreloadするよう設定しておきます。 _game.start() # ゲームを開始します。 # ただ、いきなりゲームが開始されるわけではなくまずは画像のロードが行われます。 # ロードが完了すると_game.onloadメソッドが呼ばれます。
ロード完了時の処理
ロードが完了すると_game.onloadメソッドが呼ばれます。 onloadメソッド内では以下の処理を行っています。
- ゲームタッチ/クリック時の処理を登録
- 動画の埋め込み
- 判定ラベルの追加
- 着地ポイントのs追加
_game.onload = -> # 画像等のロードが完了すると実行されるメソッドです # ゲーム画面をタッチ/クリックしたときの処理を登録しておきます。 _game.rootScene.addEventListener "touchstart", (e)-> if _yt.isReady() # youtubeプレイヤーが準備できている? _game.rootScene.addEventListener "enterframe", _proccesRootSceneFrame # 30FPSすなわち1/30 = 33.3333..ms周期で呼び出されるメソッドを登録しておきます。 # 33.3333..ms経過するたびに_proccesRootSceneFrameメソッドがコールされます。 _status = "playing" # ステータスをplay中に変更 _yt.play() # youtube動画を再生します。 # 動画を埋め込みます video = new Entity() video._element = document.createElement('div') video.x = 500 # 動画設X置座標 video.y = 300 # 動画設置Y座標 # iframeタグを埋め込みます。 # https://www.youtube.com/embed/HNYkOJ-T63k?enablejsapi=1..のHNYkOJ-T63kが動画IDです。 # ここを変更することで動画を変更できます。 # その他詳細はhttps://developers.google.com/youtube/js_api_reference?hl=jaを参照してください。 video._element.innerHTML = '<iframe src="https://www.youtube.com/embed/'+YOUTUBE_ID+'?enablejsapi=1&controls=0&showinfo=0&autoplay=0&rel=0&vq=small" width="300" height="200" frameborder="0" id="player"></iframe>' _game.rootScene.addChild(video) # 動画をシーンに追加 _yt = new Yt() # youube制御インスタンスを生成 # 「GREAT」などの判定結果を表示するラベルを生成 _judge = new Label() _judge.font = "36px Arial" # サイズ、フォントを指定 _judge.x = 100 # X座標 _judge.y = 100 # Y座標 _game.rootScene.addChild(_judge) # シーンに追加 # 落下してくるオブジェクトの着地ポイントを示すイメージを設置する shadow = new Sprite(80, 80) shadow.image = _game.assets["shadow.png"] # 画像ファイルを指定 shadow.x = 100 # X座標 shadow.y = 380 # Y座標 _game.rootScene.addChild(shadow) # シーンに追加
ノート生成部
_isNoteGenerateTiming はノートを作成するかどうか問い合わせるメソッドです。
返り値がtrueの場合ノートを生成するタイミング、falseの場合は生成するタイミングではないことを示します。
動画の再生時間がタッチ/クリックタイミングから1秒減算した値より大きければtrueを返します。
すなわちタッチ/クリックタイミングから逆算して約1秒前にノートを生成しています。
_isNoteGenerateTiming = -> if _timing[_timingIndex]? # 配列_timingにデータが存在するかしらべます。 if _yt.getCurrentTime() > _timing[_timingIndex] - 1 # タッチ/クリックタイミングの1秒前? return true return false
_generateNote は引数で指定された番号のノートを作成し、ノートの動作設定、33.3333..ms周期(すなわち30FPS)で呼び出されるメソッドを登録、ノートをタッチ/クリックしたとき呼び出されるメソッドを登録を行います。
_generateNote = (number)-> # ノートの生成部 note = new Sprite(80, 80) note.image = _game.assets["icon.png"] note.number = number note.x = 100 note.y = -100 # 画面外に配置 note.timing = _timing[number] #タッチ/クリックタイミングnote.timingに設定しておく _game.rootScene.addChild(note) # シーンに追加 # ノートがどのように動作するのかを記述します。 note.tl.setTimeBased() # tl.setTimeBased()を実行することでenchant.jsのアニメーションを時間ベースで実行することができます。 # デフォルトはフレームベースであるため「何秒間でここからここまで移動」というような処理ができないためです。 # ノートのY座標を現在の位置(すなわち-100px)から380pxまで```_timing[number] - _yt.getCurrentTime()) * 1000```msecかけて移動するよう設定しています。 # ```_timing[number]```という目標時間から```_yt.getCurrentTime()```という現在時間の差を求めどれだけの時間で移動すべきかを計算しています。 note.tl.moveY(380, (_timing[number] - _yt.getCurrentTime()) * 1000) # ノートをタッチ/クリックしたとき呼び出されるメソッドを登録を行います。。 note.addEventListener "touchstart", (e)-> @clearTime = _yt.getCurrentTime() # タッチ/クリック時の時間をclearTimeとしてnoteのプロパティに登録しておきます。 @clear = true # クリアフラグをたてます。 # 33.3333..ms周期(すなわち30FPS)で呼び出されるメソッドを登録 note.addEventListener "enterframe", -> # タッチ/クリックタイミングから1秒以上経過していればノートをシーンより削除します。 if _yt.getCurrentTime() > _timing[@number] + 1 then _game.rootScene.removeChild(@) # クリアフラグが立っていれば if @clear # ここでノートをタッチ/クリックしたときの処理を記載します。 # 以下の処理も33.3333..ms周期で実行されるため、33.3333..ms毎にノートを透明にしていく処理とノートを大きくする処理を記述しています。 @opacity -= 0.2 # 不透明度を減らしていきます。 @scale(@scaleX + 0.05, @scaleY + 0.05) # 5%ずつスケールアップさせる # ノートが完全に透明になったら if @opacity <= 0 _game.rootScene.removeChild(@) # シーンから削除します。 # ```note.addEventListener "touchstart", (e)->```内で登録したタッチ/クリック時の時間とタッチ/クリックタイミングを比較し±0.2秒であれば"COOL"、±0.4秒であれば、"GOOD"、それ以外は"BAD"としています。 if -0.2 <= @clearTime - _timing[@number] <= 0.2 then _judge.text = "COOL" else if -0.4 <= @clearTime - _timing[@number] <= 0.4 then _judge.text = "GOOD" else _judge.text = "BAD"
メインフレーム部
_proccesRootSceneFrame は33.3333..ms周期(すなわち30FPS)で呼び出されるメソッドです
_game.onload 内で登録されています。
処理内容は以下です。
- ノートを生成すべきであればノートを生成
- 再生時間が終了時間を超えていればゲーム終了処理を行う
_proccesRootSceneFrame = -> if _status is "playing" if _isNoteGenerateTiming() _generateNote(_timingIndex) _timingIndex++ # 再生時間が終了時間以上? if _yt.getCurrentTime() >= _endTime # 動画のボリュームを落としていく。 _yt.setVolume(_youtube.getVolume() - 1) # ボリュームが0になったら再生終了し、ステータスを"end"に設定 if _yt.getVolume() <= 0 _yt.stop() _status = "end"
youtube制御クラス
動画制御クラスですが、公式のサンプルをclassに閉じ込めただけなので詳細は省きます。 詳細はhttps://developers.google.com/youtube/js_api_reference?hl=jaを参照してください。 以下のメソッドで動画を制御可能です。
- play : 動画を再生
- getCurrentTime : 再生時間を取得
- setVolume : 音量を設定
- getVolume : 現在の音量を取得
- isReady : Youtubeプレイヤーの準備ができているか問い合わせる
class @Yt _player = null _isReady = false _state = null constructor : (parms)-> tag = document.createElement('script') tag.src = 'https://www.youtube.com/iframe_api' firstScriptTag = document.getElementsByTagName('script')[0] firstScriptTag.parentNode.insertBefore(tag, firstScriptTag) play : -> _player.playVideo() getCurrentTime : -> _player.getCurrentTime() setVolume : (volume)-> _player.setVolume(volume) getVolume : -> _player.getVolume() isReady : -> _isReady onPlayerReady = -> _isReady = true window.onYouTubeIframeAPIReady = -> _player = new YT.Player 'player', events: 'onReady': onPlayerReady
実行部
newすることにより、constructor 、_game.onload の順にコールされ、ゲームが開始されます。
new Game()
コンパイル
書き終えたらmain.coffeeをコンパイルしてください。 main.jsが生成されますのでこれをhtmlファイルで読み込みます。 コンパイルは公式サイトの「TRY COFFEESCRIPT」からも可能です。たぶん。
html
<html> <head> <meta charset="utf-8"> </head> <body> <div id="enchant-stage"></div> <script type="text/javascript" src="enchant.js"></script> <!-- enchant.jsは予めダウンロードしておいてください--> <script type="text/javascript" src="main.js"></script> <!-- 今回書いたものをコンパイルしたもの --> </body> </html>
注意点など
この程度であればなんとか動作すると思いますが、規模が大きくなってくると現状の作りではパフォーマンス面で苦しくなってくるかと思います。 簡素化のため現状メモリの管理はまったく気にせず、好き放題やっていますが、このようなつくりの場合GC(ガベージコレクション)が、がしがし走るかとおもいます。 GCが走っている間はユーザのプログラムは止まった状態となるので、動きがカクカクしたり、一瞬止まったりする原因になります。 ここでは割愛(というか自分もよくわかっていない)しますがゲームを作る上では必要な知識となります。 本格的に勉強される方は以下の記事も参考にしてみてください。
wise9 › enchant.jsで3Dゲームを作ってみる!
オブジェクトプールを使った静的メモリ JavaScript - HTML5 Rocks
スプライトをプールして使い回す - enchant.jsのTipsを集めるWiki
ソース
main.coffee
class @Game YOUTUBE_ID = 'HNYkOJ-T63k' _game = null _yt = null _judge = null _timing = [6.14,7.486,8.155,9.977,10.377,11.611,12.062,12.765,13.583,13.945,14.223,14.707,15.059,16.241,16.577,17.425,20.186,20.917,21.593,22.313,22.449,23.123,24.297,24.965,25.113,25.464,26.148,26.635,27.294,28.103,30.910,31.601,32.305,33.024,34.054,34.786,35.360,36.140,37.028,38.402,38.829,39.129,40.354,41.051,41.553,42.233,43.043,43.729,44.261,45.705,46.448,47.416,48.407,50.158,51.310,52.363,53.031,54.417,55.288,55.640,56.472,57.190,58.110,59.095,59.648,60.776, 61.993, 62.370, 63.072, 63.808, 64.493, 65.111, 65.688, 66.414, 67.192, 68.891, 69.209, 69.918, 70.056, 71.111, 71.744, 72.428, 72.861, 73.263, 73.755, 74.099, 74.639, 75.090, 75.426, 75.941, 76.472, 76.992] _timingIndex = 0 _status = "stop" _endTime = 80 constructor : (parms)-> enchant() _game = new Core(800, 600) _game.fps = 30 _game.preload("icon.png", "shadow.png") _game.start() _game.onload = -> _game.rootScene.addEventListener "touchstart", (e)-> if _yt.isReady() _game.rootScene.addEventListener "enterframe", _proccesRootSceneFrame _status = "playing" _yt.play() video = new Entity() video._element = document.createElement('div') video.x = 500 video.y = 300 video._element.innerHTML = '<iframe src="https://www.youtube.com/embed/'+YOUTUBE_ID+'?enablejsapi=1&controls=0&showinfo=0&autoplay=0&rel=0&vq=small" width="300" height="200" frameborder="0" id="player"></iframe>' _game.rootScene.addChild(video) _yt = new Yt() _judge = new Label() _judge.font = "36px Arial" _judge.x = 100 _judge.y = 100 _game.rootScene.addChild(_judge) shadow = new Sprite(80, 80) shadow.image = _game.assets["shadow.png"] shadow.x = 100 shadow.y = 380 _game.rootScene.addChild(shadow) _isNoteGenerateTiming = -> if _timing[_timingIndex]? if _yt.getCurrentTime() > _timing[_timingIndex] - 1 return true return false _generateNote = (number)-> note = new Sprite(80, 80) note.image = _game.assets["icon.png"] note.number = number note.x = 100 note.y = -100 note.timing = _timing[number] _game.rootScene.addChild(note) note.tl.setTimeBased() note.tl.moveY(380, (_timing[number] - _yt.getCurrentTime()) * 1000) note.addEventListener "touchstart", (e)-> @clearTime = _yt.getCurrentTime() @clear = true note.addEventListener "enterframe", -> if _yt.getCurrentTime() > _timing[@number] + 1 then _game.rootScene.removeChild(@) if @clear @opacity -= 0.2 @scale(@scaleX + 0.05, @scaleY + 0.05) if @opacity <= 0 _game.rootScene.removeChild(@) if -0.2 <= @clearTime - _timing[@number] <= 0.2 then _judge.text = "COOL" else if -0.4 <= @clearTime - _timing[@number] <= 0.4 then _judge.text = "GOOD" else _judge.text = "BAD" _proccesRootSceneFrame = -> if _status is "playing" if _isNoteGenerateTiming() _generateNote(_timingIndex) _timingIndex++ if _yt.getCurrentTime() >= _endTime _yt.setVolume(_youtube.getVolume() - 1) if _yt.getVolume() <= 0 _yt.stop() _status = "end" class @Yt _player = null _isReady = false _state = null constructor : (parms)-> tag = document.createElement('script') tag.src = 'https://www.youtube.com/iframe_api' firstScriptTag = document.getElementsByTagName('script')[0] firstScriptTag.parentNode.insertBefore(tag, firstScriptTag) play : -> _player.playVideo() getCurrentTime : -> _player.getCurrentTime() setVolume : (volume)-> _player.setVolume(volume) getVolume : -> _player.getVolume() isReady : -> _isReady onPlayerReady = -> _isReady = true window.onYouTubeIframeAPIReady = -> _player = new YT.Player 'player', events: 'onReady': onPlayerReady new Game()
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <div id="enchant-stage"></div> <script type="text/javascript" src="enchant.js"></script> <script type="text/javascript" src="main.js"></script> </body> </html>
その他
もう少しわかりやすくして、おっ立ち野郎 (id:ottati)さんのコードレシピ - 初心者のための簡単プログラミングレシピサイトに登録させてもらおう。
enchant.jsで埋め込んだinput要素にスペースが入力できない対策
こんばんは。 今更気づいたんですが、enchant.jsでゲーム内にinput要素を埋め込んだ時にスペースが入力できないことに気が付きました。どうやら以下のようになっており、いくつかの文字は弾かれているようです。
if ((37 <= e.keyCode && e.keyCode <= 40) || e.keyCode == 32) { e.preventDefault(); e.stopPropagation(); }
対策として以下のようにすることでスペースが入力できるようになります。
enchant(); enchant.ENV.PREVENT_DEFAULT_KEY_CODES = [37, 38, 39, 40];
参考URL
keydown時のpreventDefault対象keyCodeを利用者指定可能にしたい · Issue #74 · wise9/enchant.js · GitHub
【WEBサービス】youtubeを使った音ゲー×タッチタイピングサービスを作ってみた【つくってみた】
あけましておめでとうございます。 2015年は頭にあるサービスをいくつか形にしたいと考えているbokuwebです。
早速ですが、2014年の秋から作っていたサービスをβ版ですが公開しましたので告知いたします。
つくったもの

どんなもの?
「typebeats」はタッチタイピングを楽しく学ぶことを目標に作成した、youtube動画の音楽に合わせてタイピングする音ゲー×タッチタイピングサービスです。
いわゆる「BEATMANIAシリーズ」や「太鼓の達人」のような音ゲーとタイピングゲームを合わせてものです。
使用技術
- 言語 PHP、CoffeeScript(JavaScript)
- フレームワーク enchant.js、 jQuery
- CSSフレームワーク Flat UI(Twitter Bootstrap)
- データベース MySQL
- CMS wordpress
- バージョン管理 Git
- WEB API YouTube Player API
制作経緯
自分はPCのを触る仕事にもかかわらずブラインドタッチが苦手です。 ゲームで練習したこともありますが、いまいち続かなかったため、楽しく続けられるものをイメージして作成しました。”これじゃタイピングの練習になんねーよ”ってつっこみは無しでお願いします。。
また、実はhttp://typebeats.com/という、2年ほど前に作成していて今回の http://cloud.typebeats.com/は次期バージョンという位置付けになっています。
開発に関して - デザイン編 -
ゲームの実装などより、デザインに多くの時間を割いています。(というかデザインができないため時間がかかてしまった、という言い方が正しい) そのわりにこれかよって気もしますが、素人なりにそれっぽくがんばったつもりです。 ゲームなのであまり地味地味にならないように気をつけました。
ただ、細かい部分などは匙を投げてしまったり、見てみぬふりをしてしまっている部分がありますので、そのあたりは目をつぶってください。(僕はゲーム部分がほぼほぼ実装されると急にモチベーションが下がってしまうようです。)
背景画像
知らない人は少ないかもしれませんが背景はhttp://graphicriver.net/で買いました。 クオリティの高い背景やゲーム用のキャラクタなどが安値で販売されています。
Design Templates - Fonts - Logo - Icons | Customizable | GraphicRiver
CSSフレームワーク
CSSフレームワークはFlat UI - Free Bootstrap Framework and Themeを使用しました。 フラットデザイン自体賞味期限が怪しいですが、やはりこういったフレームワークを使用するだけで見た目をなんとかごまかすことができます。
Flat UI - Free Bootstrap Framework and Theme
フォント
知らないひとはいないかもしれません。 M+ OUTLINE FONTS | WEB FONTSを使用させてもらっています。
アイコン
アイコンはFont Awesome, the iconic font and CSS toolkitを使用しています。 種類が豊富で使いやすい。
Font Awesome, the iconic font and CSS toolkit
ロゴデザイン
ロゴデザインにはSquarespace Logo — Squarespaceを使用しています。 簡単にそれっぽいロゴができる素晴らしいサービス。 低解像度であれば無料です。
Squarespace Logo — Squarespace
開発に関して - 技術編 -
クライアントサイド
ゲーム部の言語はCoffeeScriptを使っています。最高です。 1人でjavascriptを書くならCoffeeScriptを使わない理由はないかと思っています。(あくまで個人的な意見ですが)
javascriptで以下の記述が
$(function() { // hoge });
CoffeeScriptならこうなる
$ -> # hoge
また、フレームワークにはenchant.jsを使用しました。 以前のhttp://typebeats.com/ではフレームワークを使用せずに実装したんですが、今回enchant.jsを使用することでこんなに簡単になるのか!と驚きました。 この辺りの詳細についても機会があったら記事にしたいです。
'15.1.18 記事にしました
100行で書けるブラウザで動作するyoutube音ゲーの作り方 - ぼくのかんがえたさいきょうのうぇぶさーびす
サーバサイド
wordpressを使用しています。 ほんとはRuby On RailsやPythonなどを使用してみたいところですが、今回の本題はそこではないので使い慣れたwordpressを使用しています。oAuthやコンタクトフォームもボタンひとつで導入できるのも素敵なポイントです。
読んでませんが以下のような書籍もでていますね。

- 作者: Rakhitha Nimesh Ratnayake,プライム・ストラテジー株式会社(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/11/27
- メディア: 大型本
- この商品を含むブログ (2件) を見る
使用プラグインやカスタマイズなどは読んでくれる方がいそうであれば記事にしたいです。
WEB API
Youtube Player APIを使用しています。 javascriptから非常に簡単にyoutube動画を操作することができます。
iframe 組み込みの YouTube Player API リファレンス - YouTube — Google Developers
TODO
- コメント、評価機能を付ける
- パフォーマンスの改善 現状FPSが24程度しかでておりません。 公開後分かったんですが、ガベージコレクションが頻繁に走るようなつくりになってしまっているようです。 この辺の詳細もできれば記事にしたいです。
- 人気譜面一覧の追加
- コード整理して、コア部分をコードレシピ - 初心者のための簡単プログラミングレシピサイトにまとめてみたい
初心者向け簡単プログラミングレシピ投稿サイト『コードレシピ』を公開しました - あのねノート。
- 譜面作成時のタイミング調整機能の追加
ひとまず以上です。 技術部の詳細や公開後の話など機会があれば記事にしたいですね。
それでは本年もよろしくお願いいたします。