Unityのシリアライゼーション

この記事はUnityのシリアライゼーションシステムの概要について説明します。

このシステムを理解すると、あなたの開発に大いに役立ちます。さらにパフォーマンスにも大きな影響を与えることが出来ます。さぁこの記事を読み始めましょう。

“もの(オブジェクト)”のシリアライズはUnityのコア部分です。Unityの多くの機能はシリアライゼーションシステムの上で構築されています:

  • スクリプトに格納されたデータを保存 – これは多くの人にとっておなじみの機能ですね。
  • インスペクターウィンドウ – このウィンドウは、インスペクトされているプロパティの値が何であるかを把握するためにC#のAPIを使用しているわけではありません。インスペクターウィンドウは自分自身となるオブジェクトをシリアライズするよう要求し、シリアライズされたデータを表示します。
  • プレハブ – 内部的には、プレハブは1つ(または複数)のゲームオブジェクトやコンポーネントをシリアライズしたデータストリームです。プレハブのインスタンスはシリアライズされたデータ上で作成すべき変更リストになります。プレハブはエディタ上のみで存在する概念です。プレハブの変更点はUnityが行う時、またはインスタンス化される時に通常のシリアライゼーションストリームでベイクされます。インスタンス化されたゲームオブジェクトは、自身がプレハブであったかどうかを判断する術を持ちません。
  • インスタンス化 – Editor上でプレハブ、シーン上にあるゲームオブジェクト、または他の何か(UnityEngine.Objectで派生されたシリアライズ可能なオブジェクト)でInstantiate()を呼び出した時、私達はオブジェクトをシリアライズし、新しいオブジェクトを作成します。さらに新しいオブジェクト上でデータを”デシリアライズ”します。(その時にUnityEngine.Objectを派生したオブジェクトの参照が何処かにあった場合は同じようにデータをシリアライズしていきます。その参照が、Instantiate()で実行されているデータの一部である場合は、UnityEngine.Objectのすべての参照をチェックします。もし参照が ”外部” のもの(例えば画像)である場合は、その参照を残したままにし、参照が “内部” のもの(例えばゲームオブジェクト)である場合はコピーに相当する参照を適用します。)
  • 保存 – Unityで”Force Text Serialization”を選択し、.unityのシーンファイルを開いた場合、YAML形式に合わせたシリアライザを行います。
  • ロード – 意外に思われるかもしれませんが後方互換性のロードもシリアライゼーションの上に構築されたシステムです。エディタではYAMLのロードでシリアライゼーションシステムを使用していますが、同様にシーンや他のアセットのランタイムのロードでも使用されています。アセットバンドルもまたそのシリアライゼーションシステムを使用しています。
  • エディタコードのホットリローディング – エディタスクリプトを変更した時、全てのEditor Window(EditorWindowはUnityEngine.Objectを継承しています)をシリアライズします。その時全てのウィンドウは破棄され、古いC#のコードはアンロードし、新しいC#コードをロードします。そして、ウィンドウを再作成、してそのウィンドウにデシリアライズされたデータを流し込みます。
  • Resource.GarbageCollectSharedAssets() – これはUnityが持つネイティブのガベージコレクタで、C#のガベージコレクタとは異なるものです。これはシーンをロードした後、もう参照されていない前のシーンを見つけ出すものです。これを実行することにより、アンロードすることができます。ネイティブのガベージコレクタは全ての外部のUnityEngine.Objectの全ての参照を受取ることが出来る場合にはシリアライザモードで実行します。これは「シーン2」をロードする時に「シーン1」で使用されたテクスチャをアンロードします。

シリアライゼーションシステムはC++で書かれています。私達はそのシステムを全ての内部のオブジェクトタイプ(Texture、AudioClip、Cameraなど)で使用しています。シリアライゼーションはUnityEngine.Objectのレベルで行われ、UnityEngine.Objectは毎回丸ごとシリアライズ化されます。それらは他のUnityEngine.Objectの参照とシリアライズ化されたプロパティの参照を含めることが可能です。

