質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

2024閲覧

他のゲームオブジェクトにアタッチされたスクリプトにアクセスする方法

shukrin

総合スコア14

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/12/15 17:52

前提・実現したいこと

Unityを勉強中なのですが、よく分からないことがあって質問させていただきました。以下のサイトの"2d-shooting-game"のチュートリアルに沿ってゲームを製作しているのですが、第10回のソースコードによく分からない箇所があります。
2d-shooting-game 第10回 タイトルをつける

分からないこと

質問したいところは「10.3 他のスクリプトからマネージャーを呼び出す」のPlayer.cs の FindObjectOfType<Manager>().GameOver(); の箇所です。

lang

1using UnityEngine; 2using System.Collections; 3 4public class Player : MonoBehaviour 5{ 6 7~中略~ 8 9// ぶつかった瞬間に呼び出される 10 void OnTriggerEnter2D (Collider2D c) 11 { 12 // レイヤー名を取得 13 string layerName = LayerMask.LayerToName(c.gameObject.layer); 14 15 // レイヤー名がBullet (Enemy)の時は弾を削除 16 if( layerName == "Bullet (Enemy)") 17 { 18 // 弾の削除 19 Destroy(c.gameObject); 20 } 21 22 // レイヤー名がBullet (Enemy)またはEnemyの場合は爆発 23 if( layerName == "Bullet (Enemy)" || layerName == "Enemy") 24 { 25 // Managerコンポーネントをシーン内から探して取得し、GameOverメソッドを呼び出す 26 FindObjectOfType<Manager>().GameOver(); 27 28 // 爆発する 29 spaceship.Explosion(); 30 31 // プレイヤーを削除 32 Destroy (gameObject); 33 } 34 } 35} 36

Managerゲームオブジェクトには Manager.cs というスクリプトがアタッチされており、内容は以下のようなものとなります。

lang

1using UnityEngine; 2 3public class Manager : MonoBehaviour 4{ 5 // Playerプレハブ 6 public GameObject player; 7 8 // タイトル 9 private GameObject title; 10 11 void Start () 12 { 13 // Titleゲームオブジェクトを検索し取得する 14 title = GameObject.Find ("Title"); 15 } 16 17 ~中略~ 18 19 public void GameOver () 20 { 21 // ゲームオーバー時に、タイトルを表示する 22 title.SetActive (true); 23 } 24}

Playerというゲームオブジェクトにアタッチされたスクリプトから、別のManagerというゲームオブジェクトにアタッチされたスクリプト内のメソッドを実行しようとしているところです。Unityリファレンスを調べてみたところFindObjectOfType<>()の返り値はObject型とのことでしたが、ManagerはGameObject型なので今回はGameObject型のインスタンスが返ってきていると思います。

GameOverメソッドは今回勝手に作ったものですので、当然GameObjectクラスには定義されておりません。それにも関わらずFindObjectOfType<Manager>().GameOver();と書けてしまうのはなぜなのでしょうか。私としては以下のように書かなければならないように感じます。

lang

1FindObjectOfType<Manager>().GetComponent<Manager>().GameOver();

実際はどちらで書いた場合も見かけ上動作に違いはありませんでした。ネットで調べてみたところ、このような場合にはGetComponentを使用するのが一般的なようでした。

GetComponentなしで直接メソッドにアクセスできる理由を教えていただけないでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

FindObjectOfType<T>() の戻り値は 必ずGameObject型というわけではなく、T型です。
いわゆるジェネリクスというやつですね。

cs

1FindObjectOfType<Manager>();

ここでManagerというコンポーネントがついたものを探して最初に見つかったものの参照を取ります。見つからなければnullが返ります。今回、Tの部分でManager型を指定しているので検索するのものこれですし、戻り値として返ってくるのもManager型です

わかりやすく言えば、シーン上でアクティブになっているオブジェクトを全部検索してManagerがアタッチされているものがあるかどうかを調べていきます。それで最初に見つけたManagerをしょっ引いて連れてきてくれるのがこのコードがやっていることです。

試しに下記のコードでhogeは何型になっているかを調べるとManager型になっているかと思います。

cs

1var hoge = FindObjectOfType<Manager>();

Manager型の参照が取れているということは、改めてGetComponentし直す必要はなく参照を使ってMethodを叩くことができます。

…ただ、FindObjectOfTypeは全オブジェクトのコンポーネントをなめるように検索するので時間がかかります。あまりおすすめしません。参照は予めどこかで持っておくなどすると良いかと思います。
あと、今回はおそらくManagerがアタッチされたものが1つしかないので問題ないのだと思いますが、これが仮にManagerが複数のオブジェクトにアタッチされている場合などではどちらの参照をとっているのかがわかりませんので問題が起きる可能性があります。

参考:
【Unity】GameObject.Find 系関数の処理速度の検証結果
http://baba-s.hatenablog.com/entry/2014/07/09/093240

投稿2019/12/16 00:37

編集2019/12/16 00:42
hogefugapiyo

総合スコア3302

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shukrin

2019/12/17 11:06

ご回答いただき大変ありがとうございます。FindObjectOfTypeでは返ってくる型の名前を指定するのですね。同名のGameObjectを返す関数だと勘違いをしていました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問