C#のGetComponent
このチュートリアルの
- オブジェクト内から別のスクリプトにアクセスする
- GetComponentはどのように機能しますか?
- 多くのコンポーネントを取得する
- オブジェクト間の相互作用。
- 階層内のオブジェクト間の相互作用
- SendMessageとBroadcastMessage
- インタラクティブ関数
はじめに
Unityを開始するときに繰り返される問題の一つは、別のスク 多くの人は、スクリプト名からの逆参照で十分だと思うでしょうが、彼らはすぐにそれがそうではないことに気付きます。
プログラムを開発するとき、変数は異なる場所のメモリに格納されます。 オブジェクトが他のオブジェクトメンバーを見ることができれば、意図されていなくてもそれらを変更するリスクがあります。 インスタンスの場合、2つのオブジェクトが同じ変数名を持つ同じスクリプトを保持している場合、コンパイラはどちらを参照しているのかを把握
この問題を回避するために、メモリ内の各オブジェクトは他のオブジェクトを見ることができません。 次に、必要な変数がメモリ内のどこにあるかを1つのオブジェクトに伝える必要があります。 ポインタの原理全体はずっと前に削除されましたが、今では代わりに参照を使用しています(実際にはポインタを内部的に使用しています)。変数にアクセスするにはさまざまな方法があり、いくつかは他のものよりも優れており、いくつかは単に特定の状況で使用されます。
変数にアクセ
- GetComponent、最も一般的な、また、最初に最も混乱するもの
- SendMessage、それは把握する方が簡単に見えるかもしれませんが、それはまた、あまり効率的ではありません。
- 静的変数、最も簡単なだけでなく、最初は完全に理解するのが最も複雑です。
シーンの設定
新しいシーンを作成し、空のゲームオブジェクトを追加し、”StormTrooper”という名前を付けます。 “Health”と”Force”という名前の二つのスクリプトを作成します。 両方のスクリプトをStormTrooperに追加します。
エディタで両方のスクリプトを開き、次の行を追加します。
Health。cs
using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private int health = 5; private bool hasForce = false; private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { this.hasForce = Force.ReportForce(); if(hasForce == true) { this.health = 10; } } }}
強制します。cs
using UnityEngine;using System.Collections;public class Force : MonoBehaviour{ private bool force = false; private void Start() { this.force = true; } private void Update() { if (Input.GetKeyDown(KeyCode.P)) { string str = (this.force == true) ? " the": " no"); Debug.Log("I have" + str + " force"); } } public bool ReportForce() { return force; }}
このコードを試してみると、”非静的メンバーにアクセスするにはオブジェクト参照が必要です”というエラーが返されます。
StormTrooperのHealthコンポーネントにForceコンポーネントがどこにあるかを伝えて、メンバーにアクセスできるようにする必要があります。 私たちのスクリプトは両方とも同じオブジェクト上にあるので、アクセスしてGetComponentを使用してスクリプトを見つけるスクリプトの型の変数を宣言す
オブジェクト内の別のスクリプトにアクセスする
ヘルスを次のように変更します。
using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private Force forceScript= null; public int health = 5; private bool hasForce = false; private void Start() { this.forceScript= GetComponent<Force>(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { this.hasForce = this.forceScript.ReportForce(); if(this.hasForce == true) { this.health = 10; } } }}
変更して、ゲームを実行し、Spaceを押してからPを押すと、StormTrooperは10のヘルスを持つようになりました(StormTrooperはフォースを持っていないことに注意してください。これは例のためのものです)。 変数には、メソッドにアクセスしたのと同じ方法でアクセスします。 あなたが到達しようとしているメンバーが公開宣言されているか、エラーが発生することを確認してください。
メンバーがプライベートであれば、それらにアクセスすることはできません。 クラス/スクリプトの外部から到達すべきではないものは、カプセル化の原則に対処するためにプライベートでなければなりません。
GetComponentはどのように動作しますか?
まず、到達したいスクリプトの型のオブジェクトを宣言します。P>
private Force forceScript = null;
Forceは私たちのタイプであり、forceScriptはスクリプト内の参照を使用するために提供する単なる名前です。 任意の名前を付けることができます。
GetComponent関数は、オブジェクト(StormTrooperゲームオブジェクト)の内部を調べて、渡された型に対応するコンポーネントを見つけます。 何も見つからない場合はnull参照が返され、forceScript変数は何もしません。 Force型のコンポーネントが見つかった場合、そのコンポーネントのアドレスが変数forceScriptに渡され、その変数はメモリ内のどこかに格納されているForceコンポーネ Forceのパブリックメンバーにアクセスするには、ドット演算子を使用してスクリプト変数を逆参照するだけです。 ForceScript変数はオブジェクトへのハンドルであると言われています。 これは、すべての公開メンバー(および内部メンバー)へのアクセスを提供します。
script.PublicMembers;
プライベートメンバーと保護されたメンバーはアクセスできません。
SerializeField属性を使用すると、inspector desputeの変数がprivateであることを確認できます。 あなたのforceScript変数はNone(Force)の値で始まり、Force参照に変わります。 それをクリックすると、ターゲットオブジェクトが強調表示されます。P>
だから私はGetComponentでコンポーネントをキャッシュすることができますか?
はい、いくつかの定期的に使用されるコンポーネントのためにそうすることをお勧めします。
Rigidbody、Transform、Rendererなどの組み込みコンポーネントにアクセスするとき、Unityは次のような”直接”アクセスを許可していました。
private void Update(){ this.transform.position += this.transform.forward; this.rigidbody.AddForce(force);}
与えられたゲームオブ
GetComponentはUpdate内では避けるべきであるため、キャッシュは次のようないくつかの簡単なコード行で推奨されます:p>
private Transform myTransform = null;private Rigidbody rigidbody = null;private void Start(){ this.myTransform = this.gameObject.GetComponent<Transform>(); this.rigidbody = this.gameObject.GetComponent<Rigidbody>();}private void Update(){ this.myTransform.position += this.myTransform.forward; this.rigidbody.AddForce(force);}
これは、いくつかの計算を保存します。
多くのコンポーネントを取得する
私たちのStormTrooperが自分自身の中に多くの力を持っている場合はどうなりますか(そうではありません)。 ストームトルーパーが三つ持っているように、力を二度複製します。
健康になります:
using UnityEngine;using System.Collections;public class Health : MonoBehaviour{ private Force scripts = null; private int health = 5; private void Start() { this.scripts = this.gameObject.GetComponents<Force>(); } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { for (int i = 0; i < this.scripts.Length; i++) { if(this.scripts.ReportForce()) { this.health += 5; } } } }}
ゲームを実行し、スペースを押して、私たちのストームトルーパーはジェダイになってきています。 GetComponentsは、型のすべてのコンポーネントを検索し、それらの配列を返します。 これは、型の特定のコンポーネントをターゲットにしていないが、それらのすべてをターゲットにしている可能性が高い場合に問題ありません。
ライトをオンとオフにするだろう次を参照してください。
public class LightFlicker:MonoBehaviour{ private Light lights = null; private float timer = 0.0f private float clockTimer = 0.0f; private void Start(){ this.lights = this.gameObject.GetComponents<Light>(); this.clockTimer = Random.Range(0.5f, 2.0f); } private void Update(){ this.timer += Time.deltaTime; if(this.timer > this.clockTimer){ this.timer = 0.0f; this.clocktimer = Random.Range(0.5f, 2.0f); for(int i = 0; i < this.lights.Length; i++){ lights.enabled = !lights.enabled; } } }}
オブジェクト間の相互作用。また、同様の方法で相互作用する2つの異なるオブジェクトを取得することもできます。
“Sith”という名前の新しいオブジェクトを作成します。 StormTrooperから複製されたコンポーネントを削除して、一つだけが残っているようにします。P>
using UnityEngine;using System.Collections;public class DarkSide : MonoBehaviour { private const bool force = true; private Force script = null; private void Start () { this.script = GameObject.Find ("StormTrooper").GetComponent<Force>(); } private void Update () { if(Input.GetKeyDown (KeyCode.Space)) { UseForceToControl (); } } private void UseForceToControl() { if(this.script.ReportForce() == false) { Debug.Log ("Do as I say."); } }}
StormTrooperのforceスクリプトを変更して、彼が力を持たないようにします(Sithはこれを不審に思うでしょう…)、私はあなたにそれがStartスクリプ
以下は、StormTrooperを見つけてその力成分を取得する行です。
private void Start () { this.script = GameObject.Find ("StormTrooper").GetComponent<Force>();}
GameObject。Find()は、まずパラメータとして渡されたオブジェクトを検索し、その中の必要なコンポーネントを検索します。 ラグがゲームプレイに影響を与えないゲームの開始時またはゲームの時点でこれらのアクションを実行することをお勧めします。 GameObject。FindとGetComponentは、階層全体を反復処理する必要があるため、高価な関数です。 シーンに10個のオブジェクトしか含まれていない場合は問題ありませんが、何千ものオブジェクト(クローンの軍隊)を保持している場合は、確かに悪い効 これらをStart関数で呼び出すことを検討してください。 あなたのシーンに多くのStormTroopersが含まれている場合、Uityはあなたが使用することを意味するものを知ることができず、最初に見つかったものを返すことに注 ほとんどの場合、衝突コールバックなど、対話するオブジェクトへの参照を保持します。
階層内のオブジェクト間の相互作用
車両を検討し、それを駆動するとき、文字は子オブジェクトとしてそれに添付されています。 ドライバーが力(より良いハンドリング、より速くなど)を持っている場合、車両は特別な機能を持っています。 ドライバーが接続されているので、彼は階層の下にあります。
車両は、我々が以前に使用したのと同じ原理でドライバの力成分を見つけることができます(GameObject。しかし、ドライバーがそれ自身の階層にあることを車両に示すことによって、それをより速く実現することができます。
private Force script = null;private void Start () { this.script = this.transform.Find ("Sith").GetComponent<DarkSideForce>();}
GameObjectは変換のためにスワップされます。 実際、子オブジェクトは親の変換に属しているため、オブジェクトを見つけるために変換を調べる必要があります。 この例では、オブジェクトは呼び出し元のオブジェクトのすぐ下にあり、Sithがcarオブジェクトの下のcockpitオブジェクトの内側にある場合は、パスを提:p>
private void Start () { this.script = this.transform.Find ("Cockpit/Sith").GetComponent<DarkSideForce>();}
呼び出し元のオブジェクトは車であり、呼び出しでthisとして参照されます。 その後、コックピットが検索され、シスが検索されます。 パスが間違っている場合、Findはnullを返し、Null参照例外が発生します。 あなたはこのようにそれを避けることができます:
private void Start () { Transform tr = this.transform.Find ("Cockpit/Sith"); if(tr != null){ this.script = tr.GetComponent<DarkSideForce>(); }else{Debug.LogWarning("No component found");}}
transform。Findは、Gameobjectの間に変換を返します。FindはGameObjectを返します。さらに良いことに、私たちは使用することができます:
private void Start () { this.script = GetComponentInChildren<DarkSideForce>();}
今、私たちの車は私たちのシスが力を持っていることを知り、特別な機能を適用
知っておくべき問題の1つは、親オブジェクトに探しているタイプのスクリプトが含まれている場合、それを返します。 したがって、子コンポーネントを扱っていると考えると、実際には親コンポーネントを扱っています。 この例では、StormTrooperかSithかを各種類のドライバーで確認する必要があります。 より良いアプローチは、継承を使用して、最上位の基本クラスがforce変数を保持し、1つの関数がすべてのタイプのドライバに使用されるようにするこ しかし、継承と効率はここでの主な問題ではありませんでした。
SendMessageとBroadcastMessage
SendMessageとBroadcastMeassageは、より単純であるが、より高価である可能性がある他の二つの可能性があります。 原則は同じですが、オブジェクトへの参照があり、それに含まれるスクリプトを変更したいと考えています。
SendMessageを使用すると、スクリプトへの参照を見つけることなく、オブジェクトの関数を呼び出すことができます。 今、あなたは私に教えてください:”あなたはそれを簡単にすることができれば、このすべての混乱の乗り物は何ですか?”
SendMessageはリフレクションを使用し、GetComponentと比較して非常に遅く、ゲームに影響を与えない場合や他の解決策がない場合にのみ考慮する必要があ
そして、それは論理的に聞こえるはずです。GetComponentでは、スクリプトがメモリ内のどこにあるかを正確に示します。 SendMessageでは、コンパイラはオブジェクトを知っていますが、幸運な勝者が見つかるまですべてのスクリプトと関数を通過します。 これは、複数のオブジェクトで頻繁に使用する場合や、オブジェクトに多くのコンポーネントとスクリプトがある場合にゲームに影響を与える可能性があります(これはコンポーネントです)。
SendMessageは呼び出しが行われたオブジェクトのみを検索し、BroadcastMessageはターゲットオブジェクトだけでなく、そのすべての子に対しても検索を開始します。 オブジェクトに大きな階層が含まれている場合、研究はさらに長くなります。 あなたが推測したはずのUnity4SendMessageUpwardsで登場した新人はBroadcastMessageと同じですが、ルートまでオブジェクトのすべての親を上向きにします。
さて、私たちのStormTrooperは注文を受け取るための機能を持っていると考えてみましょう
//Order.csusing UnityEngine;using System.Collections;public class Order : MonoBehaviour{ private void ReceiveOrder(string order) { switch(order) { case "Fetch": //Action Debug.Log("Can you repeat Sir?"); break; // other cases } }}
今、私たちのStormTrooperは帝国からの司令官を満たしていると考えてみましょう:
// Commander.csprivate void Update(){ if(Input.GetKeyDown(KeyCode.Space)) { GameObject.Find("StormTrooper").SendMessage("ReceiveOrder","Fetch"); }}
あなたが見ることができるように、原則は同じままです、私はターゲットオブジェクトへの参照が必要ですし、私はそれにメッセージを送信します。 GetComponentと同じように、オブジェクト参照をキャッシュした方が良いです。
メッセージが受信者を見つけるかどうかを確認するためのパラメータを追加することもできます。
値を返す関数を使用することはできないことに注意してください。 その後、戻ってGetComponentを使用する必要があります。
インタラクティブ関数
Unityは、複数のオブジェクトを相互作用させる多くの機能を使用します。 これらの関数の利点は、他のオブジェクトに関する多くの情報を格納することです。
- 衝突関数OnTriggerXXXX/OnCollisionXXXX
- 物理関数XXXXcast
衝突宣言があります。
private void OnCollisionEnter(Collision colReference){}private void OnTriggerEnter(Collider colReference){}
関数の呼び出しは、CollisionまたはCollider型の変数を作成します。 この関数は、このインスタンスに衝突に関する情報を入力し、対話している他のオブジェクトへの参照を保持します。colReferenceはCollision型であり、そのオブジェクトへの参照をメンバーとして含むため、他のオブジェクトを探す必要はないことに注意してください。 今、私はいつものように私のGetComponentを実行し、私の公衆衛生変数にアクセスすることができます。
Physicsクラスの関数も同様の原理を持っています。 データはCollision/ColliderインスタンスではなくRaycastHitインスタンスに格納されます:私はRaycastHit型の変数を宣言し、それを関数に渡します。 RaycastHitは構造体であり、値型であるため、関数が構造体を変更できるように、その構造体への参照を作成するにはoutキーワードが必要です。 そのプロセスについて疑問がある場合は、メモリ管理の記事と値/参照型のセクションをチェックしてください。
私がここでやっていることは、カーソルを上に置いている表面がどのように平らであるかを確認することです。 これは、崖の上ではなく、建物を配置したい戦略的なゲームに便利です。
私はvector3で地形法線の内積を使用します。アップし、結果が1に十分に近い場合、それらが整列されていることを意味し、地面は建設のために十分に平らです。
明らかに、これは一点だけであり、通常の周りをチェックする必要があるので、いくつかのより多くの計算が必要ですが、あなたはアイデアを得ます。 レイキャストを使用して、マウスの位置と地形の間の相互作用を作成しました。
レイキャストには、開始点と方向が含まれています。 私たちの場合、開始点は、マウスの位置をワールドの位置に変換し、次にカメラの回転と投影行列に基づく方向に変換することです。 この行は定義された方向に無限に移動し(最大距離を設定することもできます)、コライダーに当たった場合、そのコライダーと衝突の情報はRaycastHit構造体に保 この構造体には、ヒットオブジェクトへの参照が含まれています。