箱庭ハーブblog

7年目プログラマの趣味の小部屋

完全攻略、Unityシリアライゼーション

UNITY、Unity、unity
SERIALIZABLE、Serializable、serializable
SCRIPTABLEOBJECT、ScriptableObject、scriptableobject

2017/03/13
ScriptableObjectに関して、勘違いがあったため補足。

2017/04/22
全体的に記事を修正中(やりかけ)

 
使い方を間違えなければ、非常に強力です。
Unityのシリアライズは非常に便利なのですが、使い方を間違えると不具合地獄に落ちます。

ーー

この門をくぐる者は一切の高望みを捨てよ

ーー

まさにそんな言葉が頭によぎるレベルですね。

基本的な内容は、下記の2つの公式サイトを読むのが早いと思いますが、
詳細に関してはプログラマでも文章だけではよくわからない。


Unityのシリアライゼーション
http://japan.unity3d.com/blog/?p=1630

スクリプトシリアライゼーション
https://docs.unity3d.com/ja/current/Manual/script-Serialization.html


実際に試した方が早いということで、冒頭のスクリーンショットのようになりました。

実際にやったこと



S1.cs
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    // S1 is not UnityEngine.Object!
    [Serializable]
    public class S1
    {
        // PARAMETER / STATUS

        int _num1_private; // private member don't show on editor and don't serialize.

        [SerializeField]
        int _num2_private_serializable;

        public int _num3_public;

        [NonSerialized]
        public int _num4_public_NonSerialized;

        public int Num5_property { get; set; }

        public List _numList; // Array and List are serializable.

        // PROPERTY & FIELD / METHOD

        public void CountUp()
        {
            _num1_private++;
            _num2_private_serializable++;
            _num3_public++;
            _num4_public_NonSerialized++;
            Num5_property++;
        }

        public string GetText()
        {
            return
                "num1(private) = " + _num1_private + "\n" +
                "num2(SerializeField) = " + _num2_private_serializable + "\n" +
                "num3(public) = " + _num3_public + "\n" +
                "num4(NonSerialized) = " + _num4_public_NonSerialized + "\n" +
                "num5(Auto Property) = " + Num5_property;
        }
    }
}


MyBehaviour.cs
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    public class MyBehaviour : MonoBehaviour
    {
        // PARAMETER / STATUS
#pragma warning disable 649 // SerializedField's fileds or public fields are initialized by Unity.

        public S1 _s1_public;

        S1 _s1_private; // MonoBehaviour's private fields is not initialized!

#pragma warning restore 649
        // PROPERTY & FIELD / METHOD

        // Use this for initialization
        void Start()
        {

        }

        // Update is called once per frame
        void Update()
        {

        }

        public void AllCountUp()
        {
            _s1_public.CountUp();
            if (_s1_private != null) {
                _s1_private.CountUp();
            }
        }

        public string AllGetText()
        {
            var a = "MyBehaviour's" + "\n"
                + "s1_public" + "\n"
                + _s1_public.GetText() + "\n"
                + "s1_private" + "\n";
            if (_s1_private == null) {
                a += "null";
            }
            else {
                a += _s1_private.GetText();
            }
            return a;
        }
    }
}

MyScriptableObject.cs
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    [CreateAssetMenu(menuName = "UnlimitedFairyTales/HowTo/SerializeSamples/MyScriptableObject")]
    public class MyScriptableObject : ScriptableObject
    {
        // PARAMETER / STATUS
#pragma warning disable 649 // SerializedField's fileds or public fields are initialized by Unity.

        public S1 _s1_public; // ScriptableObject's field seems to be saving value on editor, but It is not actually saved.

        S1 _s1_private;

#pragma warning restore 649

        // PROPERTY & FIELD / METHOD

        public void AllCountUp()
        {
            _s1_public.CountUp();
            _s1_private.CountUp();
        }

        public string AllGetText()
        {
            return "MyScriptableObject's" + "\n"
                + "s1_public" + "\n"
                + _s1_public.GetText() + "\n"
                + "s1_private" + "\n"
                + _s1_private.GetText();
        }
    }
}


AssetUserBehaviour.cs
using UnityEngine;

