メディア芸術データベースのラッパーGemを作ってみました
はじまり
数日前、こんな記事を目にしました。
なんともすごいデータベースが公開されました。しかも利用規約によると、データベースの情報は自由に使うことができて商用利用もOKとのこと。このデータベースを使って何かのサービスを作ろうと考える人も多いのではないでしょうか。しかし、今のところ情報を取得する手段がサイトから検索するしかないので、データベースの情報をスクレイピングするRubyGemを作ってみることにしました。
メディア芸術データベース
簡単にサイトの説明と、今回作成したgemでアクセスできる範囲を説明します。
サイトには大きく分類して次のデータがあります。
- マンガ
- アニメ
- ゲーム
- アート
この中で、今回対応したのは「マンガ」のみ(資料、原画を除く)です。また、蔵書情報など我々が使う上では必要性が低そうな情報は対応していません。
マンガ情報の内容として、次のデータがあります。
- マンガ単行本作品情報・・・あるマンガの単行本や掲載雑誌などの全体的な情報
- マンガ単行本全巻情報・・・あるマンガの単行本全巻の情報
- マンガ単行本情報・・・あるマンガの単行本1巻の情報
- マンガ雑誌掲載作品情報・・・あるマンガの掲載雑誌全巻の情報
- マンガ雑誌全巻情報・・・ある雑誌の全巻の情報
- マンガ雑誌情報・・・ある雑誌の1巻ごとの情報
- 著者情報・・・ある著者の作品などの情報
- 資料情報・・・あるマンガの資料の情報
- 原画情報・・・あるマンガの原画の情報
- その他情報・・・その他の資料(同人誌など)の情報
また検索方法としては大きく分けて次の方法があります。
- マンガタイトル検索
- 雑誌タイトル検索
- 著者名検索
- 詳細検索
はじめてのRubyGem
RubyGemを作るのは初めてですが、スクレイピングは得意技なので簡単にできるだろうと軽い気持ちで作り始めました。しかし、このデータベース、サイトもデータも意外と複雑な構造になっていて、gemのインターフェイス設計にかなり悩みました。
例えば、1つの検索方法から得られる結果が1種類だけではなく複数種類が混在している場合があることや、検索の詳細条件がたくさんありインターフェイスとしてどう表現するか、返された値をどのように表現するか、など。
ぱっと思いついたのは、ActiveRecordのようにそれぞれの情報をそれぞれのClassにして、あるデータの小データをメソッドで取得したり、その逆であったり横方向であったり・・といった機能までできればと思いましたが、まずは単純に検索してハッシュデータを取得だけの機能をサイトやデータの構造にそって作ることにしました。(こういう時に、他の人のコードを読むって大事だなと思います。)
Rubyが分かってない
作り始めてすぐに、Rubyの基本的なことが分かっていない(または忘れている)ことに痛感しました。やっぱりRailsだけやっててもRubyを書けるようにはならないですね。ふだんRailsで使ってるあの機能はどうやって作られているんだろうとか色々と関心が湧いてきました。ソースを見てもらえるとRuby力が低いのがすぐわかると思うので、色々とご指摘を貰えたらと思います。
あと、Rspecを書くのも久しぶりだしRailsでしか書いたことがなかったのでどうしたものかと思いましたが、公開するならさすがに書かないとダメだろうと思い、他のgemのspecなどを眺めながらなんとか書いていきました。これは本当に書いて良かったです。というのも、最初から最後まで色々とインターフェイス設計を試行錯誤していたのでspecがあることで修正漏れが見つけられたのは非常に安心感がありました。テスト大事、テスト大事。
RubyGemsへリリース!
ということでリリースしました。使い方はGithubの方に書いてありますが、詳細なDocument(YARD)は書きませんでした。やっぱりインターフェイスを見なおしてActiveRecordのようにしたいので、書くならその時に書きます。あとは、Travis CIで自動テストさせるようにしました。他にもGithubと連携してくれるツールがたくさんあるので調べてみたら面白そうです。
Unityをいじってみた(その4〜お皿を回転させる技術〜)
これで最後の記事です。
お皿を回転させる技術
このゲームでこだわりたいポイントとして、ターンテーブルの動きをいかに自然に、指の動きに合わせて回転できるかということがありましたので、それについて試行錯誤した実装を説明します。
1.通常時の回転
ゲームシーンでは、何もしない状態で常に一定のスピードでターンテーブルが回転している状態にしておきます。それには、ターンテーブルのGameObjectにRigidbody2Dコンポーネントをつけて、Rigidbody2D.angularVelocityに値を設定します。angularVelocityの単位は角度/秒です。
2.指で回転させる動き
指の動きはTouchクラスで取得します。Touchの状態として、次の4つの状態があります。
- TouchPhase.Began タッチし始めた時
- TouchPhase.Moved タッチしながら指を動かした時
- TouchPhase.Stationary タッチしながら指が動いていない時
- TouchPhase.Ended タッチし終えて離した時
そして、それぞれの動作は次のとおりです。
BeganとEndedの時は何もしません。
Stationaryの時は、ターンテーブルを指で押さえている状態になるので、ターンテーブルのRigidbody2D.angularVelocityを0にして回転を止めます。
Movedの時は、前のフレームと今のフレームで、ターンテーブルの中心から何度回転したかを取得し、それをRigidbody2D.angularVelocityに回転速度として与えます。また、前のフレームと今のフレームでターンテーブルの回転方向が変わったら、判定処理を行います。
// 更新前の回転の向き float beforeAngularVelocity = discRigidbody2D.angularVelocity; // 回転角度を求める float deltaAngle = GetDeltaAngle (goDisc.transform.position, touch.position, touch.position - touch.deltaPosition); // 回転速度にするために適当な倍率をかけ、回転速度の上限を設定する float angularVelocity = Mathf.Clamp (deltaAngle * 25, -500.0f, 500.0f); discRigidbody2D.angularVelocity = angularVelocity; // Discの回転の向きが変わったら && 一定以上の角度があったら反応する if (beforeAngularVelocity * angularVelocity <= 0.0f && Mathf.Abs (angularVelocity) > 10.0f) { // スクラッチ音の再生(省略) // 判定処理(省略) }
// 3点間の角度を求める private float GetDeltaAngle (Vector2 p0, Vector2 p1, Vector2 p2) { float deltaAngle = GetAim (p0, p1) - GetAim (p0, p2); deltaAngle += deltaAngle > 180 ? -360 : 0; deltaAngle += deltaAngle < -180 ? 360 : 0; return deltaAngle; } // 2点間の角度を求める private float GetAim (Vector2 p1, Vector2 p2) { float dx = p2.x - p1.x; float dy = p2.y - p1.y; float rad = Mathf.Atan2 (dy, dx); return rad * Mathf.Rad2Deg; }
discRigidbody2DがターンテーブルのRigidbody2Dコンポーネントです。判定処理のため、まず現在の回転速度(回転方向)を取得しておきます。次にGetDeltaAngleメソッドにて前のフレームと今のフレームで、ターンテーブルの中心から何度回転したかを取得します。引数はターンテーブルの中心座標、今タッチしている座標、前のフレームでタッチした座標です。この3点から角度を求めます。後はその角度に適当な調整をかけて角速度にしてdiscRigidbody2D.angularVelocityに設定します。残りは判定処理です。
この「3点の角度を求める」という、おそらく三角関数の基礎なんでしょうが、それが分からず苦労しました。以下のサイトが参考になりました。
参考:【Unity2D】2点間の角度を求める - Qiita
そして壊れる
色々やっていたら、GameObjectがmissing状態になり読み込めない状態になってしまいました。どうやらメタファイルに記録されているゲームオブジェクトのUUIDが変わってしまったために起こる現象らしいです。原因としてはUnity Editor以外のFinderなどでファイルの変更操作を行ったりクラッシュだったり、色々とあるようなので操作は慎重にしてライブラリ管理をちゃんとやらないといけないですね。
参考:Unity開発者が複数人で開発を進める上で覚えておくと幸せになる9つの事 - テラシュールブログ
おわりに
というわけで最後は壊れてしまったのですが、どちらにしても作り直したいと思っていたし、Unity5もリリースされたので新しい機能などもチェックしつつ、次はもっとスマートな設計にしたいと思います。
Unityをいじってみた(その3〜データのダウンロードと保存方法〜)
続きです。実際にゲームを作ることでGameObjectやScriptに関する知識はたくさん溜まりましたが、すべてを書くのは果てしないので、このゲーム独特の内容を書いていきたいと思います。
データの配置と読み込みについて
このゲームは、UIイメージの他に、音源や譜面にBMSデータ(※)を使用しています。主に使うデータとしては、「イメージ画像」「音源(WAV)」「譜面(テキスト)」の3種類になります。※フリーのBeatmaniaクローンゲームのデータ。音源等には著作権があるため、あくまで個人利用。
「イメージ画像」「音源(WAV)」といった素材はUnityのProjectに配置することで、Unityに最適化された状態に変換されるようです。またこの時、ProjectのResourcesフォルダに配置することで、Script側からオブジェクトとして簡単に呼び出すことが可能になります。
一方で「譜面(テキスト)」の場合はUnityで利用する素材ではないため、テキストファイルとして読み込む必要があります。読み込みには.NetライブラリのFileInfoクラスなどを使います。また、Unityはビルド時にアセットを統合してしまうため、実機で実行するとファイルをロードすることができなくなります。統合されたくないデータはProjectのStreamingAssetsというフォルダに配置することで、そのままの状態でファイルをロードできます。
データの保存領域について
スマホアプリとしてゲームを配布する場合、データ容量に注意する必要があります。Android、iPhoneともに、アプリのサイズは最大50MBに制限されています。BMSファイルは1曲で数MB〜数十MBになるため、とてもアプリに含めて配布することができません。そこで、BMSファイルをダウンロードしてアプリ領域以外にデータを保存する領域が必要になります。
1.キャッシュ領域
キャッシュ領域はデータ容量の上限はありませんが、その代わり永続性はありませんので、ゲームデータを保存するのには向きません。
Unity - スクリプティング API: Application.temporaryCachePath
2.永続領域
キャッシュではなく永続的にデータを保存できる領域です。ゲームデータを保存するのにも良さそうです。しかし、iPhoneアプリでここをたくさん使う場合、リジェクト対象となるそうです。
Unity - スクリプティング API: Application.persistentDataPath
3.Asset Bundle
Asset Bundleはアプリ領域にデータを追加保存するようです。ソーシャルゲームなどで一般的に使われているのはこれだと思います。BMSファイルを配布するにはこれが一番良いとおもいきや、Pro版のみの機能ということであえなく断念しました。
参考:【Unity】AssetBundleを使わずに動的にデータを取得するゲームを作る | バイナリ覚書
Asset Bundleもどきを作る
Asset BundleはPro版でしか使えないので、永続領域にBMSファイルをダウンロードして保存し、そこからデータを読み込むことにしました。この場合、iPhoneではリジェクト対象となるのでAndroidだけをターゲットにすることにしました。実装までには次の3つの機能を作成します。
1.ダウンロード
データのダウンロードは、.NetのWWWクラスを使うことで実現できます。ファイルがローカルに存在するか確認し、なければダウンロードします。
string filename = "bms.zip"; string path = "file:///" + Application.persistentDataPath + "/"; string url = "http://localhost/bmsdata/"; if (File.Exists (Application.persistentDataPath + "/" + filename)) { Debug.Log ("Find local file." + Application.persistentDataPath + "/" + filename); } else { Debug.Log ("Can't find local file, Downloading.."); WWW www = new WWW (url + filename); while (!www.isDone) { } File.WriteAllBytes (Application.persistentDataPath + "/" + filename, www.bytes); }
2.Zip解凍
BMSデータはzip圧縮しているので、ゲームをプレイする前に解凍します。最初は.NetのライブラリのZipFileクラスを使おうとしたのですが、Unityが利用するMonoは.Netの3.5までしか対応しておらず、ZipFileクラスは4.5で追加されたライブラリのため使うことができませんでした。そこで、CodePlexにあったDotNetZipライブラリを使用しました。
外部ライブラリを追加する場合はUnityのProjectに追加するだけでScriptからusingできるようになります。
以下はzipファイルから特定の拡張子のファイルだけを解凍するサンプルコードです。ExtractExistingFileAction.OverwriteSilentlyを指定することで、既にファイルが存在する場合も上書きします。
using Ionic.Zip; using (ZipFile zip = ZipFile.Read (Application.persistentDataPath + "/" + filename)) { zip.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently; foreach (ZipEntry entry in zip) { if (Regex.IsMatch (entry.FileName, @"\.(wav|WAV|bme|BME)$")) { Debug.Log ("decompress: " + entry.FileName); entry.Extract (Application.persistentDataPath, ExtractExistingFileAction.OverwriteSilently); } } }
しかしこのライブラリは、Windowsにしか対応していないため、与えられたパスの'\'を'/'に置換するという処理が入っていました。そのため、Macなどの環境ではパス指定ができず読み込みエラーとなりました。(.NetのライブラリなのでWIndowsしか想定していないのは当たり前といえば当たり前ですね)。これをモンキーパッチ的にLinux用に修正されたモジュールがあり、それを使用することでこの問題はひとまず回避出来ました。
DotNetZip Library - View Issue #15236: "Path is empty" when extracting in a linux system.
3.AudioClipの作成
Unityでは音源をAudioClipのインスタンスにして再生します。ダウンロードした音源はWAVファイルなので、これをAudioClipに変換する必要があるため、以下を参考にして実装しました。正直、このレベルになってくると自分の力では相当難しいので、Githubにソースが公開されていて助かりました。
Post Position 【Unity】 WAVファイルからAudioClipを自前で生成するサンプルを作ってみた
以上で、Asset Bundleもどきができました。
できた後で知りましたが、Androidアプリの場合、ダウンロードなどはせずにSDカードに直接データを入れる方法を取るほうが一般的みたいですね。
次に続きます。
Unityをいじってみた(その2〜開発環境とデバッグ〜)
この記事の内容は、Unity4.6時点の内容です。現在、Unity5が公開されていますので、この記事の内容は古い情報となりますが、個人的な記録のために書いています。
前回からの続きです。
作りたいゲームについて
まず目標にしたのが、「スマートフォンで遊べる音ゲーアプリ」にしました。Beatmaniaのスクラッチ部分だけがあり、画面に表示されるターンテーブルをタイミングよく回すゲームです。製作途中の実際の画面はこちら。(非常に音量が小さいです。)
開発環境について
開発環境について説明します。
Unity Editor
Unity EditorはUnityの統合開発環境そのものです。Mac版を使用しています。
Xamarin Studio
Xamarin StudioはScriptを編集するためのエディタです。Windowsを使う場合はVisual Studioを使うのが一般的ですが、Macではデフォルトのエディタが使いにくいのでこれが一般的です。しかしXamarin Studioも使いにくい点があるので、いくつかカスタマイズしました。
1.キーバインド
Xamarin Studioを使う最大のメリットはクラスやメソッドの入力補完が使えることですが、Google日本語入力を使用していたため、そのショートカット(Command + Space)が入力ソースの切り替えのショートカットと被ってしまい使えなかったので、まずは入力ソースの切り替えのショートカットを無効化しました。(普段、Visual Studioを使うのでCommand + Spaceがとてもしっくりくる。)。変更方法は次の通り。
システム環境設定>キーボード>ショートカット>入力ソース の、「前の入力ソースを選択」のチェックを外す。
2.コードフォーマット
Xamarin Studioのデフォルトのコードフォーマットは余分なスペースが多く、Visual Studioに慣れている身にとっては少し気持ち悪いので修正しました。変更方法は次の通り。
Xamarin Studio>Preferences>ソースコード>コードフォーマッティング>C#ソースコード>C#フォーマット>編集>White Space で、チェックを外す。
3.エラーハイライト
ビルドエラーや実行エラーが起きた行がハイライトされるのですが、どのシンタックスハイライトを設定してもソースコードにハイライトが被ってしまいとても見にくいです。そんな時はブレイクポイントを設定することで赤背景に白文字になり見やすくなるのでそうしていましたが、ハイライトの色自体を変えることはできなかったので知っている人がいれば教えて下さい。
Unity Remote 4
Unity Remote 4は、UnityEditor上のゲーム画面をiOSやAndroidで操作するためのリモートツールです。Touchイベントなども実機と変わらず取得できるので、開発中はほぼこれを使いました。「Unity Remote」で検索すると、一つ古いバージョンのUnity Remote 3も出てくるので間違えないよう。(私は間違えてしばらく3を使っていました。)
iPhone実機
iPhoneの実機でDebug実行するには、XcodeとiPhoneの設定(おそらくiPhone Developer Programへの登録)が必要です。おそらくと云うのは、私のiPhone端末は既にそれらの登録を行っていたため、特に困ることなくすんなりできました。実際には何が必要だったのか分かっていません。
Android実機
端末側はUSBデバッグを有効にします。またPC側にはJDKとAndroid SDKのインストールが必要です。Android SDKはAndroid StudioをインストールするとSDKのインストーラもついてくるので、それでインストールすると簡単です。
参考:無償化されたUnityのスマートフォン書き出し機能でお手軽にAndroidゲームを作る方法まとめ | GMOメディア エンジニアブログ
デバッグ方法
Consoleデバッグ
ScriptにDebug.Log("Hoge")でUnity EditorのConsoleにデバッグメッセージが出力されます。
iPhone実機デバッグ
Consoleデバッグ同様のデバッグメッセージが、XcodeのConsoleに出力されます。
Android実機デバッグ
Android SDKのtools(/Developer/adt-bundle-mac/sdk/tools/monitor)にあるmonitor(Android Debug Monitor)に出力されます。非常に大量のデバッグメッセージが出力されるので、フィルタをすると良いです。ちなみに無料版だからか分かりませんが、ものすごい勢いで”profiler is only supported in unity pro”というメッセージが流れ続けてとても見づらいです。Unity5では出なくなっているでしょうか。
参考:Unity - IDEを起動せずにAndroid実機ログを見る方法 - Qiita
次回に続きます。
Unityをいじってみた(その1〜作り始める前に〜)
はじめに
この記事から何回かに分けて、ゼロからUnityを使ってみて得た感想やノウハウをアウトプットしていきたいと思います。まだまだ浅い知識ですので、間違いなどありましたらご指摘いただければと思います。
Unityとは
Unityはゲーム開発のための統合開発環境です。元々は3Dゲームの開発のためにつくられており、3Dエディターとゲーム開発エディターが統合されたようなUIになっています。Unityを使う利点としては、Unityで作られたゲームをiOSやAndroid、Webブラウザなどマルチプラットフォームに配布できることや、AssetStoreというUnity専用のストアでコンポーネントを購入することで開発を効率良く進めることができることです。またUnity4.6からは正式に2Dゲームにも対応しました。
何をしたら良いかわからない
ゲーム開発には興味があったので、何度かUnityをインストールしてチュートリアルをやったことはありました。その時は、チュートリアルの通りにボタンをポチポチと押していって「やったー動いたー!」というところまでは行くのですが、いざ自分で何かつくろうとすると、何をしたら良いかわからない状態になっていました。(3Dで作りたいゲームのアイディアが浮かばなかったというのもありますが。)
一番の近道は、できる人に教えてもらう
そんなある日、たまたま補欠で参加した勉強会でUnityのプロに出会いました。そこで、次の勉強会までに、おそらく実現できそうな簡単なミニゲームのアイディアを考えて、その人に「これを実現するには何をしたらいいのか」をきいてみました。すると、ものの数時間で何をすべきかが見えてきて、それ以降は自分で調べながら進めるという軌道に乗ることができました。あの時の体験はほんとうに感謝です。
目標を決めることが大事
Unityはあらゆるゲームが作れるので、前述のように目標がない状態でさわってみても迷子になるだけです。目標があれば、チュートリアルや書籍でもある程度何をすべきかが見えてくると思います。Webでも日本語の情報がかなり多くなっていますので、一度走り始めることができさえすれば、調べながら進めることはそんなに難しくはないと思います。
作り始める前に知るべきこと
実際に作り始めてから知って、困ったことは次の通りです。
まずライセンスによる機能差異については、次のページに書いてあります。
いきなりこれを見ても何がなんだか分からないと思います。このうち自分が困ったことは、『アセットバンドルによる本格的なストリーミング』という機能です。アセットバンドルとは、よくゲームなどでインストール後の起動時やアップデート時にデータをダウンロードする機能です。これについては後で詳しく書きます。
[本文修正] 記事を書いた翌日にUnity5がリリースされ、情報が古くなってしまいました。Unity5になり、ライセンスによる機能差異はほぼ無くなり、ProfessionalEditionの方は大規模開発や商用化に向けて必要な機能が追加されました。機能差異については次のページを参照してください。
またプラットフォーム間の違いについても色々ありますが、一番厄介なのは(これはUnityとは直接関係ないですが)iOSのアプリ申請の厳しさだと思います。なので、iOSをターゲットにする際はアプリ申請基準を十分に確認する必要があります。もし、初めてアプリを作ってそれを公開したいのであれば、まずはAndroidをターゲットにすることをお勧めします。
ここまではうんちくばかりでしたが、次回からは技術的な話に入りたいと思います。
第4回社内勉強会を開催しました
レポート
今回のテーマは「コードレビュー」でした。
自分もメンバーもコードレビューをそんなにしたことはないので、まずイントロとしてSonicGardenStudyさんのオンライン勉強会の内容を簡単にまとめたものを紹介しました。
KokubunjiStudy03 - Google スライド
内容としては以下の3点。
- コードレビューをしないとどうなるのか(ウンコード・マニアさんの事例とかを紹介)
- 良いコードレビュー・悪いコードレビューとは
- コードレビュー時における注意点(技術面・精神面)
そのうえで、他のメンバー二人が用意してくれたコードレビューを実施しました。一人はEnchant.js、もう一人はRPGツクールのスクリプト(Ruby)です。どちらもゲームエンジンプログラムということで、両方を見比べられたのが興味深かったです。特にツクールのスクリプトは、あらかじめ用意されたクラスモジュールやドキュメントが充実していて、Rubyを勉強するにも非常に良い題材になっているという印象でした。
レビューをガッツリやったというよりはアルゴリズムとか動きの確認が多かったですが、ソースの改善としては以下のような内容がありました。
- 定数値はそのままコードに書くのではなく変数にする。
- if文が深くなった。どうしたらいいか?→1.機能単位でまとめて関数化する。2.(クラスインスタンスのパラメータ操作が多かったので)クラスメソッド化する。
KPT
Keep
- 開始時間を1時間遅らせて、先に飯を食う。(腹ペコな若者が多いからw)
Problem
- 特定の技術についての集まりではないので、参加者の知識にばらつきが大きく、なかなか話に入っていけない人が多い。
- 自由すぎて目的がよく分からない。
Try
- 目的について再整理。どうやったらみんなが楽しく参加できるか考える。
第3回社内勉強会を開催しました
の前に、第2回の記事を書くのを忘れてしまいました。
第2回はテーマなし(フリー)で行いました。自分はWebの通信方式(HTTPやAjax、WebSocket、WebRTCなど)の概要的な発表と、自分が過去に作ったサービスでWebSocketを使った事例を紹介しました。
KokubunjiStudy01 - Google スライド
それから後輩君が、自分が第1回に紹介したスクレイピングの技術を使ってサービスを作った話を発表してくれました。しかもこれが初めてのLTとのことで、こういう反応があると非常に嬉しいです。
そして第3回目を今日はテーマは「ゲーム開発に関する技術」でした。自分はiOS(Swift + Spritekit)でゲームを作るという発表をしました。
KokubunjiStudy02 - Google スライド
Xcodeの環境やObjective-CのサンプルをSwiftに変換することにかなり手間取り、プロトタイプにも満たないところまでしか作れませんでしたが、苦労した分、得るものも結構ありました。(ある程度の完成度まできたら、ブログにも書こうと思います)
さらに今回は初参加の方も2名来てくれました。普段会社では技術よりの話とかしない人でも、意外と興味を持っている人が多いようです。どこまで輪を広げようかは相変わらず手探り状態ですが、当面はこのくらいの規模がやりやすいかもしれません。
あとLTに関しては自分は特に下手クソなので、もっとLT力をつけて、LTって楽しい!ってことを自分が感じられて、みんなにも伝えられるようになりたいところです。