Skip to content

Part with a T# script

To make a part do something while the world runs, give it a T# script (a .cs file with the same name next to the .mochi.xml, written in TsukimiSharp). Each place the part is placed gets one UdonBehaviour that runs the script.

C# file<Name>.cs in the same folder as the part’s <Name>.mochi.xml. This file alone makes the part a part with a T# script.
ClassA class named <Name>, written at the top level of the file or directly under a namespace, that inherits TsukimiBehaviour. Write the base class directly in the declaration in <Name>.cs (inheriting through a class of your own is MOCHI3015).
partialRequired for parts where MoCHI generates <Name>.Mochi.cs (parts with props or runtime bindings).
Program assetPlace the T# program asset <Name>.asset in the same folder. You can create it together with the .cs file via Assets > Create > T# Script (add partial to the created class).
UdonBehaviourOne is attached at each place the part is used, and it runs that program.
An existing script with the same nameIf it isn’t the .cs for the part, rename one of them.
An environment without T#Results in MOCHI8001.

The following part, Frame, becomes a part with a T# script when Frame.cs is placed next to it. The examples are checked without placing a .cs file, so only the .mochi.xml syntax is checked.

Assets/Gallery/Wall.mochi.xml
<Wall prefab="true">
<Frame name="First" index="0" position="0 1.5 0"/>
<Frame name="Second" index="1" position="1 1.5 0"/>
</Wall>
<!-- file: Assets/Gallery/Frame.mochi.xml -->
<Frame>
<Props>
<Prop name="index" type="int"/>
<Prop name="lit" type="bool" runtime="true" default="false"/>
</Props>
<Object name="Rim" active="{lit}"/>
</Frame>

The part’s C# file (not checked as an example).

using Tsukimi;
public partial class Frame : TsukimiBehaviour
{
public void Select(int selected) { SetProps(selected == index); }
}

The content MoCHI generates from the two files above (not checked as an example; the trailing fingerprint is omitted).

// This file was generated by MoCHI from Frame.mochi.xml. Do not edit it:
// edit Frame.mochi.xml or Frame.cs instead. If this file is changed by hand, generation stops until it is regenerated.
public partial class Frame
{
// Props declared in Frame.mochi.xml
[global::UnityEngine.SerializeField] private int index;
[global::UnityEngine.SerializeField, global::Tsukimi.Reactive] private bool lit;
// Runtime props arrive here, all at once
public void SetProps(bool lit)
{
this.lit = lit;
}
// Runtime props sent by name from a part with a T# script that uses this one, one prop at a time
[global::UnityEngine.SerializeField, global::UnityEngine.HideInInspector] private bool mochiInLit;
public void MochiReceiveLit()
{
this.lit = mochiInLit;
}
// Rim: active="{lit}"
[global::UnityEngine.SerializeField, global::UnityEngine.HideInInspector] private global::UnityEngine.GameObject mochiRimActive;
[global::Tsukimi.Effect] private void MochiRimActive()
{
mochiRimActive.SetActive(lit);
}
}
// MoCHI fingerprint: …
MemberDescription
Field per propprivate, with [SerializeField]. Props with runtime="true" also get [Reactive].
SetProps(…)Generated only when there are props with runtime="true". Receives all runtime props, in declaration order.
mochiIn<Name> and MochiReceive<Name>()Generated for each runtime="true" prop: a receiving field through which a part with a T# script that uses this one passes a runtime value by name, and a method that writes the received value to that prop only (the other props are not changed)
mochi<name>Active and Mochi<name>ActiveFor each runtime active binding, generates a field holding the target GameObject and an [Effect] method that calls SetActive. <name> is built from the target element’s name attribute (when not a binding) or the element’s name.
mochi<Name> and Mochi<Name>For each component attribute bound to a runtime value, a field holding the component and an [Effect] method that sets the property. <Name> is made from the GameObject’s name, the component type and the attribute name
mochi<child><prop> and Mochi<child><prop>For each attribute that passes a runtime value to a runtime prop of a child part with a T# script, a field holding the child’s UdonBehaviour and an [Effect] method that writes to the child’s receiving field and calls that prop’s MochiReceive<Name>() are generated. <Child> is made from the child element’s name (when it is not a binding) or the element’s name
Bindings inside <For>, <If> and <Else>The field of the target becomes an array, and the [Effect] method writes to its elements in order. Each place the part is used gets the objects made there (nothing is set for a branch that is not taken)
mochi<name>Arg<number>A field holding an argument, for each binding that calls a function (an array inside <For> and the like). Each place the part is used gets the value written there. The [Effect] method lists the members the function reads, as [Effect(nameof(…))]
Header note and trailing fingerprintWhether it was edited by hand is determined by the trailing fingerprint.
NamespacesIf the class is inside a namespace, generates it in the same namespace.
Parts with no generated fileParts with no props and no runtime bindings. In this case the class doesn’t need partial.
Where it is placedSame folder as the .cs file.
Props without runtimeSets the value written at the usage site on the UdonBehaviour at each place the part is used.
Props with runtime="true"The value at the usage site is the initial value. Changed during execution with SetProps(…); never assigned directly to the field.
runtime="true" on a part with no T# scriptAllowed. The received value is written to the prefab, like any other prop.
Writing to props from the classIf the part’s class writes to the field of one of its own props, it is a MOCHI6001 error. The files checked are those of the same partial class that T# compiles together, excluding .Tests.cs. Change a runtime prop with SetProps(…), or keep the value in a [Reactive] field you declare yourself. A prop without runtime is decided at generation time, and writing to it does not change the generated prefab, so keep a value you want to change in a field you declare yourself.
What counts as a writeAssignment, compound assignment, ++, --, and ref and out arguments (including forms written with this.). For props of type bool, int, float, Vector3, and Color, a write inside the field, such as pos.x = …, also counts. Writes to a parameter or local variable of the same name, to a field of another instance (other.index = …), and through SetProgramVariable do not count.
Generation on MOCHI6001<Name>.Mochi.cs is written, and the prefab is left as the previous output. Up to 10 errors are reported per part.