namespace Assets.UnlimitedFairyTalesHowTo.UnityAndCSharp.SerializeSample.Parts
{
    public class AssetUserBehaviour : MonoBehaviour
    {
        public GameObject _myPrefab;
        public MyScriptableObject _myScriptableObject;

        // PARAMETER / STATUS

        public S1 _s1;

        GameObject _instantiated;
        MyBehaviour _instantiatedBehaviour;

        // PROPERTY & FIELD / METHOD

        // Use this for initialization
        void Start()
        {
            _instantiated = Instantiate(_myPrefab);
            _instantiatedBehaviour = _instantiated.GetComponent();
        }

        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Space)) {
                _instantiatedBehaviour.AllCountUp();
                _myScriptableObject.AllCountUp();
                _s1.CountUp();
            }
            var canvasText = this.GetComponentInChildren();
            canvasText.text =
                "instantiated" + "\n"
                + _instantiatedBehaviour.AllGetText() + "\n"
                + "\n"
                + "myScriptableObject" + "\n"
                + _myScriptableObject.AllGetText() + "\n"
                + "\n"
                + "S1" + "\n"
                + _s1.GetText();
        }
    }
}



【操作】
1、実行し、5回キーを叩く
2、一度止め、再度実行し、5回キーを叩く
3、一度止め、MyScriptableObjectのインスペクタからResetを実行し、5回キーを叩く
4、一度止め、再度実行し、5回キーを叩く

結果からわかること

1、MonoBehaiviour上の非公開メンバは、newで初期化されない
(myMonoBehaviourのs1_privateがnullのまま)

2、ScriptableObjectのAssetを使用する際は、初期化済み。Asset初回使用=Deserializeなため
(myScriptableObjectのs1_privateが初期化済み)

3、ScriptableObjectの状態は、privateでも保存される
(myScriptableObjectのprivateメンバが20回叩いたことになっている)

2017/03/13
privateメンバは20になり一見記憶されているように見えるが、
実際のところシリアライズ対象でないため、ファイルとして保存されない
エディタを起動しなおすと、リセットされている

3-1、NonSerializedは、保存されない

3-2、privateは、そのままではReset対象外である

3-3、自動プロパティは、privateと同じようにエディタ上は見えないが保存される

2017/03/13
privateメンバと同様一見保存されているように見えるが、
シリアライズ対象でないため、ファイルとして保存されない
エディタを起動しなおすと、リセットされている

4、MonoBehaviourのメンバであるS1は、保存されない
(Instantiateされたものを使用しているので、元Prefabと直接関係ない)

個々を見ると謎めいた動きですが、
全ての結果を並べると、動作自体は非常に単純な仕様であることが分かります。

・ScriptableObjectは参照。共有される。保存される
・Editor上に表示されていないメンバは、そのままではResetされない
・Instantiateなどで参照が維持されるのは、Unityオブジェクトのみ
 (今回の調査対象外ですが、このような前提が存在しています)

なお、ScriptableObjectを複製したい場合は、
Prefabと同じようにInstantiateすると良いです。

また、ScriptableObjectAssetに値を保存したくない場合
(パラメータではなくステータス)は、privateにするか、
NonSerialized属性を付加すればよいです。

UnityのStartとAwakeの違い

UNITY、Unity、unity、Start、start、Awake、awake



基本的に、順序詳細に関しては下記URLを見れば分かります。

公式はここ
https://docs.unity3d.com/ja/current/Manual/ExecutionOrder.html

unity-の-instantiate-と-awake-とか-startが流れるタイミングを把握する
http://jigax.jp/unity-の-instantiate-と-awake-とか-startが流れるタイミングを把握する/

概要

・前提
非アクティブは未考慮

・要約
1. 公式のInitialization中にInstantiateされたオブジェクトは、同フレーム中にUpdateする。
  ただし、事実上シーンロード直後の第1フレームでしかこの状況は発生しない(※1)
2. 公式のGame Logic中にInstantiateされたオブジェクトは、同フレーム中にUpdateしない(※2)
3. AwakeはInstantiateが返る前に呼ばれる
4. StartはInstantiateを呼び出した呼び出し元の大元(※3)が終わるまで呼ばれない
5. 公式のGame Logic中にInstantiateされたオブジェクトは、同フレーム中にStartする