今、いくつかのコンテンツを作成し、それがうまく動作して満足しているあなたにとっては「これらのことはあまり関係がないことだ」と思うかもしれません。しかしながらあなたのスクリプトの裏側ではMonoBehaviourコンポーネントのシリアライズで同じシリアライザを使用しているため無関係ではありません。なぜならシリアライザはとても高いパフォーマンスを要求し、全てのケースにおいてC#デベロッパーがシリアライザとして期待する通りに動作しないからです。ここで私達はシリアライザを最大限に活かすために、シリアライザの働きといくつかのベストプラクティスを説明します。

私のスクリプトにあるフィールド(変数)をシリアライズするには何が必要ですか?

  • public または [SerializeField] 属性をもつもの
  • staticでないもの
  • constでないもの
  • readonlyでないもの

フィールドタイプはシリアライズ出来るタイプである必要があります。

どのフィールドタイプがシリアライズ出来ますか?

  • 自分で作成した[Serializable] 属性を持つクラス(抽象クラスでないもの)
  • 自分で作成した[Serializable] 属性を持つ構造体(Unity4.5からの新機能です)
  • UnityEngine.Objectから派生したオブジェクトの参照
  • プリミティブタイプ(int, float, double, bool, string, etc…)
  • シリアライズ出来るフィールドタイプの配列
  • シリアライズ出来るフィールドタイプのリスト(List<T>)

それでシリアライザが期待する動作をしない状況とは何なのですか?

カスタムクラス(自分で作成したクラス)は構造体のように動作します

3つのAnimalオブジェクトの参照を持つanimals配列を作成する場合、シリアライゼーションストリームは3つのオブジェクトを探し当てます。デシリアライズするときにはそれらは3つの異なるオブジェクトとなります。もし、複雑な参照を持つオブジェクトグラフをシリアライズする必要がある場合、全てのシリアライズをUnityのシリアライザに任せることは出来ないでしょう。この例はUnityのシリアライザを使用せずにシリアライズする方法になります。

[Serializable]
class Animal
{
    public string name;
}

class MyScript : MonoBehaviour
{
    public Animal[] animals;
}

MonoBehaviour内で使用するとカスタムクラスはMonoBehaviourの完全なシリアライゼーションデータの一部になるので”インライン”にシリアライズされたことになります。”public Camera myCamera”のように記述したUnityEngine.Objectの派生クラスである参照をフィールドが持つとき、カメラのデータはインラインにシリアライズされたのではなくCameraのUnityEngine.Objectへの参照をシリアライズします。

カスタムクラスはnullをサポートしていません

いきなりですがクイズです。このスクリプトを使用するとMonoBehaviourをデシリアライズした時どのくらいの割り当てが発生するでしょうか。

class Test : MonoBehaviour
{
    public Trouble t;
}

[Serializable]
class Trouble
{
    public Trouble t1;
    public Trouble t2;
    public Trouble t3;
}

答えとしてTestオブジェクトの「1回のアロケーション」を期待することはおかしくありません。TestオブジェクトとTroubleオブジェクトの「2回のアロケーション」を期待することもおかしくはありません。ですが正解は「729回のアロケーション」です。なぜならシリアライザはnullをサポートしていません。もしオブジェクトやフィールドがnullの場合、そのタイプの新しいオブジェクトをインスタンス化してからシリアライズを行います。言うまでもないかもしれませんが、これは無限の繰り返し処理を発生させてしまう事になります。そこで私達は「相対的に7レベルの深さまでシリアライズする」というおまじないとして制限を設けることにしました。その時点で私達はカスタムクラス/構造体、さらにリストと配列をもつフィールドのシリアライズを停止させます。[1]

私達が顧客のプロジェクトのパフォーマンスに関する問題を調査する時、ほとんどこの問題を見つける事ができたため、Unity4.5では繰り返し処理を発生させている場合、警告を出すようにしました。大量の警告ログを出してしまうことで散らかったようになってしまい、この警告ログを消すには警告の該当箇所を修正をするしか方法はありません。これはあまりにもなので、私達はすぐにこの問題を修正したパッチをリリースします。警告を出すことには変わりありませんが、”プレイモードに入る”ごとに警告を出すようにします。それによって狂ったスパムメールのような警告を出すことはなくなりました。あなたは今にもコードの修正を行いたいと思うでしょう、ですがそれはあなたが都合のいい時に行うべきです。