A value that changes at runtime arises when a runtime prop or a C# member is bound inside the body of a part with a T# script. It is handled in one of the following 26 ways depending on where it is written. The only places you can write a value that changes at runtime are active, component attributes and runtime props.

Where it’s writtenHandlingNumber
activeCan be bound to a bool runtime prop, a [Reactive] field or a [Computed] property. The GameObject’s active state is switched in the part’s Start and whenever the value changes
The value of active saved in the prefabWhen the value at start is known when the prefab is generated, the object is saved with that value (it is in that state when the world loads, and in the editor outside Play mode). The value is known for a runtime prop (the value written where the part is placed, or its default) and for a bool [Reactive] field (the value it is declared with, false when none is written). A field is excluded when the class writes it while starting (in Start, OnEnable or an [Effect], or in a method or property they use), when it is [UdonSynced], or when another script writes it. A GameObject saved inactive, and the UdonBehaviours under it, run neither OnEnable nor Start until it is first shown. A part with a T# script under it still gets the runtime props its parent passes at start, before it is first shown
Objects saved activeAn object that follows a [Computed] property or a function, and one that follows a runtime prop the parent passes while the world runs are saved active whatever the value at start is. Such an object is shown until the part’s Start; when the world loads, its components’ OnEnable runs and an AudioSource with playOnAwake starts playing. To save it hidden, bind a runtime prop or a [Reactive] field directly
active with a non-bool valueAn error.MOCHI3006
{!name}Binds the opposite of a bool value
{!name} on a value that isn’t a boolAn error.MOCHI3006
Component attributesCan bind the same things as active. The value is set on the C# property named by the attribute at start and whenever it changes. Works on components added with the part, inside <Prefab> and <At>, and with add="true"
A property Udon can’t set, or a value of the wrong typeA compile error together with the generated file, located at the attribute in the .mochi.xmlMOCHI3016
active on a member whose changes T# can’t trackAn error (anything that isn’t a [Reactive] field, a [Computed] property, or a runtime prop).MOCHI5005
A name that’s neither a prop nor a rowRead as a C# member of the part. A name that isn’t on the class errors when compiled together with the generated file.MOCHI3016
test on <If>An error. Write visibility toggles on active instead.MOCHI5003
Name, key, position, rotation, scale, static, size, pivot, <Prefab src>, non-runtime propsAn error, because it’s a value fixed at generation.MOCHI5004
Lists, UnityEvents and references to objects, and material, mainTexture, colorNot supported yet.MOCHI9001
Inside <For>, <If> and <Else>Allowed. The generated file does not depend on the data: bindings in rows that are not there and in branches that are not taken are counted too, so every entry point that uses the part writes the same file
A <Prefab> with overflow="repeat", and inside itNot supported yet.MOCHI9001
A binding that calls a function, {Name(…)}The value of a method of the part’s C# class, called with arguments fixed when generating. Use it to give each row of a <For> its own value (active="{IsCurrent(art.index)}"). It can be written only in the part’s own .mochi.xml
Arguments of a functionNumbers, true/false, 'text', props and columns of a row. The method’s parameters can be bool, int, float, string, Vector3 or Color. Write the arguments in order. Parameters with a default value at the end can be left out (C# fills in the default; MoCHI does not read it). A parameter that is left out can be of any type
An argument that changes at runtimeAn error. Write the calculation in a [Computed] propertyMOCHI5004
A call or an expression as an argument, too few or too many arguments, a named argument, and a function binding outside the partAn error.MOCHI5002
A value that does not convert to the parameter’s typeAn error.MOCHI3006
When the value of a function changesMoCHI reads the method, the methods and properties of the class it uses (in the files of the same partial class that T# compiles with it: files in the same folder or below it, or in a TsukimiShared folder under Assets/, in the same namespace, not counting .Tests.cs files) and its local functions, and sets the value again whenever a [Reactive] field, a [Computed] property or a runtime prop it reads changes. A parameter or local variable with the same name hides a field. A method that reads none of them is called once at start
Functions that read members of another instance or classOnly the members of the part’s own instance are followed. A [Reactive] field, [Computed] property or runtime prop of another instance (next.lit, next?.label) or another class (bell.ringing) does not set the binding again when it changes, so it is a warning (the generated file and the prefab are still made). A method called on another instance (next.IsLit()) is not looked into. For a value that changes while the world runs, copy it into a [Reactive] field of this instance and read thatMOCHI5006
A function MoCHI cannot readAn error when the method is not in those files, has more than one with the same name, returns nothing, changes a member it reads (also through a property’s set or a field of a struct), calls a method that is not in those files (a method of the base class, for example), or has a params, in, ref or out parameter (even one that would be left out)MOCHI3015
runtime="true" props of a child part with a T# scriptCan be passed. That prop of the child is set at start and whenever the value changes, and the child’s bindings follow. Only the props passed are set: the child’s other runtime props (including values the child changed with its own SetProps(…)) stay as they are. A prop that is passed takes the parent’s value again each time that value changes, even if the child changed it in between. The child’s .cs can be in any folder
A value that does not convert to the child prop’s typeA compile error together with the generated file, reported at the attribute in the .mochi.xml (an implicit conversion such as int to float is accepted)MOCHI3016
Props with runtime="true" on a part with no T# scriptCan be passed.
What it runsRuleNumber
Prop nameA C# identifier (ASCII letters, digits and _, not starting with a digit) that is not a C# keyword and does not start with __ or mochi (in any case). Names starting with mochi are used by the code MoCHI generatesMOCHI3014
Prop names that can’t be usedThe class name, SetProps, _, and nameof.MOCHI3015
Same name as a generated memberCan’t be written on the class. Generated members are the prop names, SetProps, and names starting with mochi or Mochi.MOCHI3015
Class declarationAn error if there is no class named <Name>, it has no partial, or it doesn’t inherit TsukimiBehaviour directly.MOCHI3015
StageDescription
1. SaveWrites <Name>.Mochi.cs first. The Behaviour uses members of the generated file, so it can’t compile before the generated file exists.
2. After compilationAfter T# compiles the Behaviour together with the generated file, the prefab is generated and the values are set on the UdonBehaviour of each place the part is used. The sync mode is the one the script declares with [UdonBehaviourSyncMode]. An <UdonBehaviour> attached to the same GameObject whose program declares a different sync mode other than None is MOCHI1012
While waitingThe entry point is waiting for compile, and uploading the world is blocked. The generated file’s Inspector shows “Waiting for T# to compile the T# scripts of its parts.”
A script that fails to compileMOCHI3016. The generated file stays as written, and the prefab isn’t updated. Fix what is at the location shown. When the failing line in the generated file was made from a runtime active binding, the location is that binding in the .mochi.xml.
No program asset, or it’s for a different .csMOCHI2013.
compiles in inspectinspect can’t tell whether T# compiled from the latest source, so compiles can be true even while Unity is still waiting. Unity makes that determination.
Editing <Name>.Mochi.csNot done. If the trailing fingerprint doesn’t match, generation pauses for every entry point that uses that part, until it’s regenerated.
DeletionMoCHI doesn’t delete <Name>.Mochi.cs. If it remains next to a part that no longer has props or runtime bindings, a MOCHI2014 warning appears.
SyntaxWrite <UdonBehaviour programSource="…/Judge.asset" speed="2.5" label="/Board/Text"/> inside an <Object>. The .cs and the program asset are used where they are
programSourceThe path to the T# program asset (.asset). It must be compiled (MOCHI2013 if not). A program asset of another compiler can’t be attached yet (MOCHI9001)
Other attributesThe program’s public variables. A name the program doesn’t have gives MOCHI1005, with the list of variables
How values are writtenDetermined by the variable’s type. bool, int, float, string, Vector3 and Color are written as for props, asset types as a path to the asset, and GameObject, Transform, components and UdonBehaviour as a path to an object in the same prefab (TextAsset can also be written as a path to a .txt, .bytes or similar file). Arrays of these types separate the items with ; (an empty value is an array of size 0). Unlike a component’s array attribute, a string array keeps empty items (words="apple; banana;" has 3 items, the third being ""). A path is resolved from the file it is written in (a path passed as a prop, from the file that passed it; a path from a table, from the table). Types such as Vector2 and Quaternion are MOCHI9001
Values T# madeMoCHI fills in the variables that take the materials of GPU kernels and the weight textures of models (made by T# for the program). Do not write them (an attribute written for one is used instead)
When values are setAs with a part’s props, in Unity after the prefab is generated
Sync modeThe sync mode the program declares with [UdonBehaviourSyncMode]. VRChat gives every synced UdonBehaviour on one GameObject the same sync mode, so attaching programs that declare different sync modes other than None (Continuous and Manual) to the same GameObject is MOCHI1012 (attach them to separate GameObjects). A program that declares None, or no sync mode, is not counted
CallingonClick="/Judge:Run" calls it
NumberAn object can have two or more. Give each a different key (a simple name). Only one may have no key. Two with the same key, or two without one, are MOCHI4001. A key also cannot be one that another component on the object has (Identity and key). key and programSource are never public variables
The root of a part with a T# scriptDirectly under the root element it needs a key: the UdonBehaviour without a key on that object is the part’s script (MOCHI4001)
#keyAdd #key to the end of a path to point to one of the object’s UdonBehaviours (onClick="/Judge#sound:Play", ear="/Judge#sound"). A path without # points to the object’s only UdonBehaviour; with two or more it is MOCHI1009. A key the object doesn’t have is MOCHI2016
Objects inside a placed <Prefab>GameObjects inside a prefab have no key, so a GameObject with two or more UdonBehaviours cannot be pointed to (MOCHI1009). Put the UdonBehaviour you point to on a GameObject in the prefab that has only that one
fileIDThe one without a key has the same fileID as when it was alone, so adding a second doesn’t break references to it
An environment without T#It gives MOCHI8001