Google

2011年10月19日水曜日

別のGameObjectにアクセス

Component詳細の前にGameObject検索に横道。
今までの例では、スクリプトに割り当てられたGameObjectを触っていました。
そして紐付けはUnityのGUIからドラッグ&ドロップだったため、実行前に決められていました。

じゃあ実行時に別のGameObjectを、、、という場合はどうでしょう。
複数方法がありますが、代表的なものがこちら。
  • 実行時にFindで検索
  • publicメンバにして実行前に紐付け
前者は速度的には厳しいですが、動的な生成破棄をするなら必須です。
後者はオブジェクト指向的に問題なのと、GUI操作必須ですが高速です。

まずは前者から。
GameObjectのstatic関数、Findで検索が可能です。
宣言はこんなかんじ。

static function Find (name : String) : GameObject

ヒエラルキー上で表示されている名前を入れると検索し、該当すればインスタンスが戻るという仕組みです。
しかしこの名前、GameObjectの最基底クラスであるObjectのnameメンバなのですが、シーン上でユニークな訳ではありません。
実際、シーン上には同じ名前のGameObjectがいっぱい作成できてしまいますし。
プレハブでもそうですが、名称を自力で固有に変更しなければ欲しいインスタンスにアクセスできないのでご注意を。

以下は具体例です。 (公式から抜粋)
このようにヒエラルキー構造を指定して検索を掛けることも可能です。
スラッシュから始まる検索の結果は、「ルートに存在するオブジェクトである可能性がある」と公式には書いてありますが、実際にはルートにあっても階層下にあっても検索できてしまいます。
/Monster/Armのように階層が深くユニークなら実質問題ないようです。

他にも手はあって、タグを利用した名前付けで検索することも可能です。

static function FindWithTag (tag : String) : GameObject

これもまたユニークというわけではないので自力で設定しましょう。
なお、遅いと言いましたが、Update()で書いたりすると恐ろしく遅くなります。
Awake等で検索し、自前メンバに保持しておくのが一般的です。


長くなりましたが続いて後者。
上記のコードでGameObjectのhandがpublicになっています。
オブジェクト指向の世界で、何も考えずにメンバをpublicにすることは通常ありません。
が、こうしておくと、Unityのインスペクタにそのメンバが表示されるようになります。
以前この値をGUIから変更可能と書きましたが、クラスメンバの場合はここから静的にインスタンスを指定することが可能です。
要は、Findでやることを実行前に確定させるだけの事。
反則感はありますが、これも推奨のようです。

そしてそのやり方は簡単。
publicなクラスメンバを持ったスクリプトをGameObjectに紐付けます。
そのGameObjectを選択すると、インスペクタのスクリプト欄にそのメンバが出ます。
ヒエラルキーからドラッグ&ドロップで終了。

未設定ならNoneに、入ってれば名称がでます。上書きもおk

なお、マテリアル等ならプロジェクトビューからでも紐付けできます。

6 件のコメント:

  1. ひとつ質問があります。
    この記事を参考にGetComponentを使ってみたのですが、
    GetComponentするとUnityごと落ちてしまいます。
    下記のスクリプトはtargetを追いかけるものです。

    public GameObject target;

    private Vector3 vec;
    private float speed = 0.07f;

    void Update()
    {
    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(target.transform.position - transform.position), 0.07f);
    transform.position += transform.forward * speed;
    }

    上記のスクリプトのtargetに別のスクリプトからselectedTargetを送ります。

    上記スクリプト名 ms = (上記スクリプト名)GetComponent("上記スクリプト名");
    ms.target = selectedTarget.gameObject;

    実行した瞬間Unityごと落ちるのでエラーとかわからないです。

    返信削除
    返信
    1. コメントありがとうございます。
      ソース全体を見ないと全ての原因を指摘するのは困難ですが、憶測は可能です。

      >上記スクリプト名 ms = (上記スクリプト名)GetComponent("上記スクリプト名");

      上記カッコで包まれたスクリプト名は、シーン上及びこのスクリプトで宣言されているGameObjectの名前である必要があります。
      もしスクリプト名=GameObject名なら問題はありません。

      また、Unityごと落ちるのはPCの調子の関係もありそうですが、実行時にnullアクセスでUnityが引きずられて落ちてる可能性も高いと思われます。
      サンプルでは横着してますが、業務レベルならランタイム、つまり実行時にnullの可能性があるものは事前にnull判定のif文でクラッシュ回避コードを書くべきでしょう。
      そうすることでいきなりクラッシュする前に問題の原因に近づきやすくなります。

      Unityが丸ごと落ちるのは全く別の原因(レジストリ絡み)が考えられるため、OS等も含めたクリーンインストール等で改善される場合もあります。

      拙い回答で恐縮ですが、ご参考になれば幸いです。

      削除
  2. 返答ありがとうございます。
    Null回避用のifを用意してみたのですが、変わらずに落ちてしまいます。
    試しにtargetにselectedTargetを送る

    ms.target = selectedTarget.gameObject;

    をなしにすると落なくなりました。
    msの状態を見ようとDebug.Log (ms.target);をやってみたら同じく落ちました。
    どうもtargetにアクセスしようとすると、落ちるみたいです。

    返信削除
    返信
    1. 再度のコメントありがとうございます。
      落ちなくなったのはなによりですが、根本的な解決には至っていませんね、、、。
      ソースの断片を見ている限りでは、これでUnityごと落ちるのがかなり解せないところです。
      私も以前実機でのみクラッシュという現象に悩まされた事がありましたが、UnityのMonoはMSとも違う独自のエンジンで手も足も出ず、以下のような策をとったことがあります。

      ・別のアクセス方法で試す(今回の例だとpublicメンバをsetterメソッドに変更等)
      ・コードの造りを変える(外部からのアクセスという形式を止め、targetのObjectを知っているソースと合体させる等)。
      ・Unity Editorのログと動いているOSログ(Windows App log等)で追いかける

      Unityのエンジン自体にもバグは潜んでいますので、回避しつつ直るまでうまく付き合うという逃げ方な訳です。
      ログ情報にどれだけ残っているかは分かりませんが、以下のURLを参考にログも確認されては如何でしょう。
      http://answers.unity3d.com/questions/9739/how-can-i-find-editor-log-file.html

      削除
  3. 質問にお答えしていただき、ありがとうございました。
    策とURLを参考にがんばってみます。
    今、卒業制作としてUnityを使ってゲームを作っているのですが、
    周りにUnity経験者がいないので問題解決に時間がかかってしまいます。
    もしかしたら、再び質問させていただくかもしれません。
    差し支えなければ、その時はよろしくお願いします。

    返信削除
    返信
    1. 良い解決策を提示できず大変失礼いたしました。
      今回のようにイレギュラーなエラーが出る場合、unityPackage化して新規プロジェクトに全て引越しするなど、別の手を使って時間を短縮してみてください。
      またなにかございましたらご遠慮無くご質問下さい。

      削除