ポリモーフィズムはサポートされていません

もし以下の様なフィールドを持ち

public Animal[] animals;

犬、猫、キリンのインスタンスを格納して、シリアライズを行うと、あなたはAnimalの3つのインスタンスを持つことになります。

この制限に対処するたった1つの方法はインラインにシリアライズされたものを取得する”カスタムクラス”でのみ適用されるということです。
他のUnityEngine.Objectの実際の参照やそれ自身のシリアライズされた参照はポリモーフィズムは正しく動作します。ScriptableObjectを継承したクラスや他のMonoBehaviourを継承したクラスを作成し、参照を行います。しかしこの欠点は、どこかにMonoBehaviourやScriptableObjectのオブジェクトを保存しなければいけない点と、うまくインラインにシリアライズ出来ないという点です。

これらの制限の理由は、シリアライゼーションシステムのコア部分にいくつか理由があり、1つは、オブジェクトのデータストリームのレイアウトは事前に把握していなければいけません。もう1つは、フィールドの内部で保存されているものに何が起こっているかではなく、クラスのフィールドのタイプによります。

私はUnityのシリアライザがサポートしていないオブジェクトをシリアライズしたいです。何をすればいいですか?

最善のアプローチとして、多くの場合、シリアライズのコールバックを使用することで解決できます。それらのコールバックはフィールドからデータを読み込む前と書き込んだ後に通知することが出来ます。あなたが実際にシリアライズする時よりも、ランタイムでシリアライズ困難なデータが存在する場合にこのコールバックを使用することが出来ます。Unityが何らかのデータをシリアライズする前に、あなたはUnityが理解できるデータに変換するときに使用します。Unityがあなたのフィールドにデータを書き込んだ後に、ランタイムで元のデータに戻すために変換するときにも使用します。

仮にあなたがツリー状のデータ構造を持ちたいとします。もしUnityに直接データ構造をシリアライズさせると、”nullの未サポート”の制限のためデータストリームがとても大きくなり、多くのシステムでは一番のパフォーマンスの低下に繋がります:

using UnityEngine;
using System.Collections.Generic;
using System;

public class VerySlowBehaviourDoNotDoThis : MonoBehaviour
{
    [Serializable]
    public class Node
    {
        public string interestingValue = "value";

       //このフィールドには ‘class cycle’ を取り入れているため、とても大きなシリアライゼーションデータが作成されます。
       public List children = new List();
    }

    //シリアライズされたものを取得するためのフィールドです
    public Node root = new Node();

    void OnGUI()
    {
        Display (root);
    }

    void Display(Node node)
    {
        GUILayout.Label ("Value: ");
        node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));

        GUILayout.BeginHorizontal ();
        GUILayout.Space (20);
        GUILayout.BeginVertical ();

        foreach (var child in node.children)
            Display (child);

        if (GUILayout.Button ("Add child"))
            node.children.Add (new Node ());

        GUILayout.EndVertical ();
        GUILayout.EndHorizontal ();
    }
}

代わりにUnityが直接ツリー構造をシリアライズせずに、シリアライズされたフォーマットをフィールドに保存するにはフィールドを分けておく必要があります:

using UnityEngine;
using System.Collections.Generic;
using System;

public class BehaviourWithTree : MonoBehaviour, ISerializationCallbackReceiver
{
    //ランタイムで使用するノードクラス
    public class Node
    {
        public string interestingValue = "value";
        public List children = new List();
    }

    //シリアライズに使用するノードクラス
    [Serializable]
    public struct SerializableNode
    {
        public string interestingValue;
        public int childCount;
        public int indexOfFirstChild;
    }

    //ランタイムで使用するもの。シリアライズは行われません
    Node root = new Node();

    //Unityにシリアライズさせるフィールド
    public List serializedNodes;

    public void OnBeforeSerialize()
    {
        //UnityはserializedNodesフィールドの内容を読み込もうとします。そのまさにその直前に正しいデータをserializedNodesフィールドに書き込みます。
        serializedNodes.Clear();
        AddNodeToSerializedNodes(root);
    }

