Unityで簡単な2D脱出ゲームを作ってウェブサイトで公開してみよう 〜スクリプトでオブジェクトをセット〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
動画には存在しないサムネイル

この記事を三行にまとめると

動画の5:16〜8:50あたりのことをやります
ちょっとだけ分かったこともあるので発表します
焼肉定食とか量子力学とか青銭万選とか西尾維新みたいに
今回は前回の記事で説明しきれなかったスクリプトからゲームオブジェクトをセットする方法を紹介します。動画的には前回と同じです。



この動画の5:16〜8:50あたりのことをやります。



GameObject.Find

今まで変数にゲームオブジェクトを突っ込む場合はインスペクターを使う方法を採用してきましたが、スクリプトの中だけでセットすることもできます。

スクリプトでセットするには「GameObject.Find」という関数を使います。

public class Item : MonoBehaviour{
  private GameObject obj;
  protected Dialog hint;

  void Start() {
    obj = GameObject.Find("Hint");
    hint = obj.GetComponent();
  }
}

こんな感じです。この場合は「Hint」という名前のオブジェクトを取得しています。インスペクターでHintオブジェクトをセットする場合と内容的には一緒ですので、その変数に対してGetComponentなどを使うことができます。

この方法なら変数がインスペクターに出てこなくても良いので、スコープがprivateやprotectedの変数に対してオブジェクトをセットすることができます。



取得できたりできなかったり

やり方自体は簡単なんですが注意しなきゃいけない点もあって、まずこの関数はオブジェクトがアクティブじゃないと取得できません。前にタグ機能を使った時に紹介した「GameObject.FindGameObjectsWithTag」と同じですね。なので上記の場合もHintオブジェクトが非アクティブだと取得できずにエラーになってしまいます。

それから同じ名前のオブジェクトが複数あると、その中のどれか一つしか取得できません。しかもどれを取得するかはこっちで指定できない。だからもしもヒエラルキーにHintという名前のオブジェクトが複数あると自分の欲しいHintオブジェクトが取得できない可能性がある。アクティブなHintが一つしかなければそれがセットされますが。

同名のオブジェクトがあるとどれがセットされるかは全くのランダムというわけではなく決まりごとのようなものがある気もするんですが……自分で試した限りでは何となくそんな動きをしていたように見えました。たぶん最後にアクティブになったオブジェクトじゃないかと思うんだけど……例えば本作の場合は「Item」って名前のオブジェクトはいくつもあるんだけど、アイテムを取得するとそのItemオブジェクトがアクティブになっていくから、FindでItemオブジェクトを取得する場合、最後に取得したItemが取れるんじゃないかというのが僕の推測なんですが……でも検証はしきれてないので自信はないです。誰か知ってたら教えてください。

まあそんな運任せなプログラムを作るのもあれだから、Findを使う時は確実にこいつが取れるっていう状況を作った方が良いですね。オブジェクト名をかぶらないようにしておくとか。

あとはそう。アクティブな状態じゃないと取得できないとは言いましたが、親が非アクティブな状態だとやっぱり取得ができません。Hintはアクティブだけどその親が非アクティブだと上記のコードでも取れない。

反対にHintは非アクティブでもその親がアクティブであれば、親からたどって取得することはできます。

本作の場合、Hintはヒエラルキーでこうなっています。

ヒントのヒエラルキー

DialogはCanvasオブジェクトで、ゲーム中はずっとアクティブです。なのでこいつからたどればHintが非アクティブでもFindが使えます。

public class Item : MonoBehaviour{
  private GameObject parent;
  private GameObject obj;
  protected Dialog hint;

  void Start() {
    parent = GameObject.Find("Dialog");
    obj = parent.transform.Find("Hint").gameObject;
    hint = obj.GetComponent();
  }
}

親からたどる場合、Findの対象はその子供だけになります。上記の例で言うとDialogの中にHintという名前のオブジェクトが一つしかなければ、他のところにHintオブジェクトがあってもターゲットを絞り込めます。



newとoverrideについて

前回の記事でnewとoverrideの違いがいまいち分からんと言いましたが、ちょっとだけ分かったこともあるので発表します。でけでけでけ……

