FreelyApps

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


    アプリ『トランプ・7並べ』を公開しました。
    Android/iOS https://goo.gl/zsFces

    タグ:Unity5.3

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity5.3から5.4になったときにNetworkMatchつまりマッチメイキングの処理の呼び出し方法が変更されました。5.3以前の処理を書いている場合は多少修正が必要になると思います。
    書き直すのは面倒ですが、修正されているところ(マッチングのフィルタリング)があるのでやってみる価値があります。
    34
    左が5.3で右が5.4のUnityEngine.Networking.Matchのクラスの一覧です。5.3では~Requestと~Responseがたくさんあります。これらはマッチングを行うときのメソッド(NetworkMatch.CreateMatchなど)に渡す情報とマッチング処理を行ったときの結果を返すためのものです。
    5.4ではマッチングを行うときのメソッドに直接値を渡す方式のみになったため、~Requestがなくなったようです。(5.3では~Requestを渡す方法と直接値を渡す方式がありました。値を渡す方式だけ生き残ったということです。)
    ~Responseは元々冗長なクラスでした。~Responseは成功したかどうかとサーバーでのエラーについての情報とそれぞれのマッチング処理の情報という形式です。3つのメンバーしかないので、わざわざクラス化しなくても良いという判断で5.4でなくなったのではないかと思います。5.3のCreateMatchResponseJoinMatchResponseは同じ情報を持つのに違うクラスでしたし、メンバーもわざわざ基本型で書いてありました。5.4では整理されてきれいにまとまった感じです。
    あとはMatchDescMatchInfoSnapshotに変わりました。なぜ……

    5.3でCreateMatchなどを使っていた場合は、5.4のCreateMatchに合わせる必要があります。呼び出し引数が変わっているのでその対応をすれば動くはずです。いくつかのパラメータは空文字や0で無効にして呼ぶことが多いでしょう。
    requestDomainという値が重要でこれが同じ値のものとマッチングするようになっています。この値が違うとマッチングしないようになっているので、通信する内容が変わった場合は値を変えておくと問題が起きません。アプリのバージョンとかと同じように通信のバージョンを管理しておくと良いと思います。

    以前はrequestDomainに対応しているような機能があったのですが、うまく機能してなくて互換性がない状態でマッチングが起こっていました。当然、通信が発生したときに情報が解釈できずエラーになって失敗します。5.4からはこれが動いているというのを確認しました。(Windows、Mac、Android、iOSにて)
    これがあるとUnity Multiplayerに変更のあるアプリのバージョンアップを行うことができます。バージョン違いのものを住み分けることで共存できます。 

      このエントリーをはてなブックマークに追加 Clip to Evernote
    Unity5.3からJsonUtilityという機能が追加されました。
    オブジェクトをJSONにすることとJSONからオブジェクトを作る・値を入れることができます。JSONはオブジェクトを文字列で表現する方法のことであり、単純な仕組みでエンコードされるために広く使われているフォーマットです。C#ではxmlやバイナリの形式にオブジェクトをシリアライズ(ファイルに保存できるような形式にすること)できますが、JSONはライブラリを使わないと使えません。 
    Unityで公式にサポートしてくれると使いやすくなるので、データの持ち方に1つ選択肢が増えたことになります。

    JSONの形式は文字列であるので、拡張性が高く柔軟だというのが特徴だと思います。あるオブジェクトAをJSONにして、別のオブジェクトBにデシリアライズ(オブジェクトに戻すこと)が可能です。なぜならJSONが文字列であり、データに何が含まれているのかを表しているだけだからです。

    これと異なる特性をもつのがバイナリです。データをメモリ上での表現をそのまま出力したような機械にとってわかりやすく、人間にわかりにくい表現です。
    バイナリ形式(BinaryFormatterを使った場合)は保存したオブジェクトと同じ型でデシリアライズしないとエラーになったり、バイナリにはフィールドがあるのに復元するクラスにはフィールドがなくなった場合にもエラーが起きます。これはBinaryFormatterで作ったデータになんらかの型情報が含まれていることを表しているでしょう。データがどういったクラスにより作られ、どういった要素が含まれているかがデータにあるために変更すると一致しなくなるのです。(どういった条件でデシリアライズできなくなるかまで調べていませんが、基本的にはフィールドの変更はしない方が良いと思います。)
    バイナリ形式はクラスの型を変更できないデメリットがありますが、文字列の解釈がないため一般的に変換が高速です。バイナリ形式の場合で将来拡張するなら、クラスに余分なフィールドを入れておくことが一番良い対策になります。余りに多くの使わないパラメータがあると無駄が大きいので、どれくらいの容量を取るかは判断が難しいところです。

    JSONを使う方が頻繁にアップデートが存在するアプリには合っているので、これを使っていた方が困らないことが多いでしょう。
    JsonUtilityは現在のところ型を利用する方法のみが使えるようです。JSONにシリアライズ、JSONからデシリアライズするためには変換用にクラスや構造体が必要だということです。 (intの配列とかをそのまま渡してもシリアライズできないです。)JSONに対してそのままデータを検索したり、データを追加したりするのは将来的にはあり得ると思います。(ドキュメントに「The JSON Serializer does not currently support working with ‘unstructured’ JSON」とあります。currentlyなので将来的にはサポートする気がします。)


    JsonUtility.ToJsonはそこまで注意することはないです。publicなフィールドをJSON化してくれます。NonSerialized属性をつけるとそのフィールドは無視されます。JSON化されるものが何かはインスペクタ―上で表示されているものと一致しているようです。例えば[SerializeField]がついたprivateなフィールドはJSON化されます。また逆に[System.NonSerialized]をつけるとpublicなフィールドでもJSON化されなくなります。
    ToJsonの第一引数はオブジェクトを渡します。
    [System.Serializable]
    public class Test
    {
        [SerializeField]
        private int a = 1;
        public int b = 2;
        public int[] c = { 12345 };
     
        public float f = 0.22f;
    }
    というクラスを定義して、これを作りJSONにしてみます。
    Test test = new Test();
    var json_test = JsonUtility.ToJson(test);
    というようにすると、json_testには以下の文字列が入っています。
    jsonNotPrettyPrint
    fの値が0.22に一致しないのは誤差のせいでしょう。JSONのデータはわかりやすい構造をしていて、データが正しいかのデバッグがしやすいです。

    第ニ引数にtrueを渡すと、下のように見やすく出力されます。改行とかインデントのための空白やタブは情報を持っていないので、情報としては無駄です。保存する場合やプログラム内でのやり取りにはPrettyPrintは使わないようにし、デバッグ用に使うのが良いです。
    jsonPrettyPrint


    JSONからオブジェクトを作るのは2通りあります。
    JsonUtility.FromJsonJsonUtility.FromJsonOverwriteです。
    Test obj = JsonUtility.FromJson<Test>(json);
    のようにすると、文字列jsonからTestクラスが作られます。使い方はシンプルなのですが、与える文字列jsonには注意が必要です。文字列はJsonUtility.ToJson等を使って同じクラスを表すJSONを使うのが通常でしょうが、特に制限はないです。全く異なるクラスから作られたJSONを渡したり、自分で文字列を作って渡しても問題ありません。
    そういった場合、余分なフィールドや型の異なるフィールドが出てくるでしょう。FromJsonはうまくできているようで、これらのものはすべて無視されます。FromJsonは型パラメータに渡したクラスのシリアライズできるフィールドを対象にし、JSONからフィールドの名前と型が一致したものだけ解釈しています。
    対応するものがないフィールドについては注意が必要で、このときフィールドはデフォルト値にされます。(例えばintだったら0です。)

    Unity5.3.4f1で試したところ、対応するものがなくてもデフォルト値にされてないように思います。
    [System.Serializable]
    public class Test2
    {
        public int a = 0;
        public int c = 5;
    }
    というクラスを新たに作り、
    Test test = new Test();
    var json = JsonUtility.ToJson(test);
    print("json:" + json);
     
    var from_json = JsonUtility.FromJson<Test2>(json);
    print("from_json:" + JsonUtility.ToJson(from_json));

    のように処理を書いてみました。 Test型のインスタンスをJSONにしたものを出力し、そのJSONからTest2のインスタンスを作っています。作ったTest2型のインスタンスのフィールドがどうなっているか知るため、JSONで出力しています。
    実行した結果は以下のようになりました。
    fromjson

    jsonにはcというフィールドがないので、Test2としてデシリアライズするときにcがデフォルト値0になるかと思ったのですが、そうはなりませんでした。JsonUtilityのバグかドキュメントのミスかも知れません。


    JsonUtility.FromJsonOverwriteはデシリアライズするJSONに復元しようとするクラスの対応するフィールドがない場合、そのフィールドには何もしません。
    上に述べたようにJsonUtility.FromJsonでも何故かフィールドの値が変更されてないため、現状ではインスタンスの生成の有無で使い分けることになると思います。

      このエントリーをはてなブックマークに追加 Clip to Evernote
    以前、同じ内容で記事を書いたのですが、より良い方法がUnity5.3では行えるようです。

    rn-custom_coroutine
    Unity5.3で新しく使えるようになったカスタムコルーチンというものを使います。 公式の説明を見るとCustomYieldInstructionクラスを継承して、keepWaitingプロパティのgetをオーバーライドすれば新しいyieldオペレーションが作れます。
    yieldオペレーションとは、yield return ~とするときの~の処理のことを言っているのだと思います。(この記事ではそうだと思って書くことにします。)
    keepWaitingがtrueであればyieldオペレーションが処理を返さず継続し、falseになると処理を抜けます。このプロパティにDebug.Logを仕込んだところ毎フレーム呼ばれていたので、コルーチンで無限ループ内にyield return 0;というような書き方で行うのと同じと考えて良いでしょう。

    以前の方法のあまり良くない点は、処理をクラス内に書いていなければいけなかったため、あるクラスのstaticメソッドにするかクラス内での利用しかできないようになっていたことです。色々なところから自由に呼び出すということを考えると、その処理は独立した1つのクラスになっている方が見通しが良いのでそうすべきだと思っていました。
    メソッドに処理がある場合は、呼び出し方がStartCoroutineを介するというのもいまいちだと思います。StartCoroutineを使うと、渡すパラメータの個数の制約があること、わずかにコードが増えること、文字列での呼び出しだとエディター上で編集しにくいことが良くないです。
    新たな方法では導入が手間ですが、その後の作業を減らしてくれるため良い方法だと思います。WaitUntilやWaitWhileを使っても同じことができますが、よくある同じ処理をいちいち書くのは無意味なので、以下のようにカスタムコルーチンを作りました。

    上のソースは、ある時間処理を待たせるがスキップも可能であるというyieldオペレーションになります。使い方は
    yield return new SkippableWaitForSeconds(10, () => Input.GetMouseButtonUp(0));
    のようにします。この例だと10秒待つ処理であり、もしその間にマウスの左ボタンが上げられたらスキップするということになります。
    スキップするかの判定を外から渡せるため、色々と使えるようになっています。

    このページのトップヘ