    void AddNodeToSerializedNodes(Node n)
    {
        var serializedNode = new SerializableNode () {
            interestingValue = n.interestingValue,
            childCount = n.children.Count,
            indexOfFirstChild = serializedNodes.Count+1
        };

        serializedNodes.Add (serializedNode);
        foreach (var child in n.children)
            AddNodeToSerializedNodes (child);
    }

    public void OnAfterDeserialize()
    {
        //Unityはちょうど今serializedNodesフィールドに新しいデータを書き込みました。
        //それでは今からそのデータをランタイムデータに流し込みましょう。

        if (serializedNodes.Count > 0)
            root = ReadNodeFromSerializedNodes (0);
        else
            root = new Node ();
    }

    Node ReadNodeFromSerializedNodes(int index)
    {
        var serializedNode = serializedNodes [index];
        var children = new List ();
        for(int i=0; i!= serializedNode.childCount; i++)
            children.Add(ReadNodeFromSerializedNodes(serializedNode.indexOfFirstChild + i));

        return new Node() {
            interestingValue = serializedNode.interestingValue,
            children = children
        };
    }

    void OnGUI()
    {
        Display (root);
    }

    void Display(Node node)
    {
        GUILayout.Label ("Value: ");
        node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));

        GUILayout.BeginHorizontal ();
        GUILayout.Space (20);
        GUILayout.BeginVertical ();

        foreach (var child in node.children)
            Display (child);

        if (GUILayout.Button ("Add child"))
            node.children.Add (new Node ());

        GUILayout.EndVertical ();
        GUILayout.EndHorizontal ();
    }
}

このコールバックを含むシリアライザはメインスレッドで実行されていないことに注意してください。なので「メインスレッド以外でUnity APIの使用はできない」という制限が発生します。(シリアライゼーションでシーンロードの一部ではロードスレッドで行われます。スクリプトからInstantiate()を実行した時、一部ではメインスレッドで行われます。)
しかし、あなたのデータをUnityがシリアライズ出来ないものから、Unityがシリアライズしやすいフォーマットへ変換することが可能です。

これでシリアライザの作成は終了です!

ここまで読んでくれてありがとうございます。これらの情報が、あなたのプロジェクトでうまく利用できることを願っています。

それでは。Lucas. (@lucasmeijer)

追伸:これらの情報はドキュメントにも反映させます。

[1] 正確には答えは729ではないので私は嘘をついてしまいました。これは7レベルまでの深さという制限をもたせる前のとても昔の話で、私がこのスクリプトを書いた時はエンドレスループが発生しOut Of Memoryが発生した時のものです。5年前、これらの問題を解決するために、私達がとった最初の修正としては同じフィールドタイプのものはシリアライズを行わないことでした。これは明らかにTrouble1->Trouble2->Trouble1->Trouble2 というようなサイクルのクラスを簡単に作成することが出来ない、とても堅牢な修正ではありませんでした。なのでその後、私達はそれらのサイクルも扱えるように7レベルまでの深さ制限を実装しました。ポイントとしてはそのような無限サイクルを作成してしまうことが一番の問題ではなく、サイクルについてトラブルが発生した時、何が問題かをきちんと把握していることです。

本記事はunity3d.comの記事「Serialization in Unity」を翻訳したものです。

Unity for PlayStation®Mobile フルリリース!

Unity_for_PSM_940x200

PlayStation®Mobile(PSM)のパブリックプレビューと時間を空けずに、Unity for PSMのオフィシャルリリース版を発表できることを、大変嬉しく思っています!

本当に無料なんですか?

その通りです。しかもとりわけ素晴らしいニュースですが、史上初めて、PSMデベロッパープログラムに参加している方ならば誰もが、PlayStation®Storeに自作のUnity製コンテンツをパブリッシュ出来るばかりでなく、完全に無料でPlayStation®Vita(PS Vita)向けに開発できるようになります。

開発キットも必要ないですし、手数料もかかりません。しかもPSM向けビルドオプションは、無料版のUnityとUnity Proの両方で機能しますので、あなたは1円も追加コストを払うことなしに、あなた自身のゲームをPS Vita向けに開発し、デプロイすることができるのです。