変数にスクリプトをセットする時、クラス名を型にするという説明を何度かしたかと思うんですが、その時に親クラスを型にした変数を作るとnewとoverrideで異なる動きをします。

例えばこんなコードを書いた場合。

//親
public class Parent : MonoBehaviour {
  public void Func1() {
    Debug.Log("親です");
  }

  public virtual void Func2() {
    Debug.Log("親です");
  }
}

//子供
public class Child : Parent {
  public new void Func1() {
    Debug.Log("子です");
  }

  public override void Func2() {
    Debug.Log("子です");
  }
}

//他人
public class Other : MonoBehaviour {
  public void Func() {
    Parent parent = new Child();
    parent.Func1(); //親です
    parent.Func2(); //子です
  }
}

他人クラス(Other)のFunc関数を呼び出した時、変数の型は親クラスのParentにしていますが、変数には子供のクラスをセットするという処理を行なっています。ここでFunc1とFunc2をそれぞれ実行するとどうなるかと言うと、Func1(newをした方)は「親です」と出力され、Func2(overrideした方)は「子です」と出力されます。

つまりnewをした場合は親と子のFunc1は別物(?)として扱われ、overrideした場合は親のFunc2を子が上書きしているみたいな感じかなと思われます。だからnewをした方では親のFunc1がそのまま呼び出されて「親です」と出力されますが、overrideの方では親のFunc2を子供が上書きしている状態だから子供のFunc2が呼び出されて「子です」と出力されたと。

この使い分けを活かせる日が来るかは怪しいですが、とりあえず上記のような変数の作り方をした時に、親の関数を直接呼び出したいならnewを使う、子供の関数を呼び出したいならoverrideを使うってことだけ覚えておけば、何かの時に役立つかもしれません。






僕も別に説明が上手いわけではないですけど、専門的なことの説明ってなぜかやたら小難しく書いてあって、すっと頭に入ってこないこと多くないですか? 政治家の答弁っつーの。

このnewとoverrideの説明もさ、公式のリファレンスっぽいやつを読むと「new修飾子はアクセス可能な基底クラスのメソッドを非表示にして、override修飾子は基底クラスのvirtualメソッドを拡張します」みたいなことが書いてあったりするんだけど、まだそこまでプログラミングに慣れてない人がこの説明を読んでも「だから何?」って思うよね。僕もこれだけ言われても何のこっちゃか分からんちん。まず基底クラスってどのクラスなのよ。落ちこぼればかりが集まる、ドラマでは最終的にトップになる素質を持った人たちのクラスですか? それとも励起されてない、エネルギー準位が一番低い電子たちが集まったクラスのこと?

もう少し掘り下げようと思っても「newを使うと基底クラスから継承されたメンバーを明示的に隠ぺいでき、それによって派生バージョンのメンバーで基底クラスのバージョンが置き換えられます」とか「overrideは継承したメソッド、プロパティ、インデクサー、またはイベントの抽象実装または仮想実装を拡張したり修飾したりする際に必要です」とか、さらに知らない単語が増えるわけよ。インデクサー? エリクサーの下位互換的アイテム? 抽象実装とか仮装実装とか、急にそんな四字熟語出されてもね……焼肉定食とか量子力学とか青銭万選とか西尾維新みたいに、誰もが聞いたことのあるような四字熟語じゃないと初見では理解できんわさ。

でも分からない単語の説明もしようと思うとあれもこれも言わなきゃいけないってなって、文章がものすごく冗長になって読む気が失せたりもするから、それはそれで悩ましいんですけどね。うちのブログがまさに良い例ですね。もっとも僕の場合はいらんことをちょいちょい差し込んでるせいもありますが。

まあそんなことはどうでもいいとして、次回はメッセージの出し方やそれを自動で非表示にする処理を実装します。特定のメソッドを遅延的にアクティベートすることにより、プレイヤーの任意のオペレーションを受け付けずとも動的なプロセスを実行できる方法の紹介になります(ちょっと何言ってるか分からない)

それじゃあ次回もよろしくお願いしんきんぐたいむ。



本シリーズの記事の一覧はこちら
Unityで簡単な2D脱出ゲームを作ってウェブサイトで公開してみよう 〜エピローグ〜
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください