FreelyApps

個人によるアプリ開発の日記です。アプリの収入だけで生活できるようになるのが目標です。UnityでAndroid向けのゲームアプリを作成しています。


    アプリ『トランプ・ジン・ラミー』を公開しました。
    Android/iOS https://goo.gl/PYKFYG

    タグ:C#

      このエントリーをはてなブックマークに追加 Clip to Evernote
    UnityのプログラミングはC#を使うのが一般的です。CやC++をやってきた人であればグローバルな#defineが使えないかと思うことでしょう。
    C#には#defineディレクティブがあります。ファイルに定義するものなので、他のファイルでこれを参照することができません。CやC++にある#includeディレクティブがC#にはないので、定義はファイルごとにしていかなければなりません。(C#で使えるプリプロセッサディレクティブはここにあるものです。)

    グローバルな#defineを行うには、Unity Editorにシンボル(文字列)を登録することで可能です。 

    Player Settings->Other Settings->Configurationのところに以下のようにScripting Define Symbolsという枠があります。各プラットフォームごとにありますので、定義を有効にしたいプラットフォームを選択してScripting Define Symbolsに設定を行ってください。
    定義の仕方は、
    TEST;TEST2;TEST3
    というようにセミコロンで各定義を区切って入力してください。
    scrdef
     
    Unityのマニュアルではこのページの下部に「プラットフォームのカスタム定義」と「グローバルのカスタム定義」という内容が書いてあります。上で説明したのは「プラットフォームのカスタム定義」になります。
    「グローバルのカスタム定義」は専用の定義ファイルを用意するので、使っていると結構面倒です。複数のプラットフォームで同じ定義をしたいときにこれを使います。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    C#でスクリプトを書くときは通常クラスごとにファイルを一つ作るでしょう。公式の解説にもあるようにメニューからスクリプトを作り、名前が選択されているときにリネームするのが便利な作り方です。

    Assets->Create->C# Scriptと選べば、プロジェクトブラウザー上にNewBehaviourScriptというスクリプトが追加されます。以下のように追加したタイミングではファイル名が変更できるようになっています。この瞬間だけ名前を変えることで、ファイルの中身にある文字列も一緒に変わります。
    newscript

    うっかりEnterキーなどを押してNewBehaviourScriptのままになってしまった場合は、ファイルのリネームとクラスのリネームは別々に行えば問題ありません。意外と面倒なので、ファイルを消してやりなおした方がおそらく楽です。

    Test.csというファイル名に変更した場合は以下のようなスクリプトができます。

    classのところがTestになっていて、ファイル名と一致しています。 
    ファイル名とクラス名が一致しているとゲームオブジェクトにアタッチできるということが重要です。 なぜそうなのかというとUnityのルールだからです。(C#ではファイル名とクラス名が一致しなくても良いようです。)
    ゲームオブジェクトにアタッチするというのはスクリプトを簡単に呼び出すことができ、Unityでは以下のようにドラッグ&ドロップするだけです。


    GameObjectにTestというコンポーネントが新たに設定されているのがわかると思います。



    ファイル名とクラス名が一致しない場合どうなるかというと、アタッチできません。そのようなスクリプトにはインスペクタ―上に以下のような注意書きが載ります。MonoBehaviourを継承し、かつファイル名とクラス名が一致していないという注意です。これは問題ない場合もあります。(static classとかpartial classとか)

    nomono

    この注意書きが出ているスクリプトをアタッチしてみると、警告ダイアログが出ます。

    errdi

    警告にも書いてあるようにアタッチできないスクリプトということがわかりました。ただし、GUIではできないというのが今回の本題です。

    GUIでの操作は駄目でしたが、スクリプトから
    ファイル名とクラス名が一致しないスクリプトをアタッチすることができます。やり方はAddComponentを使うことです。 

    まずファイル名とクラス名が一致しないMonoBehaviourを継承したクラスを用意します。以下の例はファイル名がNotSame.csであり、クラス名がNotSameScriptというクラスだけのスクリプトです。

    このクラスをアタッチするために次のようなTest.csを用意しました。

    Testスクリプトにはゲームオブジェクトの参照が渡せます。渡した参照のゲームオブジェクトにNotSameScriptをアタッチすることになります。

    こうしてアタッチすると正しく動きます。StartとUpdateしか試していませんが、動いていました。

    fatt

    corratt

    上のように本来アタッチできないスクリプトをアタッチしたところ、アイコンが微妙に変わりました。下の図はアタッチできるTestスクリプトの場合です。コンポーネントの名前の左のアイコンとScriptという枠が違います。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    C#でスクリプト上で重い処理を作ったとします。大きなリストの複雑なソートをするとか、大量の計算を行うといったことです。
    そういったときスクリプトのある特定の部分に書いてあることが、どれくらいの時間で処理されるのかということを知りたいことがあるでしょう。 こういったときはC#のStopwatchクラスを使うと簡単に実行時間が測れます。以下にコードを載せておきます。


    ストップウォッチクラスを作り、Startします。処理が終わったタイミングでStopし、ElapsedMillisecondsでかかった時間をミリ秒単位で取得しています。非常に簡単なコードなので、簡単にパフォーマンスを測れます。
    リリース版に計測用のコードが紛れ込むのが嫌なら、定義を使っておくと良いと思います。(#if DEBUGなど)

    ProfilerやFPSの計測よりも細かな範囲でのパフォーマンス計測になります。はじめは大きな計測から始めて、徐々に計測範囲を狭めていくと処理が重い原因を調べやすいと思います。
    このスクリプトのあるメソッドが重いとわかっているなら、今回の計測方法でさらに詳しく原因を探れるはずです。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    BGM用のファイルをインポートするとデフォルトの設定がオーディオクリップに適用されます。
    audioclipsetting

    これをインスペクタ―上から手動で設定することが普通だと思いますが、数が多くなってくるといちいち設定をするのが面倒になります。手動での設定は行うことを忘れることもあります。
    これらを解決するには自動で設定を行えるようにするのが一番です。

    AssetPostprocessorというクラスを継承してクラスを作ります。AssetPostprocessorはアセットのインポートや変更時になんらかの処理を行うために使うクラスです。
    Editorフォルダの下にスクリプトをおかなければいけません。(Unity5からなのかEditorフォルダの下に置かなくても動作するようになっていました。しかしながら、互換性のためEditorフォルダに置くことを推奨します。) 

    上のスクリプトはBGMをインポートしたときにオーディオクリップの設定を行うサンプルです。
    BGM_IMPORTERシンボルが定義されていれば、Resources/Sound/BGMにおかれたオーディオクリップをストリーミング再生で圧縮フォーマットはOgg Vorbisというように設定します。
    新しくオーディオファイルを追加したときはこのスクリプトの処理が行われます。既に追加してあるファイルに処理を通したい場合は、リインポートを行うかオーディオクリップの設定を変更すると処理が通ります。
    リインポートはアセットを選択した状態でAssetsメニュー->Reimportを選択するかアセットを右クリックして出るメニューからReimportを選択すればできます。フォルダを指定するとそのフォルダ以下をリインポートすることもできます。

    上記のスクリプトはBGM用に作ったものですが、少し改造すればSEやVoiceといったBGMとは少し設定を変えるであろうオーディオクリップにも使えます。
    フォルダを分けるのが素直な方法だと思います。 Resources/Sound/SEとかResources/Sound/Voiceにあるファイルだったらで場合分けするというのがお手軽です。

    こういった自動化用のスクリプトはEditor拡張というやり方でUnityは柔軟に対応することができるようになっています。 Editor拡張は面倒なことが多いのですが、一度作ってしまうと後の作業を劇的に減らしてくれる可能性を秘めています。
    同じような作業を繰り返していると感じているときは自動化できないかと考えてみると楽ができます。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity5.1からUnityEngine.Assertionsが使えるようになりました。スクリプトリファレンスはこちらに載っています。
    アサートは開発時に有用な機能です。Unityだけでなく、色々なプログラミング言語に同じような機能があります。どのような場合もプログラムの検証用に用いられます。
    例を挙げると、ある変数の値が0であるべき場所でそうなっているかを調べたり、値の範囲が想定内に収まっているかを調べるために使われます。

    基本的にデバッグ版のみに有効化するものです。リリース版には検証用のコード(Assertも含む) は取り除かれ余計な処理をせずに高速に実行するべきだからです。
    デバッグであれば実行速度が遅くなったとしても問題を検知できる方が重要なので、Assertを有効化して問題が起こっていないかを調べるのです。

    Assertとは英単語で意味は「主張する」といったものです。それゆえ条件を満たしているかを「主張する」機能に使われるようになったようです。 
    大抵の場合は条件を満たさないときにエラーメッセージを出力するか例外を発生させます。UnityEngine.Assertionsではこのどちらもサポートしていますが、エラーメッセージを出力するほうで十分だと思います。例外の処理を忘れると、おかしな挙動になってしまうのです。



    UnityEngine.Assertionsの使い方を説明していきましょう。

    using UnityEngine.Assertions;を宣言しておき、判定したいところでAssert.IsTrue(bool値のもの);というように書けば良いです。Assert.IsTrueの場合は引数の値がtrueであれば何もせず、falseであるとエラーになります。第2引数の指定もでき、そこにはメッセージを指定できます。どのようなエラーなのかわかりやすくするためにコメントを書いておくことをお勧めします。
    他にもIsNotNull(nullでないことを保証する)とかAreEqual(値が一致するか保障する)などがあります。

    このアサートはUNITY_ASSERTIONSが定義されているとき有効であり、ないときはコードから消えます。Development BuildのときはUNITY_ASSERTIONSが自動で定義されるようですが、UNITY_ASSERTIONSを自分で定義する方が便利だと思いました。

    using UnityEngine.Assertions.Must;を宣言すると、Assertを利用した拡張メソッドが使えるようになります。bool、int 、floatなどの型にメソッドが追加され、(数式).MustBeEqual(0);というようにアサートを書くことができます。(拡張メソッドなので、using UnityEngine.Assertions.Must;は必須になります。)

    Assert.raiseExceptionsをtrueに設定するとエラーを表示するのではなく、例外を発生させます。デフォルトはfalseなのでエラー表示がされるだけです。例外処理を書いてアサートを処理したい場合はtrueにすることになるでしょう。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    FPS を変えるところだけ知りたい方は、横線が引かれた下の部分だけお読みください。

    Unity で作ったゲームはFPS が自動的に設定されます。端末ごとに自動で決まるので意識しないことも多いかと思います。
    FPS は処理の更新頻度と考えることができますが、当然処理が重くなれば高いFPS は出しにくくなっていきます。PC とモバイルの性能差は大きいもので、開発中(PC で作業)のときは問題ないのに実機(モバイル)では動きが滑らかでないということが起きます。 それは描画の処理はハードウェアの仕組みで常に一定のタイミングで変更がなされるためです。(ここでそれについては深く触れないので、Unity で作ったアプリで処理が描画の更新に間に合わなければカクカクした動きになるということだけ認識してください。)
    Unity のFPS は変動するので、可変フレームレートの設計で作らなければいけません。Update 関数の呼び出しのタイミングが一定ではないので、前の呼び出しから何秒経ったかを取得しその時間を考慮して更新を行うということです。例えばキャラクターの移動を(速度)×(Time.deltaTime) の値を足していくことで実現していれば良いのです。このように作られていれば、フレームレート(FPS) を変更してもそのままゲームが動くはずです。 

    Unity でゲームを作れば可変フレームレートの設計になります。更新のタイミングがどうであろうとゲームが動くように作られているなら、FPS の目標値を下げたりする必要があるのかという疑問を思うかもしれません。FPS の目標値が60である場合、FPS は0 ~ 60 までの値を取り得ます。十分に処理に余裕があれば60 の値になり、余裕がないときは10 とかになるでしょう。60 のときは滑らかで10 のときは動きがぎこちない。それを行ったり来たりしてしまうと、非常に見た目が悪くなります。頻繁に低いFPS を取ってしまう場合、処理が重すぎるので目標とするFPS を下げる必要性が出てきます。
    FPS の変動が小さくなればそれほど見た目に差は出ません。目標とするFPS が下がると、当然取りうる範囲が狭くなるので変動する可能性が減ります。また更新頻度が下がることで処理にかける時間を増やすことができます。



    Unity でFPS を動的に変更する方法は以下のようになります。
    Application.targetFrameRate = 30;
    というようにして目標とするFPS を指定できます。60 か30 が無難だと思います。
    もうひとつ変えなければいけないところがあります。QualitySettings のV Sync Count という設定をDon't Sync にしなければいけません。これはハードウェアの画面の更新に更新(Update)をあわせるかという設定なので、あわせてしまうとFPS を設定できません。
    動的に変えられるといってもゲーム起動時に30FPS に設定してあとは何もしないとかでもかまいません。その場合は単にFPS を半分にしているだけなので、QualitySettings のV Sync Count をEvery Second V Blank にするだけでスクリプトで制御しなくても同じことが実現できます。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity Adsを使う上で気をつけた方が良いことをまとめておきます。

    動画広告のためか通信速度が速くないと、広告の取得ができなくなります。 3G回線ではかなり厳しいかと思います。

    サンプルはあくまで最低限の実装であり、これで動くというものです。これだけのスクリプトで動画の広告が載せられるということは素晴らしいのですが、実用的にはもう少し厄介な問題を解決する必要があります。サンプルをそのまま使っても問題は起きません。ただ表示されないときの処理が単純すぎるだけです。

    実はAdvertisement.Initialize が重要です。初期化に失敗することがあり、このメソッドは一度しか呼ぶ意味がないようになっています。
    通信できない状況でAdvertisement.Initialize を呼ぶと100%失敗します。一度しか呼ぶ意味がないというのはメソッドの内部で呼び出したかどうかのフラグを見ており、一度目以外処理が行わないようになっているからです。
    つまり、Advertisement.Initialize を呼び出したときに通信がうまくいかないと、一度ゲームを終了させない限りUnity Adsは使えないということになります。
    このあたりのことはソースを見ていて発見したことでした。Unity AdsはUnityに完全に統合されているわけではないので、ソースコードが結構内部まで見れます。

    Advertisement.isInitialized をサンプルでは使っていません。使わなくても動くからですが、本来は使うべきものだと思います。
    このプロパティは初期化が成功していればtrueになるので、これを調べてfalseのときは広告を出す処理をしないとするべきです。falseのときは広告が出せることはないからです。

    Advertisement.isReady というものがあります。これは広告の準備ができたかを判定するためのものです。Unity Adsを使う場合は広告の表示がしたいわけであり、そのための準備を待ちたいと思うのは自然でしょう。そうするとAdvertisement.isReady がtrueになるまで待つような実装をしたいと思うかもしれません。しかし、それには十分な注意が必要なのです。
    Advertisement.isReady は内部でAdvertisement.isInitialized を見ています。初期化が失敗していた場合Advertisement.isReady は永遠にfalseになります。したがって以下のようなことが起きやすいと思います。
    Advertisement.isReady がtrueになるまで待つという実装をした場合、無限ループに突入する可能性があります。
     以上のことは非常に重要ですので、気をつけて実装しましょう。準備が終わる保証はないのです。
    サンプルが問題なく動くのは広告表示ができるかどうか関係なしに広告表示するようにしているためです。表示を試してみて失敗するか成功するかという実装です。

    Advertisement.isInitialized がtrueであったときだけAdvertisement.isReady の判定処理に行くというのが正しい実装だと思います。そうすればAdvertisement.isReady が絶対にfalseにしかならないということは防げます。
    またAdvertisement.isInitialized がtrueであったとしてもAdvertisement.isReady の判定を時間制限した方が無難だと思います。 時間判定に引っかかった場合は広告を表示させずに処理を終わらせます。



    Unity Adsはデフォルトで画面横向きの広告が流れるとき、ホームボタンを右側にして横向きに強制します。UntiyのPlayer Settingsで言うとLandscape Leftになっていないと、画面が回転してしまいました。Unity Adsの方の設定で変更できればいいのですが、今のところ見当たりません。 (縦方向というのはありました。自由な向きではないです)


     
    Advertisement.isReady とAdvertisement.Show はゾーンIDを指定しない場合は、管理サイト上で設定したデフォルトの広告を表示しているみたいです。(isReady については未検証です。)
    ゾーンIDを指定しなければ、管理サイトを使って自由に変えられるので、指定しない方が便利な気がします。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity AdsのSDKはアセットストアからダウンロードします。

    Unity AdsのSDKはC#で書かれているので、C#で実装するのが一番簡単です。基本的な実装の流れはドキュメントのサンプルに書いてある通りです。 このサンプルをシーンに存在するGameObjectにつけて、GameIDと呼ばれるゲームごとに固有なIDを設定するだけで動きます。GameIDはUnity Adsの管理サイトの登録済みゲームの一覧が見れる画面で見ることができます。(日本語の場合、ゲームIDと書いてあります)

    サンプルコードの重要な所だけ説明しておきます。

     Advertisement.isSupported がtrueでない場合は、iOS/Androidではないプラットフォームを選んでいるのでUnity Adsは動きません。これはtrueだったら処理を続けるというだけのことです。

    Advertisement.Initialize  が初期化を行います。呼び出し方はゲームIDを引数に渡して呼ぶだけです。ゲームIDを間違ってしまうと、得られた報酬が実は違うゲームのものだったということになりかねません。これは十分に注意して書く必要があります。

    サンプルのOnGUIでは、Advertisement.Show を呼び出しています。特に準備できたか関係なしに実行されていることがわかります。Advertisement.isReady() はボタンの表示を変えているだけです。
    Advertisement.Show を呼び出すと広告の表示をします。必ず成功するわけではありません。このメソッドの引数にラムダ式を渡していますが、それにAdvertisement.Show の実行結果を渡します。(渡されるタイミングはわかりませんがいずれ渡されるのは確実のようです。)失敗、広告がスキップされた、広告が最後まで見られたの3通りの結果があります。サンプルでは結果をログに出しているだけですが、この引数によって処理を変えれば広告を見たときだけリワードを渡すといったことができます。

    大雑把に言うと、Advertisement.Initialize を行った後にAdvertisement.Show を呼べば広告が表示されます。広告が表示できないこともありますが、Advertisement.Show を呼んだときに処理がうまくいっていれば広告が出ます。その2つだけで実装は可能です。

    サンプルの説明は以上です。
    一応使えるものですが、実用には向かないと思います。なぜなら通信が遅いときや通信が失敗したときのことを考えられていないように思うからです。
    サンプルのままだと広告再生をしても失敗するということが多く起こると思われます。これについては今度説明したいと思います。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unityのエディター上では簡単にFPSを表示できます。
    ゲームビューのStatusボタンを押すと、画面の右中段に表示されます。
    簡易的に調べられるので便利です。
    実機上の確認には使用できないので端末の性能を測ることに使えません。
    つまり、リリースするときの機種次第では目安程度の意味しかありません。
    FPSが急激に変わったりすることを見つけるとかに使いましょう。

    FPSの表示をするにはOnGUIを使うのが簡単です。
    Unity UI(uGUI)を使ってもかまいません。 
    表示方法は何でも良いです。 

    FPSを測るには2通りあります。
    • 定義通り、1秒間に何回更新が行われたかカウントする方法 
    • 更新間隔を測り、その逆数をFPSとする方法
    1秒間を測りその間に何回更新されたかをカウントして、FPSを求めます。
    FPS = (更新回数)
    定義通りの値であり、整数値が返ります。利点は見やすく、ゲームの1秒ごとのパフォーマンスが把握できることです。1秒というのはそれなりに長いので、パフォーマンスが良い悪いという指標に使いやすいです。
    欠点は瞬間的なパフォーマンスの悪化を見逃すことと実装の面倒さです。
    1秒ごとというのを判定し、そのたびに初期化をして繰り返すといったことになります。

    更新間隔を測ることから求める方法は、測った更新間隔でこの後も処理が続くと仮定して1秒間に何回更新されるかを決定しています。
    FPS = 1 / (更新間隔) 
    になります。
    1フレーム前の更新時刻を 記録しておけば毎フレームFPSを求めることができます。この方法の 利点は瞬間的な処理の速さを知ることができることと実装が簡単ということです。欠点は瞬間的なゲームのパフォーマンスしか測れないことです。 

    FreelyAppsでは2番目の方法を使っています。
    そのまま使うと継続的なパフォーマンスがわかりにくいため、平均を用いてある程度の長さでのパフォーマンスを測っています。
    最新の更新間隔だけでなく、いくつか前の更新間隔も記憶しておくということです。



    fFPSとfAveFPSには毎フレーム更新された値が入ります。
    上では30フレーム分の更新間隔を平均し、その更新間隔からfAveFPSを求めました。
    この値をOnGUIかUnity UIで出せばいいわけです。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity 4では3種類の言語が使えました。
    C#、JavaScript(UnityScriptと呼ばれてます)、Booの3つです。 

    Unity 5になるとBooがなくなっています。
    公式のマニュアルやUnity上で新しくスクリプトを作るときの選択にBooがないのです。
    (実行は可能らしいですが、今後消えていくと思われます。)

    現在Unityで使えるプログラミング言語は C#とJavaScriptになります。
    JavaScriptの方はUnity独自の拡張が多くなされ、UnityScriptとも呼ばれます。 
    元のJavaScriptにはない書き方をしなければいけないことがあり、JavaScriptに慣れている人ほど混乱しそうに思います。

    C#が一番良いと思います。
    使われていることが多いですし、入門書に困ることもありません。Unity以外で使われているC#と同じように書けます。JavaScriptに比べて慣れればなれるほどC#が使いやすいと思います。 JavaScriptは読めれば良いくらいだと思っています。他の人が書いたコードに入っているときに知っておくと良いということです。

    C#を使う上で気をつけるべきことがあります。
    クラスのインスタンス生成時に呼ばれるコンストラクタを使わないということです。 

    Unityのシーンに保存されているデータと競合するからなのだと思います。 Unityのインスペクタ―でクラスの値を変更できる機能があるのですが、それを実現するにはコンストラクタでの初期化は都合が悪いことが推測できます。

    このページのトップヘ