プレハブに自身の参照を持たせた時にハマった挙動
先に結論を書きます。
自分自身の参照をフィールド(仮にmyselfとする)に持ったプレハブをInstantiateすると、生成されたcloneオブジェクトのmyselfフィールドはプレハブの参照からcloneオブジェクト自身の参照に置き換わります!
また、スクリプトのpublicなフィールドに自分自身をアタッチされたプレハブのクローンに対してinspectorからapplyを押すと、クローンにアタッチされたプレハブがクローン自身のオブジェクトに勝手に置き換わります(!?)。
プレハブ内の参照関係を壊さないようにする為に必要な仕様だということです。
解決するにはinstantiateした後cloneが持つ自身の参照フィールドをプレハブの参照に置き換えてあげる必要があります。
では以下蛇足。
Unity初挑戦からずいぶん時間が空いてしまいましたがまた少し触っています。
まずは練習として下のミニゲームの岩を投げる側目線のゲームを作ってみようと思ってます。
↑マリオパーティ3のごろごろいわころがし。1vs3のミニゲームで3人側は一番上を目指して坂を登り、一人側は岩を落として3人の邪魔をする
で、とりあえず岩を投げられるようにしようと思ってこんな感じのものを作ってみました。敵とかそういうのは一切まだできておらず、ただ岩を投げたら次が装填されるというだけのものです。
岩は等間隔で左右に動いていて、矢印キーの右左で向きを調整してスペースで投げます。投げたら2秒後に次の岩が出現してまた投げられるようになるというのがここまでの実装です。
この動きは全部rock_prefabのスクリプトに書いてあり、投げた後に自分自身の参照をInstantiateに渡して次の岩を出現させるようになっています。
この自分自身の参照をStartメソッドの中で毎回Resourcesからロードすれば万事上手くいったのですが、publicなフィールドにアタッチするようにすると初期位置に岩が出現せず、その場で分裂するようになってしまいました。
Hierarchyを見てみると1枚目の画像ではRock(clone)が複数出現しているのに2枚目ではRock(clone)の次はRock(clone)(clone)が出現しています。
つまりこれはプレハブではなくシーン上のオブジェクトをcloneしてしまっているということです。スクリプトも全く同じはずのcloneがなぜ別のものをcloneするのかと私はここでパニックになりました。
さらにHierarchy上のRock1オブジェクト(Rock_prefabとリンクしている)のスクリプトにRock_prefabをアタッチした状態でApplyやRevertを押すとRock_prefabがRock1に置き換わる!
その時Rock_prefab側は変化なしです。えー……
で、まあ結論としては冒頭に戻ります。参照が勝手に書き換わってしまうので上書きすればいいんですね。以下のように実装しました。
var clone = Instantiate(rockPrefab);
Rock rockScript = (Rock)((GameObject)clone).GetComponent(typeof(Rock));
rockScript.rockPrefab = this.rockPrefab;
rockPrefabフィールドにはこのスクリプトがアタッチされたprefab自身の参照が入っています。publicで宣言されていてinspector上でRock_prefabプレハブがアタッチされています。Rockはこのスクリプトのクラス名です。
Instantiateは生成したObjectを返すので、それが持つRockスクリプトを取得してrockPrefab フィールド(生成されたObjectの参照が入っている)を自身のrockPrefabフィールド(プレハブの参照)で上書きしています。
ただ、これだとinspector上でApplyを押した時の問題は解決してないんですよね。Hierarchy上のオブジェクトはプレハブとのリンクを切ったほうがいいかもしれません。
リンクはメニューのGameObjectからBreak Prefab Instanceで切ることができます。
以上の事柄は全てUnity質問スレの親切な有識者の方々から懇切丁寧に教えていただきました。本当にありがとうございました。
私もいつか初心者を導けるような技術者になりたいものです。