コンテンツにスキップ

コンポーネントどうしの連携

書き方説明
GetComponent<Renderer>()型引数での取得
GetComponent(typeof(Renderer))型の値での取得
GetComponentInChildren<Renderer>()子からの取得
GetComponentInParent<Renderer>()親からの取得
GetComponentInChildren<Renderer>(true)無効なオブジェクトを含めた取得
GetComponents<Renderer>()同じ型すべての取得
GetComponentsInChildren<Renderer>()子からのすべての取得
target.GetComponent<Renderer>()別のオブジェクトからの取得
transform.Find("Lamp")名前での子の取得
GetComponent<ComponentsLamp>()自分で書いた型での取得
GetComponents<ComponentsSpeaker>()自分で書いた型すべての取得
GetComponent<TsukimiBehaviour>()基底の型での取得
GetComponent<UdonBehaviour>()実行環境の型での取得
SendCustomEvent(nameof(Ping))自分への呼び出し
other.SendCustomEvent("Ping")別のプログラムへの呼び出し
SendCustomEventDelayedSeconds(nameof(Ping), 1.5f)秒を置いた呼び出し
SendCustomEventDelayedFrames(nameof(Ping), 10)フレームを置いた呼び出し
SendCustomEventDelayedSeconds(nameof(Ping), 1f, EventTiming.LateUpdate)呼ばれる時機の指定
[NetworkCallable] public void Hit(int damage)引数のあるメソッドの登録
door.Open()メソッドの呼び出し
counter.Add(5)引数を渡す呼び出し
board.Score()戻り値のある呼び出し
tank.levelフィールドの読み出し
dial.position = 3フィールドへの書き込み
public ComponentsLight[] lights;配列での参照
public IComponentsSwitch target;インタフェースでの参照
public UdonBehaviour other;実行環境の型での参照
(int)other.GetProgramVariable("count")値の読み出し
other.SetProgramVariable("count", 5)値の書き込み
GetProgramVariable(nameof(count))自分の変数の読み書き
書き方説明エラー理由代わりに
SendCustomEvent("Ping")自分に Ping というメソッドが無いTUKI0001設計名前を直す。nameof で書くと綴りの誤りが出ない
SendCustomEvent(nameof(Ping))Pingprivate になっているTUKI0001設計public にする
SendCustomEvent(nameof(Hit))Hit が引数を取るTUKI0001設計Hit[NetworkCallable] を付ける
[NetworkCallable] private void Ping()属性を public でないメソッドに付けるTUKI0001環境public にする
[NetworkCallable] public static void Ping()属性を static メソッドに付けるTUKI0001環境static を外す
[NetworkCallable] public int Ping()属性を戻り値のあるメソッドに付けるTUKI0001環境戻り値を void にする
[NetworkCallable] public void Ping(a, ..., i)属性を引数 9 個のメソッドに付けるTUKI0001環境引数を 8 個までにする
GetComponents<UdonBehaviour>()UdonBehaviour を型引数にして全部取るTUKI0001未実装GetComponents(typeof(UdonBehaviour)) と書く
sink.Take(p)自分で作った struct を相手へ渡すTUKI0101未定成分ごとに分けて渡す
sink.Take(ref n)ref を付けて相手へ渡すTUKI0001未実装戻り値で受け取る
Gate.Ping()相手の型の static メソッドを呼ぶTUKI0001未定static を外して、相手の実体のメソッドとして呼ぶ
holder.__state__ で始まるフィールドを相手から触るTUKI0001設計名前の先頭の __ を外す
名前で呼ぶ型を持って呼ぶ
相手の持ち方TsukimiBehaviourUdonBehaviour相手の Behaviour の型
書き方other.SendCustomEvent("Ping")door.Open()
呼べるメソッドpublic なもの(引数があるときは [NetworkCallable] が要る)publicstatic でないもの
引数SendCustomEvent では渡せない(ネットワーク越しに送る形は同期渡せる(単独の値と、その配列)
戻り値受け取れない受け取れる
名前の誤り自分に送るときだけコンパイラが見るコンパイラが見る
相手のファイル要らない要る

型を持って呼ぶ形は、名前で呼ぶ形に変換されてから走ります。引数は相手の変数へいったん書き込まれて 渡るので、変数に置けない型は渡せません。自分で作った struct を渡せないのはこのためです(上の表)。

GetComponent は、その型が付いていなければ null を返します。transform.Find も、 その名前の子が無ければ null を返します。返ってきた null を そのまま使うと実行が止まります(Udon の実行モデル)。

自分に送る名前は、コンパイラが検査します。SendCustomEvent(nameof(Ping)) と書いて Ping が 自分に無ければ、コンパイルが止まります。

相手に送る名前は検査しません。相手のプログラムは別のファイルにあり、コンパイラはそのファイルを 見ていないためです。相手に無い名前を送っても、コンパイルは通ります。GetProgramVariableSetProgramVariable に渡す名前も、同じ理由で検査しません。

このページの例は 1 つのファイルで完結させるため、相手の形だけを abstract で置いています。 実際に書くときは、相手を別のファイルに具象の Behaviour として置いて、そこから継承させるか、 その型をそのままフィールドの型に書きます。

名前で呼んだときに起きること

Section titled “名前で呼んだときに起きること”
  • 名前で呼ぶと、相手はそのメソッドを実行します
  • 相手に無い名前を送っても、何も起きません。エラーにはなりません
  • 名前で読み書きすると、相手の private なフィールドにも届きます。見える範囲は C# の 規則で、実行環境の側には残らないためです

同じ GameObject に付いているコンポーネントは GetComponent で取ります。 見つからなければ null が返るので、使う前に調べます。

取りたい型を型引数に書きます。同じ GameObject に付いているコンポーネントを 1 つ返します。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponent : TsukimiBehaviour
{
void Start()
{
Renderer r = GetComponent<Renderer>();
Debug.Log(r == null);
}
}

型引数の代わりに型の値を渡す形も書けます。戻り値は Component なので、使う型へキャストします。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentTypeof : TsukimiBehaviour
{
void Start()
{
Renderer r = (Renderer)GetComponent(typeof(Renderer));
Debug.Log(r == null);
}
}

自分と、自分の下にぶら下がっているオブジェクトを探します。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentInChildren : TsukimiBehaviour
{
void Start()
{
Renderer r = GetComponentInChildren<Renderer>();
Debug.Log(r == null);
}
}

自分と、自分の上にあるオブジェクトを探します。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentInParent : TsukimiBehaviour
{
void Start()
{
Renderer r = GetComponentInParent<Renderer>();
Debug.Log(r == null);
}
}

無効なオブジェクトを含めた取得

Section titled “無効なオブジェクトを含めた取得”

true を渡すと、無効になっているオブジェクトも探します。渡さなければ有効なものだけを探します。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentInactive : TsukimiBehaviour
{
void Start()
{
Renderer r = GetComponentInChildren<Renderer>(true);
Debug.Log(r == null);
}
}

同じ型のコンポーネントが複数付いているとき、全部を配列で受け取ります。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponents : TsukimiBehaviour
{
void Start()
{
Renderer[] all = GetComponents<Renderer>();
Debug.Log(all.Length);
}
}

下にぶら下がっているオブジェクトの分も含めて、全部を配列で受け取ります。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentsInChildren : TsukimiBehaviour
{
void Start()
{
Renderer[] all = GetComponentsInChildren<Renderer>();
Debug.Log(all.Length);
}
}

GameObject を持っていれば、そのオブジェクトに付いているコンポーネントを取れます。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentOfObject : TsukimiBehaviour
{
public GameObject target;
void Start()
{
Renderer r = target.GetComponent<Renderer>();
Debug.Log(r == null);
}
}

子のオブジェクトを名前で探します。見つからなければ null が返ります。

using UnityEngine;
using Tsukimi;
public class ComponentsFindChild : TsukimiBehaviour
{
void Start()
{
Transform child = transform.Find("Lamp");
Debug.Log(child == null);
}
}

自分で書いた Behaviour の型でも取れます。返ってきた値は、そのままメソッドを呼べます。

using UnityEngine;
using Tsukimi;
public abstract class ComponentsLamp : TsukimiBehaviour
{
public abstract void TurnOn();
}
public class ComponentsGetComponentOwnType : TsukimiBehaviour
{
void Start()
{
ComponentsLamp lamp = GetComponent<ComponentsLamp>();
if (lamp != null)
{
lamp.TurnOn();
}
}
}

自分で書いた Behaviour の型でも、全部を配列で受け取れます。

using UnityEngine;
using Tsukimi;
public abstract class ComponentsSpeaker : TsukimiBehaviour
{
public abstract void Play();
}
public class ComponentsGetComponentsOwnType : TsukimiBehaviour
{
void Start()
{
ComponentsSpeaker[] all = GetComponents<ComponentsSpeaker>();
Debug.Log(all.Length);
}
}

基底の型でも取れます。相手の型を知らずに、名前で呼ぶ相手だけを持ちたいときの形です。

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentBehaviour : TsukimiBehaviour
{
void Start()
{
TsukimiBehaviour b = GetComponent<TsukimiBehaviour>();
Debug.Log(b == null); // => 実行環境の値
}
}

実行環境が持っている UdonBehaviour の型でも取れます。

using UnityEngine;
using Tsukimi;
using VRC.Udon;
public class ComponentsGetComponentUdonBehaviour : TsukimiBehaviour
{
void Start()
{
UdonBehaviour b = GetComponent<UdonBehaviour>();
Debug.Log(b == null);
}
}
using UnityEngine;
using Tsukimi;
using VRC.Udon;
public class R_components_get_components_udon : TsukimiBehaviour
{
void Start()
{
UdonBehaviour[] all = GetComponents<UdonBehaviour>();
Debug.Log(all.Length);
}
}

メソッドの名前を文字列で渡すと、そのメソッドが呼ばれます。名前で呼べるのは public な メソッドです。名前は nameof でも文字列リテラルでも書けます。

自分のメソッドを名前で呼びます。自分に無い名前を書くと、コンパイルが止まります。

using UnityEngine;
using Tsukimi;
public class ComponentsSendCustomEvent : TsukimiBehaviour
{
public void Ping()
{
Debug.Log(1);
}
void Start()
{
SendCustomEvent(nameof(Ping));
}
}
using UnityEngine;
using Tsukimi;
public class R_components_unknown_name : TsukimiBehaviour
{
void Start()
{
SendCustomEvent("Ping");
}
}

TsukimiBehaviour の型で相手を持てば、相手の型を知らなくても名前で呼べます。 この形の名前はコンパイラが検査しません(名前の検査の範囲)。

using UnityEngine;
using Tsukimi;
public class ComponentsSendCustomEventOther : TsukimiBehaviour
{
public TsukimiBehaviour other;
void Start()
{
other.SendCustomEvent("Ping");
}
}
using UnityEngine;
using Tsukimi;
public class ComponentsSendDelayedSeconds : TsukimiBehaviour
{
public void Ping()
{
Debug.Log(1);
}
void Start()
{
SendCustomEventDelayedSeconds(nameof(Ping), 1.5f);
}
}
using UnityEngine;
using Tsukimi;
public class ComponentsSendDelayedFrames : TsukimiBehaviour
{
public void Ping()
{
Debug.Log(1);
}
void Start()
{
SendCustomEventDelayedFrames(nameof(Ping), 10);
}
}

第 3 引数で、フレームのどこで呼ばれるかを選べます。書かなければ Update になります。

using UnityEngine;
using Tsukimi;
using VRC.Udon.Common.Enums;
public class ComponentsSendDelayedTiming : TsukimiBehaviour
{
public void Ping()
{
Debug.Log(1);
}
void Start()
{
SendCustomEventDelayedSeconds(nameof(Ping), 1f, EventTiming.LateUpdate);
}
}

引数のあるメソッドは、そのままの名前では呼べません。[NetworkCallable] を付けると、 そのままの名前で呼べるようになります。SendCustomEvent に引数を渡す口はありません。

using UnityEngine;
using Tsukimi;
using VRC.SDK3.UdonNetworkCalling;
public class ComponentsSendCustomEventWithArguments : TsukimiBehaviour
{
[NetworkCallable]
public void Hit(int damage)
{
Debug.Log(damage);
}
void Start()
{
SendCustomEvent(nameof(Hit));
}
}
using UnityEngine;
using Tsukimi;
public class R_components_send_with_args : TsukimiBehaviour
{
public void Hit(int damage) { Debug.Log(damage); }
void Start()
{
SendCustomEvent(nameof(Hit));
}
}

相手の型を持つと、メソッドをそのまま呼べます。引数と戻り値をコンパイラが検査するので、 名前で呼ぶ形より誤りが早く出ます。以下の例は 1 つのファイルで完結させるため、相手の形だけを abstract で置いています(1 つのファイルに書けるもの)。

using UnityEngine;
using Tsukimi;
public abstract class ComponentsDoor : TsukimiBehaviour
{
public abstract void Open();
}
public class ComponentsCallAcross : TsukimiBehaviour
{
public ComponentsDoor door;
void Start()
{
door.Open();
}
}

引数を渡せます。渡せるのは単独の値と、その配列です。自分で作った struct は渡せません。

using UnityEngine;
using Tsukimi;
public abstract class ComponentsCounter : TsukimiBehaviour
{
public abstract void Add(int n);
}
public class ComponentsCallAcrossArgument : TsukimiBehaviour
{
public ComponentsCounter counter;
void Start()
{
counter.Add(5);
}
}
using UnityEngine;
using Tsukimi;
public struct R_components_pair
{
public int a;
public int b;
}
public abstract class R_components_sink : TsukimiBehaviour
{
public abstract void Take(R_components_pair p);
}
public class R_components_cross_struct : TsukimiBehaviour
{
public R_components_sink sink;
void Start()
{
R_components_pair p;
p.a = 1;
p.b = 2;
sink.Take(p);
}
}
using UnityEngine;
using Tsukimi;
public abstract class ComponentsScoreBoard : TsukimiBehaviour
{
public abstract int Score();
}
public class ComponentsCallAcrossReturn : TsukimiBehaviour
{
public ComponentsScoreBoard board;
void Start()
{
Debug.Log(board.Score());
}
}
using UnityEngine;
using Tsukimi;
public abstract class ComponentsTank : TsukimiBehaviour
{
public int level;
}
public class ComponentsFieldAcrossRead : TsukimiBehaviour
{
public ComponentsTank tank;
void Start()
{
Debug.Log(tank.level);
}
}
using UnityEngine;
using Tsukimi;
public abstract class ComponentsDial : TsukimiBehaviour
{
public int position;
}
public class ComponentsFieldAcrossWrite : TsukimiBehaviour
{
public ComponentsDial dial;
void Start()
{
dial.position = 3;
Debug.Log(dial.position);
}
}

同じ型の相手を配列で持てます。編集画面から差し込んで、1 つのループで回せます。

using UnityEngine;
using Tsukimi;
public abstract class ComponentsLight : TsukimiBehaviour
{
public abstract void TurnOn();
}
public class ComponentsReferenceArray : TsukimiBehaviour
{
public ComponentsLight[] lights;
void Start()
{
for (int i = 0; i < lights.Length; i++)
{
lights[i].TurnOn();
}
}
}

Behaviour に付けたインタフェースの型でも持てます(インタフェース)。

using UnityEngine;
using Tsukimi;
public interface IComponentsSwitch
{
void Toggle();
}
public class ComponentsReferenceInterface : TsukimiBehaviour
{
public IComponentsSwitch target;
void Start()
{
if (target != null)
{
target.Toggle();
}
}
}

UdonBehaviour の型で持つと、名前での呼び出しと、変数の名前での読み書きが使えます。 メソッドを直接呼ぶことはできません。

using UnityEngine;
using Tsukimi;
using VRC.Udon;
public class ComponentsReferenceUdonBehaviour : TsukimiBehaviour
{
public UdonBehaviour other;
void Start()
{
other.SendCustomEvent("Ping");
other.SetProgramVariable("count", 3);
Debug.Log((int)other.GetProgramVariable("count"));
}
}

相手の変数は名前でも読み書きできます。相手の型を持っていなくても使えます。

戻り値は object なので、使う型へキャストします。キャストした型が中身と違うと、 書いたとおりの値になりません。

using UnityEngine;
using Tsukimi;
public class ComponentsGetProgramVariable : TsukimiBehaviour
{
public TsukimiBehaviour other;
void Start()
{
int count = (int)other.GetProgramVariable("count");
Debug.Log(count);
}
}
using UnityEngine;
using Tsukimi;
public class R_components_progvar_no_cast : TsukimiBehaviour
{
public TsukimiBehaviour other;
void Start()
{
int count = other.GetProgramVariable("count");
Debug.Log(count);
}
}

第 2 引数は object を取るので、どの型の値でも渡せます。

using UnityEngine;
using Tsukimi;
public class ComponentsSetProgramVariable : TsukimiBehaviour
{
public TsukimiBehaviour other;
void Start()
{
other.SetProgramVariable("count", 5);
}
}

自分に対しても使えます。名前を nameof で書けば、フィールドの名前を変えたときに一緒に変わります。

using UnityEngine;
using Tsukimi;
public class ComponentsProgramVariableSelf : TsukimiBehaviour
{
private int count = 3;
void Start()
{
SetProgramVariable(nameof(count), 9);
Debug.Log((int)GetProgramVariable(nameof(count)));
}
}