・Instantiate詳細
1. Behaviourコンストラクタ(アタッチされたコンポーネント群のうちの1つである。)
2. Behaviourインスペクタの読み込み
3. 他のアタッチされたコンポーネントを含む初期化が完了し、取得が許可される
4. BehaviourのAwake
5. Instantiateが戻り値を返す
6. Instantiateを呼び出した呼び出し元の大元が終わるまで
7. BehaviourのStart
8. (InstantiateされたのがStart以前だった場合=シーンロード後の第1フレーム)BehaviourのUpdate

(※1)
2フレーム目以降は、Game Logic(呼び出し元がUpdateなど)でしかInstantiateされることはないので。

(※2)
FixedUpdate中のInstantiateは試していないので、知りたい場合は試すの推奨。

(※3)
全てのスクリプトは誰かのMonoBehaviourのAwake、Start、Updateあるいはコルーチンなどから呼ばれることになる。
その呼び出しが終わるまで、InstantiateされたオブジェクトのStartは呼ばれない

使い分け

Awakeがいい
・オブジェクト単体で完結する初期化
・Instantiateが返る前に初期化しておきたい内容

Startがいい
・Instantiate呼び出し元による加工(弾の方向や配置、親子関係バインド)してから処理したい

前提

例えばこんな時
・ScriptableObjectで発射システムを作った
・全く別に、加速弾やカーブを表現できるBehaviour&Serializableパラメータのペアを作った
  ・なお、このSerializableパラメータは初期方向や初期速度もある

組み方

普通に組めば、以下の順番
Instantiate > 配置・パラメータ調整 > ロジックへパラメータを渡す

ので、
・ロジック自体、あるいは関連部品のnewはAwakeで行う
・Serializableパラメータのロジックへの適用は、Startで行う

侵攻編 4層 メモ

マクロ


/p 【1、スターダスト誘導】
/p 模様がない芝生に。3時から時計回りで6か所
/p ・
/p 【2、三連攻撃(ダイブ、チャリオット、ビーム)】
/p 6時へ集合
/p ・
/p 【3、ルナダイナモ】
/p ドーナツ攻撃なので離れない
/p ・
/p 【4、メテオストリーム】
/p   ST
/p 竜     モ
/p   MT
/p 召     詩
/p  白 学

/p 【1、倒す優先度】
/p 詩人>ST>MTの順で倒す
/p ・
/p 【2、注意技】
/p 詩人は必ず青の「アースショック」を沈黙
/p ・
/p 【3、隕石食わせ、位置調整】
/p 最後のMTは、近場の足場模様のほうきへ移動
/p ・
/p 【4、6連スターダスト】
/p 1つ目のマーク出現でスプリント
/p 2つ目のマーク出現で一斉に外ギリギリをスプリント
/p ※表示上みんなと同じ位置にいたら、遅れてます
/p メガフレア中にMTは2時の場所、その他は中央へ

/p 【1、流れ】
/p MTにダイブ
/p 天地崩壊1回目。2時の位置に集合
/p pop
/p 天地崩壊2回目+スーパーノヴァ
/p pop→LB
/p ・
/p 【2、ギミック】
/p 「拘束加速」がかかるので、ゴースト撃破後解除
/p HP47%で最終フェーズに強制移行します

/p 【全体の流れ】
/p 基本 → カータA → 基本 → カータB
/p ・
/p 【基本】
/p ファイア線 → 弱ダイブ+サンダー予兆
/p ルナダイナモ → ファイア線、サンダー予兆
/p ファイア線+チャリオット+サンダー予兆 → 3連ノヴァ
/p ファイア線+頭割りビーム → サンダー予兆
/p ・
/p 【カータライズA】
/p カータ+メテオストリーム2回 → MTから離れる 
/p ・
/p 【カータライズB】
/p カータ+チャリオット → カータ+弱ダイブ&ルナダイナモ