新機能について

とりわけUnity for PSMにはビルドオプションとして、アプリ内購入の各種APIがありますので、PlayStation®Store向けにF2Pコンテンツを提供するのはとても簡単です。さらに、Unity for PSMでは、ソニー・コンピュータエンタテインメント(SCE)の新しいエンターテインメントシステムであるPlayStation®Vita TV向けに配信できるビルドオブションが利用できます。 すでにパブリックプレビュー版をダウンロードしている皆様は、数々のバグフィックスがなされていることにお気づきかと思いますが、それに加えてさらなるパフォーマンスの向上と、デバイス上でのスクリプトデバッグができるようになっています。

どうすれば手に入りますか?

PSM向けのカスタマイズが加えられた専用Unityエディタを、ここからダウンロードしてください (将来的には、他のプラットフォームと同様に共通のUnityエディタに統合されることになります)。PSMにアプリをデプロイするためには、必ずSCEのサイトで登録をするようにしてください 。登録は、短時間で簡単にできます。PSM向けビルドはWindows版のUnityでのみ利用できます。

PSM向けデプロイは、UnityのPS Vita向けビルドオプションとどう違うのでしょうか?

PSM向けビルドオプションは、市販のPS Vitaに、素早く、シンプルかつ無償で開発ができるように設計されています。一方、既存のUnityのPS Vita向けビルドオプションは 、PS Vitaへの完全にネイティブなアクセスとPSNへのフルアクセスを提供するものです。さらにPS Vita向け開発キットの一部として、パフォーマンスおよびネィティブデバッギングツールの統合スィートである、Visual Studio インテグレーションと Razor CPU/GPU パフォーマンスツールも入手することになります。

何故、PS Vitaをターゲットにするのですか?

もしあなたが、コンソール開発に挑戦したいと考えているなら、 PS Vitaを開発ターゲットとすることは、PlayStation®プラットフォームでのやり方や決まり事に慣れるのに最もうまい方法だと言えるでしょう。しかも、手持ちのモバイルゲームをPS Vita向けに移植することはとても簡単なので、ぜひ挑戦してみてください!

Unity_for_PSM_800x425_b

* “PlayStation” は株式会社ソニー・コンピューターエンタテインメントの登録商標です。

本記事はUnity3D.comの記事「Unity for PlayStation®Mobile – full release!」を翻訳したものです。

Company News and Info、Technology、Erik Hemming が投稿 (投稿日:2014 年 6 月 20 日)

Unity for PlayStation®4が登場します!

blog-header-option-2

Unityは、 PlayStation®4 のためのUnity4.3の完全なパブリックリリースを発表できることを誇りに思います 。開発プロセスを通じて、私たちを支援してくださったすべての開発者の皆様に 大いなる感謝をいたします。 皆様の助力はすこぶる有益なものであり、かつ高く評価されるべきものであります!

PS4のどんな機能が利用できるのか?

Unity for PlayStation®4は、Shader Model 5やコンピュートシェーダーのような強力な次世代技術の数々をサポートいたします。これらを使うことで、開発者の皆様は真に驚異的なビジュアルを作成することができるようになります。それに加えて、DUALSHOCK®4に搭載されている全ての機能をサポートしていますので、各コントローラに搭載されている個々のスピーカー、タッチパッド、バイブレーション、ライトカラーなどへすぐにアクセスすることができます。

さらに、Unity4.3 for PlayStation®4は、デプス検出、手および顔のポジショニングやPlayStation®Moveの検出などのPlayStation®Cameraを利用した認識技術をサポートしています。また、「プレイステーション 4」(PS4™)の魅力を高めるバーチャルリアリティシステムとして、ゲーム体験をさらに豊かにするProject Morpheusとのインテグレーションも、すぐにフルサポートいたします。

Unity for PlayStation®4を手に入れるには、どうしたらよいでしょうか?

PlayStation®4のパブリッシャー登録からはじめましょう。登録手続きが完了すると、開発者はUnity for PlayStation®4をダウンロードして、ゲーム制作の開始が出来ます。とても簡単です。

