Skip to content

Getting and calling components

SyntaxDescriptionNote
GetComponent<Renderer>()Getting by type argument
GetComponent(typeof(Renderer))Getting by type value
GetComponentInChildren<Renderer>()Getting from children
GetComponentInParent<Renderer>()Getting from parents
GetComponentInChildren<Renderer>(true)Getting including inactive objects
GetComponents<Renderer>()Getting all of the same type
GetComponentsInChildren<Renderer>()Getting all from children
target.GetComponent<Renderer>()Getting from another object
transform.Find("Lamp")Getting a child by name
GetComponent<ComponentsLamp>()Getting by a type you wrote
GetComponents<ComponentsSpeaker>()Getting all of a type you wrote
GetComponent<TsukimiBehaviour>()Getting by a base type
GetComponent<UdonBehaviour>()Getting by the runtime’s type
SendCustomEvent(nameof(Ping))Calling yourself
other.SendCustomEvent("Ping")Calling another program
SendCustomEventDelayedSeconds(nameof(Ping), 1.5f)Calling after a delay in seconds
SendCustomEventDelayedFrames(nameof(Ping), 10)Calling after a delay in frames
SendCustomEventDelayedSeconds(nameof(Ping), 1f, EventTiming.LateUpdate)Choosing when the call happens
[NetworkCallable] public void Hit(int damage)Registering a method with arguments
door.Open()Calling a method
counter.Add(5)Calling with arguments
board.Score()Calling with a return value
tank.levelReading a field
dial.position = 3Writing to a field
public ComponentsLight[] lights;Referencing with an array
public IComponentsSwitch target;Referencing by interface
public UdonBehaviour other;Referencing by the runtime’s type
(int)other.GetProgramVariable("count")Reading a value
other.SetProgramVariable("count", 5)Writing a value
GetProgramVariable(nameof(count))Reading and writing your own variable
other.GetProgramVariableType("count")Asking for the type
SyntaxDescriptionErrorReasonAlternative
SendCustomEvent("Ping")You have no method named PingTUKI0001by designFix the name. Writing it with nameof avoids spelling mistakes
SendCustomEvent(nameof(Ping))Ping is privateTUKI0001by designMake it public
SendCustomEvent(nameof(Hit))Hit takes an argumentTUKI0001by designAdd [NetworkCallable] to Hit
[NetworkCallable] private void Ping()The attribute is on a method that isn’t publicTUKI0001runtimeMake it public
[NetworkCallable] public static void Ping()The attribute is on a static methodTUKI0001runtimeRemove static
[NetworkCallable] public int Ping()The attribute is on a method with a return valueTUKI0001runtimeMake the return type void
[NetworkCallable] public void Ping(a, ..., i)The attribute is on a method with 9 argumentsTUKI0001runtimeKeep the arguments to 8 or fewer
GetComponents<UdonBehaviour>()Use UdonBehaviour as the type argument to get all of themTUKI0001not yetWrite GetComponents(typeof(UdonBehaviour)) instead
sink.Take(p)Passing a struct you wrote to the other behaviourTUKI0101undecidedPass it broken up into its parts
sink.Take(ref n)Passing it to the other behaviour with refTUKI0001not yetGet it back through a return value
Gate.Ping()Calling a static method on the other behaviour’s typeTUKI0001undecidedRemove static and call it as an instance method on the other behaviour
holder.__stateTouching a field on the other behaviour that starts with __TUKI0001by designRemove the __ from the front of the name
Calling yourselfIt is converted into a call by name before running
Arguments you can passThe value is written into a variable on the other side before it travels, so a type that can’t live in a variable can’t be passed (this is why you can’t pass a struct you declared yourself. See the table above)

GetComponent returns null if that type isn’t attached. transform.Find also returns null if there’s no child with that name. Using the returned null Using it as-is halts execution (Udon’s execution model).

DestinationChecked
A name sent to yourselfYes. Write SendCustomEvent(nameof(Ping)) and, if you have no Ping, it is a compile error
A name sent to another objectNo. The other program is in a different file, and the compiler doesn’t look at that file (sending a name the other side doesn’t have still compiles)
Names passed to GetProgramVariable and SetProgramVariableNo, for the same reason as above

To keep the examples on this page self-contained in a single file, the other behaviour is written as just an abstract shape. When you actually write this, put the other behaviour in a separate file as a concrete behaviour and inherit from it, or write that type directly as the field’s type.

  • Calling by name makes the other behaviour run that method
  • Sending a name the other behaviour doesn’t have does nothing. It isn’t an error
  • Reading and writing by name reaches even the other side’s private fields. The visibility rules of C# rule, and it doesn’t carry over to the runtime side

Components attached to the same GameObject are obtained with GetComponent. If none is found, null is returned, so check before using it.

Write the type you want as a type argument.

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

Passing a type value instead of a type argument also works.

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentTypeof : TsukimiBehaviour
{
void Start()
{
Renderer r = (Renderer)GetComponent(typeof(Renderer));
Debug.Log(r == null); // => the runtime's value
}
}

Searches yourself and the objects hanging below you.

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

Searches yourself and the objects above you.

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

Passing true searches disabled objects as well.

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

When multiple components of the same type are attached, get all of them as an array.

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

Get all of them as an array, including the ones on objects hanging below.

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

If you hold a GameObject, you can get the components attached to that object.

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

Finds a child object by name.

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

A Behaviour type you wrote yourself can be retrieved too.

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();
}
}
}

You can also get all of a behaviour type you wrote as an array.

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);
}
}

A base type works too.

using UnityEngine;
using Tsukimi;
public class ComponentsGetComponentBehaviour : TsukimiBehaviour
{
void Start()
{
TsukimiBehaviour b = GetComponent<TsukimiBehaviour>();
Debug.Log(b == null); // => the runtime's value
}
}

You can also get by the UdonBehaviour type that the runtime has.

using UnityEngine;
using Tsukimi;
using VRC.Udon;
public class ComponentsGetComponentUdonBehaviour : TsukimiBehaviour
{
void Start()
{
UdonBehaviour b = GetComponent<UdonBehaviour>();
Debug.Log(b == null); // => the runtime's value
}
}
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);
}
}

Passing a method’s name as a string calls that method. Only public methods can be called by name. The name can be written with nameof or as a string literal.

Calls one of your own methods by name.

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");
}
}

If you hold the other behaviour as TsukimiBehaviour, you can call it by name without knowing its type.

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);
}
}

The third argument chooses where in the frame it is called.

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);
}
}

A method with parameters can’t be called under its plain name.

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));
}
}

Holding the other behaviour’s type lets you call its methods directly. The compiler checks arguments and return values, so mistakes surface earlier than with calling by name. To keep the examples below self-contained in a single file, the other behaviour is written as just an abstract shape (What can be written in a single file).

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

Arguments can be passed.

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);
}
}

Several objects of the same type can be held in an array.

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();
}
}
}

You can also hold a behaviour by an interface type attached to it (interfaces).

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

Holding it as UdonBehaviour lets you call by name and read and write variables by name.

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"));
}
}

You can also read and write the other behaviour’s variables by name. This works even without holding its type.

The return value is object, so cast it to the type you need.

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);
}
}

The second argument takes object, so you can pass a value of any type.

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

It works on yourself too.

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

It returns the type of the variable with that name as a System.Type.

using UnityEngine;
using Tsukimi;
public class ComponentsGetProgramVariableType : TsukimiBehaviour
{
public TsukimiBehaviour other;
void Start()
{
System.Type t = other.GetProgramVariableType("count");
Debug.Log(t != null);
}
}