Flying Bird (Unity Web Player)
そろそろ真面目にUnityかUE4やろうということで、作成。作成時間は推定4日くらい。
学生の頃、携帯で、ランダム生成された上下の壁の間を通って行くゲームをよくやってたなぁということを思い出したので、今回はそれを作ろうということで作業をはじめました。
企画
一応、作業に先立って下記のざっくりとした企画(仕様)を決めました。
コア:壁に当たらず奥地を目指せ!
フィーチャー:
ワンボタンの簡単操作
ランダム生成のマップで何度でも遊べる
奥地へ進むにつれて段々難しくなる
友達とスコアを競おう!
下記は勉強のテーマ。
- ひと通り完成と言える所まで作業する
- Android対応の方法まで学ぶ
- Asset Storeを利用して楽をする
- 音楽は手作りする
- 難易度設定はテキストファイルで編集できるようにする
使用Asset
作業内容
特にメモ取ったりしながら作業してなかったので、どんな感じに作ったかはあまり覚えていませんが、こんな感じ。
- プレイヤー(ただのSphere)を作り、重力を適用して、マウスダウンで上に浮くようにする
- 壁(ただのCube)を作り、当たり判定検出処理を作成
- 壁をプレハブ化し横方向に自動生成する処理を作成
- プレイヤーを横に移動する処理を作成。また、画面外に出たブロックを消す処理を作成
- 壁を上側にも作るように変更。また、生成時に壁を上下に動かすように変更。
- 難易度情報ファイルから壁の生成条件とプレイヤーの移動速度を読み込む処理を作成
- 時間経過で難易度を切り替える処理を作成
- プレイヤーをSphereからAsset Storeで無料公開されていた鳥に変更
(Sphereに残像つけたら卑猥に見えたため、青少年へ配慮しました) - スコアカウント処理を作成
- ハイスコアを記録する処理を作成
- 壁にぶつかった時、爆発する処理を作成(爆発処理自体はAsset Storeから拝借)
- 壁をCubeからLine Rendererで線を引くように変更し、角度をつけるようにした
(ここまでは横一直線で、高さだけを変えていた。角度をつけることで、壁が繋がって見えるように) - タイトル画面を作成し、爆発後にシーン遷移するようにした(ひと通りシーケンスを通した)
- フォントを変更(Asset Storeから拝借)
- BGMを作成し、追加
- Android対応(タップ検知、UI調整、不具合修正)
詰まった点とその対処法
一連の作業の中で、詰まった点を覚えている範囲で書きます。
BGMのループ時、一瞬だけ遅延する
約60秒のループBGMをFL Studioで作成し、インポートしたら、ループ時に一瞬止まってしまい、スムーズに聞こえませんでした。
原因は音データをmp3で取り込んでいたからで、wavおよびoggで取り込んだ際にはスムーズにループ再生されるようになりました。
参考:音関連について(Unity初心者がゲーム作りで学んだこと)
Android出力時、日本語が表示されない
開発環境のGameビューや、Unity Web Player、Unity Remote4では特に問題ありませんでしたが、Android実機上でデバッグを行った際、タイトル画面に表示していた日本語が表示されませんでした。(英語は問題なく表示されていた)
原因は、特定のAndroid端末では、Unity実行時にロードされるシステムフォントに日本語が含まれていないケースがあるせいだと思われます(きちんと確認していない)。参考URLの通り、適当なttfフォントファイルをインポートし、それをライブラリには含めずにFont Namesで明示的にロードするフォントを指定することで、日本語が表示されるようになりました。
参考:UnityのArialフォントがAndroid実機上で表示されない場合がある(太郎Work)
ビルド後、Detonatorコンポーネントをアタッチしたオブジェクトが爆発しない
鳥が壁にぶつかるとDetonatorというAssetを使用して派手に爆発するようにしてあるのですが、Gameビューでは爆発していたのが、Web Playerやexeファイル、Androidに出力した際に爆発せず、オブジェクトも消えないという問題が発生しました。
原因はDetonatorの仕組み上の問題(?)で、DetonatorコンポーネントのMaterialをセットする項目をnoneにしたままだと、ビルド時にデフォルトのMaterialをロードできずエラーを吐き、そこで処理が止まってしまうせいでした。
上記のようにMaterialを1つずつ手動でセットすることで、ビルド後も問題なく爆発するようになりました。
参考:今日のUnity (7) 爆発エフェクト(人工知能に関する断創録)
Android出力時、タップしても鳥が上昇しない
GameビューやUnity Remote4ではマウスクリック/タップでプレイヤーの鳥が上昇したのに、Android実機に出力すると、タップしても上昇せず、そのまま地面に落下してしまいました。
タップ長押しの判定は、Input.touchCount > 0 で取っていたので、それが上手く動いていないのかなと思いデバッグしてみるも、きちんと長押し中はInput.touchCountに1以上が入っているため、問題はなさそうでした。
原因は、PCとスマホ上でのフレームレートの違いを考慮していなかったせいでした。
具体的には、Updateメソッド内に、下記のような記述をしていたのですが、
this.rigidbody.AddForce(Vector2.up * up_power);
Unityはデフォルトだと可変フレームレートのため、Updateメソッドが秒間何回呼ばれるかは性能によって不定です。そのために、PCではUpdateメソッドが1秒間に60回呼ばれていたとしても、性能で劣るスマホでは30回しか呼ばれない可能性があります。
上記のような固定値の力を加える処理(仮に10とする)をUpdateメソッドに記述すると、PCでは10 * 60 = 600の力が加えられるのに対し、スマホでは10 * 30 = 300の力しか加えられない場合があるということで、今回の場合、このPCとスマホのフレームレートの違いで、スマホの時だけ上昇力が重力を上回れず落下していました。
対応ですが、上記の処理にTime.deltaTimeを掛けることで、フレームレートに差があってもかかる力が1秒間あたり一定になるように修正しました。
this.rigidbody.AddForce(Vector2.up * up_power * 100 * Time.deltaTime);
今までは1フレームでup_power分動いていた物体が、Time.deltaTimeを掛けると1秒あたりの移動量がup_powerになり、1フレームあたりの移動量が大幅に低下してしまうため、更に100を掛けて下駄を履かせています。これでどの環境でも鳥が上昇する速度が一定になり、一件落着。
この問題は、ゲームプログラミングを扱った事がある方なら一般的な事なのだと思いますが、自分はこれまでフレームレートを気にしてプログラムを作ったことがなかった(というよりは時間経過を考慮したプログラムをしたことがなかった)ので、Time.deltaTimeから前フレームとの時間差を取得し、秒間の移動量を一定にする処理は非常に重要なんだなと痛感しました。
参考:AddForce applies different amount on Android than PC(unityAnswers)
参考:Time.deltaTime を使用した FPS を意識した物体の操作(Lonely Mobiler)
参考:Time.deltaTimeとは何か。 #Unity(HirkBlog)
以上。なんかだらだらと書いてしまいましたが今後なんか作るとしたらもう少しサラッと書くと思います。