登録が終わり利用をはじめたら、PlayStation®4開発者向けサイトにあるUnityフォーラムを必ず訪問しましょう。Unity for PlayStation®4を使用する開発者の皆さんには正規の投稿資格がありますので、PlayStation®4向けにUnity開発者の皆さんが知識と経験をシェアするために集う場所として、利用してください。

Unity for PlayStation®4を使ってゲームを公開することができますか?

もちろんです。実際、すでにいくつかのUnity製PS4タイトルが出荷されていますし、さらに多くのタイトルが製作中です。最近のリリースとしては…

Stick It To The Man (Zoink! Games)

inarticlestickit

ある日事故に遭ったRayが目を覚ますと、頭(というか脳みそ)からピンク色のスパゲッティみたいな第三の手が生えていた!この手は彼に素晴らしい力を授けてくれた。どうやら、この手を使うと世界を大きく変えてしまえるらしいのだ。折る、破る、シールをはがし、別の場所に貼り付けての大アクション!こちらでチェック!

CounterSpy™ (Dynamighty)

blog-header

時は冷戦時代。激しい宇宙開発競争を巡って二つの超大国が対立し、やがてそれは月の破壊へと向かいつつあった。そしてその状況を止められるのは、C.O.U.N.T.E.Rのエージェントであるアナタだけ。プレイヤーはスマートなステルス戦闘スキルを使って、敵基地に潜入し、敵がもくろんでいる月破壊活動を妨害せよ。こちらでチェック!

次のステップ

現在、開発チームは二つのゴールに向けて大いに取り組んでいます。まずは4.3リリースに向けて、さらなる改善と最適化、サポートを継続すること。同時に、できる限り早くUnity 5.0をPlayStation®4にお届けするべく、取り組み中です。

やるべきことがたくさんがあります都合上、私たちは、Unity 4.x サイクル中で予定されておりますいくつかのリリースをUnity for PlayStation®4向けにリリースすることはいたしません。しかし、4.3系の定期リリースを継続して続けますし、しかるべき時が来ればUnity 5.0の最初のαおよびβリリースに含めて、これらのリリースをご提供するつもりです。詳しいことが決まり次第、PlayStation®4向けUnity 5.0ニュースを更新いたしますので、よろしくお願いします。

ここに至るまでの道のり

開発チームは、その他のPlayStationプラットフォーム向けに開発をしてきた者たちですし、オリジナルのPlayStationの時代からSonyプラットフォームに取り組んできた者たちばかりです。私たちは、自分たちのありとあらゆる経験を喜んでUnityの開発に注ぎ込んでおりますし、Unityコミュニティが将来どんなものを必要とするのか知りたいと願っております。そのために多くの開発者の皆様と密接に働くことができるのが楽しみです!

本記事はUnity3D.comの記事「Unity for PlayStation®4 is here!」を翻訳したものです。

Company News and Info, Technology、Daniel Bratcher が投稿 (投稿日:2014 年 6 月 16 日)

Unite Japan 2014 本社海外スタッフの講演動画を同時通訳音声にて一般公開開始

samnale_UNITE2014_image-logo1

2014年4月7-8日に行われた大規模カンファレンス「Unite Japan 2014」の講演で行われた、Unity Technologies本社海外スタッフによる14講演の動画を一般公開いたしました。音声は日本語での同時通訳音声となっております。イベントに足を運べなかった方も、この機会にぜひご視聴ください!

講演の一覧および動画の視聴はこちらから

 

 

DSC_6181

DSC_6220

Unite Japan 2014 講演動画の一部を一般公開開始

samnale_UNITE2014_image-logo1

2014年4月7-8日に行われた大規模カンファレンス「Unite Japan 2014」での講演のうち、24講演の動画を一般公開いたしました。イベントに足を運べなかった方も、この機会にぜひご視聴ください!

講演の一覧および動画の視聴はこちらから

 

 

 

samnale_DSC_6135

Unity Technologies開発スタッフによる動画は日本語音声で近日一般公開する見込みです。お待たせして申し訳ございませんが、公開まで今しばらくお待ちください。