/p 【考え方】
/p 1、チャリオットが来るまでは、MTに重なればいい
/p 2、奇数ファイアはMTから離れる
/p 3、サンダーはデバフアイコンを見て、MTから離れる
/p 4、チャリオットから先は、ファイアサンダー考慮不要
/p ・
/p 【各種の補足】
/p ファイア:ルナダイナモ後、頭割りビーム後が集合ファイア
/p サンダー: 稲妻が落ちて、5秒後に発動します
/p ルナダイナモ:サンダー発動とセット。サンダー対象者はネール背面へ
/p 3連ノヴァ:自分に着弾してから円周に避ければ間に合う
/p 頭割りビーム:2発目のノヴァの着弾を確認したら、ネールへ集合
/p ・
/p 【カータライズ補足】
/p カータライズは、アイコンが消えるまでに誘導位置へ

/p 【カータライズ】<se.4>
/p 【 2 ⇒ 10 】

/p 【カータライズ】<se.4>
/p 【 4 ⇒ 10 】

/p 【カータライズ】<se.4>
/p 【 7 ⇒ 10 (やや9時寄り)】

ST時の立ち回り

(超える力付きのスキルスピードある場合)
w1 ヴィントから5回→ヴィントから3.5回→ヴィントからメテスト*2ディフェ→ヴィントから
w2 緑。トマホーク3回→バフと原初→(ヴォークライアンチェ)バーサク→ブレハボーラ.
w3
開幕 、ヴィント→天地→ヴィント→スカル&バフ→ボーラ→誘導トマホ→原初ウォークラ原初
撃破後、ヴィント→天地→ヴィント→スカル&バフ→ボーラ→誘導トマホ→原初スリル
w4
 10時確認。ヴィント→ブレハ繰り返し。2周目龍神でスイッチ&ホルム

MTの立ち回り


(超える力付きのスキルスピードある場合)
w1 トマホウォークラ原初→ボーラ→ボーラ→ボーラ→フォーサ原初

侵攻編 2層 メモ

マクロ


/p 9時12時メリュジーヌ、4時半ルノー石化、
/p p1:
/p p2:HP80% 12時から30秒・反時計順に3体pop
/p p3:HP60% 4体pop、dot&即死床発動
/p p4:HP45% プロセクターpop
/p ・
/p 全員共通。PTを石化しない、させない
/p 近接DPS。popを素早く倒す
/p 遠隔DPS。遠隔同士は離れる
/p 召喚。ルノーを釣る。
/p ヒーラー。ルノーを更新する

/p 次のラミアまで 30秒
/wait 18
/p あと10秒 
/wait 5
/p あと5秒 
/wait 5
/p ポップ<se.4> 
/wait 20
/p あと10秒 
/wait 5
/p あと5秒 
/wait 5
/p ポップ<se.4> 

/p <se.4>ペトリ!そっぽ向く!

極シヴァ メモ

マクロ


/p シヴァ説明
/p 前半。適当
/p 後半全体 :何か→弓→何か→弓→・・・
/p 剣 :中央安置、(剣持ち)頭割り   十or×安置   、ドーナツとふっとばし、9連範囲&氷床
/p 杖 :中央安置、(杖持ち)ヘルスト十or×安置&氷床、全体攻撃、      9連範囲&氷床
/p 弓 :即死攻撃200度、ビーム&氷床
/p ・
/p 9連範囲:中央>外周(始まりはランダム)。 MTは避けずに12時
/p 盾の動き:前半は片方がずっと持ち、後半は弓に持ち替わるときに交代
/p      武器の持ち替えは、状態バーで確認
/p      弓  →何か:レーザー>中央安置の直後
/p      杖や剣→弓 :9連範囲の後

/p   ヘイル        剣
/p 黒    MT
/p  学  ST         氷
/p              MT
/p  竜  白   
/p 詩    召   他7名
/p
/p   MT
/p   ST           氷
/p 黒学 白召        MT
/p   竜  
/p   詩          他7名
/p リューサンと詩人さんは、どっちが来ても大よそ同じ

/p <se.6>杖もちになったよ

/p <se.4>剣もちになったよ

フリーエリア

takemori
Twitter : @takemori_kondo

1. Unityと戯れてます
2. Cake3は劣化じゃないRails

iOS
coming soon...

Windows
Html Editor - Nazuna
Managed DirectX サンプル集

beginning since
2006.08.17
renewaled on
2011.06.03

最新コメント

[2013/06/14 ミューネ]
[2012/08/30 ノートPC]