# UdonToolkit

Purpose-built Udon Behaviours and tools to make your own

![](/files/-MQXLbgwmR6AspZPJYrD)

Udon Toolkit is a project aimed at simplifying usage of Udon by providing a set of easy to use purpose-built behaviours, from generic triggers and actions, to more complete systems like Key Items, Cabinet Drawers, etc.

This also includes a system of attributes that allow you to utilize the same UI elements when building your own behaviours!

If you find that something is broken or works not as you would expect - please ping me in the [discord server](https://discord.com/invite/fR869XP), or better - create a [new issue](https://github.com/orels1/UdonToolkit/issues/new).

### Requirements

* [VRC SDK3 with Udon](https://vrchat.com/home/download) (v 2021.06.03.14.57+)
* [Udon Sharp Compiler](https://github.com/Merlin-san/UdonSharp) (v 0.19.12+)
* \[Optional] Post Processing from Unity Package Manager **(required for the Camera System)**

### Installation

* Grab the latest package [from releases](https://github.com/orels1/UdonToolkit/releases)
* Import it into the project

{% hint style="danger" %}
**If you are upgrading from a version prior to 1.x.x** [**you'll need to follow these upgrade steps to upgrade**](/extras/migration-to-v1.0.0)
{% endhint %}

* When importing you can select what to import:
  * **Internal: REQUIRED**. Contains all of the editor functionality
  * **Systems**: Various systems listed in the **SYSTEMS** section of this documentation
  * **Behaviours**: A collection of scripts that perform actions in the world. You can find the full list in the [**BEHAVIOURS** ](/behaviours/overview)section of this documentation
  * **Demo**: assets and examples scenes for all the systems and behaviours. *You should import this only if you are importing the full package*
* Open your `Project Settings` -> `UdonSharp` and in the `Default Behaviour Editor` select `UdonToolkit Editor`

### How to use

* Browse the list of available behaviours in [the behaviours section](/behaviours/overview)
* Select a Game Object you want to use (or make a new one)
* Click `Add Component` and type the name of a Behaviour
* A new component with all the corresponding UI and parameters should appear
* If it is your first time using Udon Toolkit, you might want to click "Compile All Behaviours"&#x20;

{% hint style="info" %}
Tip: You can click on the `?` icon in the Behaviour header to open the docs for that particular script
{% endhint %}

### Known Issues

* Prefabs are fundamentally broken in Udon as of June 2021. Toolkit will show a warning if you will try to use a prefab, it is **highly recommended** to unpack the prefab before editing anything, as values can just reset to whatever they were before when entering play mode

{% hint style="success" %}
An alternative to that might be to utilize [Unity Presets](https://docs.unity3d.com/2018.4/Documentation/Manual/Presets.html) which allow you to save the values of the current behaviour into a file
{% endhint %}

### Advanced

#### UI System

UdonToolkit is not just a set of prebuilt behaviours, but also a UI system built using Unity's Custom Property Drawers and Attributes that allows you to quickly create user-friendly UI. You can [read more here](/attributes/attributes-overview).

### Thanks ❤

* To VRC Team for making Udon which actually made me learn C# and Unity Editor Tools development
* To [Merlin ](https://github.com/MerlinVR)for making [Udon Sharp](https://github.com/Merlin-san/UdonSharp) without which none of this would've been possible


# Behaviours Overview

General info on UdonToolkit behaviours

{% hint style="info" %}
Udon Toolkit provides a selection of Udon Behaviours for you to utilize as building blocks in the world
{% endhint %}

Select a category of behaviours in the list on the left

<div align="center"><img src="/files/-MQXVlCwm-bwDOoayssM" alt=""></div>

You'll see a list of all available behaviours in that category on the right

![](/files/-MQXWIom0uATmJfj2ziZ)

### How to read the docs

* **Parameters** list describes all the public variables and settings exposed to you, some of them are UI only, so if you're planning to modify them via other udon behaviours - make sure to check the actual `UdonBehaviour` component on the same object.
* **Events** list describes all the exposed custom events the component expects to receive. Most things that are expected to be "Triggered" in some way usually have a `Trigger` event exposed. The full list of events can also be seen at the bottom of the behaviour UI with a convenience button for you to use while testing in editor.
* **Usage / Examples** is a free-form section with some personal usage advice and other notes

### Overall approach

You can generally split the things below into `Triggers` and `Actions` (plus some extras), one group captures player interactions in some way - the other performs some actions in response.

Most of the time you would add a bunch of `Universal Action` behaviours to the scene and then trigger them from all the different trigger behaviours listed on this page. But this system can also easily interact with other things in your world outside of Udon Toolkit! So don't feel limited by the `Universal Action` and use the `Udon Events` list with other behaviours to mix and match stuff.

### Terms

* **Event** means Udon Custom Event
* **To Trigger** means to send a Custom Event or specifically send a `Trigger` custom event
* **Action** means some kind of reaction to your triggers, generally refers to using Universal Action behaviour

{% hint style="info" %}
Check out the included Demo scene for reference usage!
{% endhint %}


# Misc Behaviours

A collection of small, general / single purpose built behaviours

## Movement / Tracking

### Universal Tracker

*Tracks the local player's Tracking Data source, or a Bone transform and copies it over to the target transform*

![](/files/-MVMPboWK-F9zGaqAJRy)

**Parameters**

* **Track Bone**: Switches between tracking a Tracking Data source (Head/Hands) and tracking Bones
* **Bone**: *Only visible when Track Bone is checked*. Specifies which Bone to track
* **Track Player Base**: Switches to player base tracking mode where the Target Transform will follow the player's ground position
* **Track Playspace**: Switches to playspace tracking mode. Allows Target Transform to persist in players playspace, only moving when the player moves with the stick
* **Tracking Target**: *Only visible when Track Bone is unchecked*. Specifies which Tracking Data target to use
* **Target Transform**: Transform to copy the tracked Bone / Tracking Data position and rotation to
* **Track Position**: Specifies whether the position should be copied
* **Track Rotation**: Specifies whether the rotation should be copied
* **Tracking Correction**: Allows to apply an extra X/Y/Z rotation to the transform after copying it over
* **Follow Main Camera**: *For editor use only*. Makes the object follow the main camera, helpful for editor testing

**Events**

* **Reset Offsets**: Resets the player offsets from their player base tracker. Call this to calibrate player offsets at least once, otherwise the tracking will be wrong

**Usage / Examples**

The basic usage of this component is intended to be things like attaching objects to player's hands, head or both.

### Velocity Tracker

*Tracks an object or the local player's Tracking Data source using unity physics systems to provide proper collision and velocity*

![](/files/-MVMGtwG4Zly8Zpn8iLS)

**Parameters**

* **Track Transform**: Track an arbitrary object
* **Track Player**: Track player's Tracking Data source
* **Source Transform**: The Transform to follow (only visible when Track Transform is selected)
* **Source on Player**: The Tracking Data Type to follow (only visible when Track Player is selected)
* **Target**: The Transform to move
* **Track Position**: Specifies whether to follow Source's position
* **Track Rotation**: Specifies whether to follow Source's rotation

{% hint style="warning" %}
The `Target` object **MUST** have a Rigidbody for this to work
{% endhint %}

**Usage / Examples**

This behaviour is aimed to be used in cases where you want to interact with physical objects at high speeds. Good example being: adding a sphere collider to the player's hand by selecting `Track Player` and setting Right Hand or Left Hand as the `Source On Player`

That way you'll have a proper physics-driven collider following the player which can nicely interact with the world.

Another example might be - following a pickup using `Track Transform` option. Allowing you to easily implement something like a golf club.

### Lerped Follower

*Makes an object follow another object with smooth interpolation*

![](/files/-MVMPmInFtcGzsxEehuR)

**Parameters**

* **Source Transform**: The source of position and rotation to copy from
* **Target Transform**: The target of position and rotation to copy to
* **Lerp Position**: Specifies whether the position should be interpolated (smoothed) or instant
* **Lerp Rotation**: Specifies whether the rotation should be interpolated (smoothed) or instant
* **Lerp Speed**: A speed with which Target will achieve Source's position and rotation
* **Ignore Rotation**: Specifies whether to copy rotation or completely ignore it

**Usage / Examples**

Generally good feeling lerp value for scene objects seems to be at around 5-15, for the player attached object (like with Universal Tracker) higher values are recommended.

Ignore rotation can be used in cases where you want to have the object look at a particular spot or generally not rotate with the Source object. Thins like Rotation / LookAt or Aim constraints come to mind as good combinations.

### Teleporter

*Teleports an object or the local player to a desired position*

![](/files/-MVQInf21N8CSXLZfWpp)

**Parameters**

* **Teleport Object**: Switches the Teleporter to an object teleport mode
  * **Object To Teleport**: The object to be teleported to the Teleport Target's location
  * **Networked**: Teleports the object for all players
* **Teleport Player**: Switches the Teleporter to the player teleport mode
  * **Keep Player Rotation**: Maintains player view rotation after teleporting. Otherwise - the player will be aligned with the Z axis of Teleport Target
* **Teleport Target**: The target position / rotation to teleport objects and players to

{% hint style="warning" %}
Make sure to only use `Networked` option when calling Teleporter locally. Otherwise you will have a double-teleport
{% endhint %}

**Methods**

* **Trigger**: Executes the teleport

### Player Movement Modifier

*Adjusts different player movement parameters like speed and gravity*

![](/files/-MVsoFZ4Mk6LsHWZnU1v)

**Parameters**

* **Jump Impulse**
  * **Jump Impulse Change Type**: Determines how the jump impulse will be adjusted
  * **Jump Impulse**: The value to use for jump impulse adjustments
  * **Set Jump Impulse**: Specifies if the jump impulse needs to be adjusted
* **Walk Speed**
  * **Walk Speed Change Type**: Determines how the walk speed will be adjusted
  * **Walk Speed**: The value to use for walk speed adjustments
  * **Set Walk Speed**: Specifies if the walk speed needs to be adjusted
* **Run Speed**
  * **Run Speed Change Type**: Determines how the run speed will be adjusted
  * **Run Speed**: The value to use for run speed adjustments
  * **Set Run Speed**: Specifies if the run speed needs to be adjusted
* **Strafe Speed**
  * **Strafe Speed Change Type**: Determines how the strafe speed will be adjusted
  * **Strafe Speed**: The value to use for strafe speed adjustments
  * **Set Strafe Speed**: Specifies if the strafe speed needs to be adjusted
* **Gravity Strength**
  * **Gravity Strength Change Type**: Determines how the gravity strength will be adjusted
  * **Gravity Strength**: The value to use for gravity strength adjustments
  * **Set Gravity Strength**: Specifies if the gravity strength needs to be adjusted

**Methods**

* **Trigger**: Executes the selected adjustments

**Usage / Examples**

This behaviour allows you to adjust different player parameters, like run or strafe speed. You can either just set a specific value, or use a relative adjustment by specifying a change type other than `SET`.&#x20;

Only the values that have checked checkboxes will actually be adjusted.

Example: Adding 2 to player's run speed

1. In the Run Speed section - select `ADD` in the change type dropdown
2. Set the value to `2`
3. Check the checkbox on the far right to tell the behaviour to actually change the value
4. Send a Trigger event to the Behaviour (via any of the triggers, e.g, an [Interact Trigger](/behaviours/misc-behaviours#interact-trigger)) to perform the adjustments

## Triggers / Actions

### Universal Action

*The bread and butter of any world reactivity, allows you to perform various actions in response to an incoming `Trigger` event. Think about it as a VRC SDK2 Triggers Lite*

![](/files/-MVlWG1332YkZChicJVL)

**Parameters**

* **Active**: Determines if the behaviour is running, since toggling game objects on and off only means they won't run `Update`
* **One Shot**: Specifies if this trigger can only be activated once
* **Fire After Delay**: *Object must be enabled at all times for this to work*. Specifies whether the triggers should be fired after a delay
* **Delay Length**: *Only visible when Fire After Delay is checked*. Sets the delay length in seconds
* **Animations**
  * **Fire Animation Triggers**: Specifies whether anything in this section should be executed
  * **Animator Triggers List**: A list of Animators and Triggers to fire, you can specify the same Animator multiple times to fire multiple triggers
  * **Animator Bools List**: A list of Animators and Booleans to be set to a provided value
  * **Animator Floats List**: A list of Animators and Floats to be set to a provided value
  * **Animator Ints List**: A list of Animators and Ints to be set to a provided value
* **Udon Events**
  * **Fire Udon Events**: Specifies whether anything in this section should be executed
  * **Networked**: When checked - Udon Events will be sent to targets set in Network Targets
  * **Network Target**: Specifies who to send the network events to
  * **Udon Events List**: A list of Udon Behaviours and Events to call
* **Game Object Toggles**
  * **Fire Object Toggles**: Specifies whether anything in this section should be executed
  * **Game Objects List**: A list of Game Objects to Enable, Disable or Toggle
* **Collider Toggles**
  * **Fire Collider Toggles**: Specifies whether anything in this section should be executed
  * **Colliders List**: A list of Colliders to Enable, Disable or Toggle
* **Audio**
  * **Fire Audio Events**: Specifies whether anything in this section should be executed
  * **Audio List**: A list of Audio Sources and Audio Clips to play through them

{% hint style="warning" %}
Make sure to only use Networked option when executing this trigger locally, e.g. via a UI Button, otherwise you might cause oversync
{% endhint %}

**Events**

* **Trigger**: Triggers this Universal Action
* **Activate**: Activates the behaviour, so it will react to Trigger events
* **Deactivate**: Deactivates the behaviour, so it will no longer react
* **Toggle**: Toggles the active state, provided for convenience
* **ResetOneShot**: Allows this trigger to be activated again

**Usage / Examples**

Universal Action will probably be the most used behaviour in your world, as its basically a simplified trigger from SDK2. It is not as deep as the original triggers, though, so you are expected to combine it with more specific behaviours from Udon Toolkit or other places to extend its functionality.

This component makes it pretty trivial to do things via UI Buttons simply by calling a `SendCustomEvent` on it with `Trigger` as the event name.

You can check out a guide on setting up a basic mirror [right here](/guides/mirror-toggle) which should provide a good example of Universal Action usage.

The major thing to note here is to make sure that the component, and the game object its on is always enabled if you're using the `Delay` parameter, otherwise it will never fire as the Update method will never be called.

### Secret Actions

*Sends events to udon behaviours based on the player's name*

![](/files/-MVlVoyrX4jCtm_f6vBM)

**Parameters**

* **Active**: Determines if the actions will be executed on Start
* **Actions List**: A list of Player Names and Udon Behaviours to send the provided Events to

**Events**

* **Trigger**: Executes the logic again, you will probably want this to be called from the network to run for everyone again

**Usage / Examples**

Ever wanted to have something special activate only for particular users? This behaviour does exactly that! Combine this with Universal Action to do something special for that user, or specify behaviours of your own.

If sending events once isn't enough - you can use the `Trigger` event to fire it again.

### Networked Trigger

*A "proxy" of sorts that expects a "Trigger" event and sends Networked events to specified behaviours*

![](/files/-MVlTubDoctPdCkfF7gp)

**Parameters**

* **Event Target**: Specifies who to send the events to
* **One Shot**: Specifies if this trigger can only be activated once
* **Master Only**: Limits the trigger execution to Master only (everyone will get the trigger effects)
* **Owner Only**: Limits the trigger execution to Owner only (everyone will get the trigger effects)
* **Allowed Users**: Limits the trigger execution to the provided list of users (everyone will get the trigger effects)
* **Udon Events List**: A list of Udon Behaviours and Events to send to them

**Methods**

* **Trigger**: Fires the events on the provided behaviour over the network
* **Activate**: Activates the behaviour, so it will react to objects
* **Deactivate**: Deactivates the behaviour, so it will no longer react
* **Toggle**: Toggles the active state, provided for convenience
* **ResetOneShot**: Allows this trigger to be activated again
* **TakeOwnership**: Makes local player the owner of the object

**Usage / Examples**

Think about this as a middle-man between local only things and the network. While its not that useful on its own - it might serve as a nice layer between UI Buttons, and some other behaviours, that you want to call for everyone in the room, or their owners.

{% hint style="info" %}
You can achieve the same results by using Universal Action and checking the Networked checkbox, but this is lighter on performance, so this separation is just a way to simplify things.
{% endhint %}

### Area Trigger

*Sends events to other behaviours when an object enters and exits a trigger collider*

![](/files/-MX44owi4oZz0ExtytUB)

**Parameters**

* **Active**: Specifies whether the triggers will fire, as just disabling the object doesn't stop it from reacting to enter/exit events
* **One Shot**: Specifies if this trigger can only be activated once
* **Collide Target**: Specifies what this trigger will collide with: Objects or Players
* **Collide With**: Specifies the list of layers to filter out the objects you don't want to react to (only visible when Collide Target is set to Objects)
* **Collide With Local Players**: Specifies whether the event would fire on local player collisions&#x20;
  * (only visible when Collide Target is set to Players)
* **Collide With Remote Players**: Specifies whether the event would fire on remote player collisions&#x20;
  * (only visible when Collide Target is set to Players)
* **Master Only**: Limits the trigger execution to Master only (everyone will get the trigger effects)
* **Owner Only**: Limits the trigger execution to Owner only (everyone will get the trigger effects)
* **Allowed Users**: Limits the trigger execution to the provided list of users (everyone will get the trigger effects)
* **Networked**: Specifies whether the events should be sent over the network or only fired locally
* **Network Target**: Specifies who to send the network events to
* **Enter Events List**: A list of Udon Behaviours and Events to send when the correct object enters the trigger collider
* **Exit Events List**: A list of Udon Behaviours and Events to send when the correct object exits the trigger collider

**Events**

* **Activate**: Activates the behaviour, so it will react to objects
* **Deactivate**: Deactivates the behaviour, so it will no longer react
* **Toggle**: Toggles the active state, provided for convenience
* **ResetOneShot**: Allows this trigger to be activated again
* **TakeOwnership**: Makes local player the owner of the object

**Usage / Examples**

This is an equivalent of VRC SDK2's `Trigger Enter` and `Trigger Exit` triggers

Some things to note when using Area Triggers

* Do not use the `LocalPlayer` or `Player` layers in the `Collide With` option, use the `Collide With Local Players` and `Collide With Remote Players` checkboxes instead
* Generally speaking you would want the `Area Trigger` to be on the `MirrorReflection` layer to avoid some weird vrchat issues, unless you have a case where you need a custom layer
* Do not use the `Networked` option if you are using `Collide With Remote Players`, as this will make everyone send events to everyone all the time, causing oversync and possibly desyncing people in the instance.

{% hint style="info" %}
A good rule of thumb when it comes to things like collisions: "if everyone can see object X enter trigger Y - you can keep everything local"
{% endhint %}

### Interact Trigger

*Makes something react to player interaction, VRC SDK2-style*

![](/files/-MVlPR8WVvyDgU92mq9L)

**Parameters**

* **Active**: Specifies whether the object can be interacted with
* **One Shot**: Specifies if this trigger can only be activated once
* **Master Only**: Limits the trigger execution to Master only (everyone will get the trigger effects)
* **Owner Only**: Limits the trigger execution to Owner only (everyone will get the trigger effects)
* **Allowed Users**: Limits the trigger execution to the provided list of users (everyone will get the trigger effects)
* **Networked**: Specifies whether the Udon Events should be sent to other players
* **Network Target**: Specifies who to send the Udon Events to
* **Udon Events**: A list of Udon Behaviours and Events to send

{% hint style="info" %}
To set interaction text or activation distance - click the `Udon Settings` button to unfold the Udon Behaviour menu
{% endhint %}

**Methods**

* **Interact**: Sends events to provided Udon Behaviours
* **Activate**: Activates the behaviour, so the object will become interactive
* **Deactivate**: Deactivates the behaviour, so the object will no longer be interactive
* **Toggle**: Toggles the active state, provided for convenience
* **ResetOneShot**: Allows this trigger to be activated again
* **TakeOwnership**: Makes local player the owner of the object

**Usage / Examples**

This is an almost one-to-one equivalent of the VRC SDK2 `Interact` trigger. Use in combination with `Universal Action` to do something when user clicks on an object.

`Activate`/`Deactivate`/`Toggle` events will also allow you to disable the highlight on the object, if you don't want it to be interactive anymore.

### Synced Trigger

Provides basic functionality for syncing an action for late-joiners

![](/files/-MVMBHReLuKGFLY7sw8X)

**Parameters**

* **Synced Value**: Specifies the initial state of the trigger (based on which the ON/OFF events are fired)
* **Use Interact**: Specifies whether the trigger is placed on an interactable object
* **Master Only**: Limits the trigger execution to Master only (everyone will get the trigger effects)
* **Owner Only**: Limits the trigger execution to Owner only (everyone will get the trigger effects)
* **Allowed Users**: Limits the trigger execution to the provided list of users (everyone will get the trigger effects)
* **ON Events**: List of events to send when the Synced Value will be set to True
* **OFF Events**: List of events to send when the Synced Value will be set to False

**Methods**

* **Toggle**: Switches the current synced value to the opposite
* **TurnOn**: Sets the current synced value to True
* **TurnOff**: Sets the current synced value to False
* **TakeOwnership**: Changes the owner of the object, useful for `OwnerOnly` synced triggers

**Usage / Examples**

This behaviour is aimed at creating an equivalent of a buffered trigger from SDK2 (`AlwaysBufferOne`). Late joiners will execute the methods for the current value of the `Synced Value` unless it is in the default state.

You can put this behaviour on some sort of a button or a switch in the world together with a `Use Interact` option - so that players will be able to trigger it by clicking on the switch.

**Logic Overview**

1. Player triggers the Synced Trigger (either by clicking on the object, or otherwise)
2. Synced Trigger checks if they have access based on `MasterOnly`, `OwnerOnly` and `Allowed Users`
3. If player has access - the Synced Value is changed, the player triggers ON or OFF events respectively
4. All remote players trigger ON or OFF events respectively
5. When a player joins into the world - they receive the current value of Synced Value. If it is the same as the initial value set in Unity - nothing will happen. If the value is different - they will trigger ON or OFF events respectively

### Object Boundary Trigger

*Sends Events to Udon Behaviours if a target object crosses a particular threshold*

![](/files/-MVMRRv9wtNoNCtEDoYt)

**Parameters**

* **Active**: Toggles the threshold checks on and off
* **Target**: The target object to observe
* **Cross Mode**: Makes this trigger run every time the Target crosses the threshold (either way), also makes it, so the trigger doesn't disable itself after the threshold is crossed
* **Test Mode**: Specifies whether the trigger will fire if ALL or ANY of the specifies boundaries are crossed
* **Boundary List**: A list of conditions and coordinates to check against, e.g. `Above Y: 10` will make the trigger fire events if the Target gets above 10 units on Y coordinate
* **Networked**: Specifies whether the Udon Events should be sent to other players
* **Network Target**: Specifies who to send the Udon Events to
* **Udon Events**: A list of Udon Behaviours and Events to send

**Events**

* **Trigger**: Fires the events specified in the Udon Events list
* **Activate**: Activates the behaviour, so it will start checking the boundaries
* **Deactivate**: Deactivates the behaviour, so it will no perform any checks
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

While this might seem confusing at first, the gist of this one is to trigger something when object's position exceeds one of the limits. A good example of it would be using a player head tracker and making it so if the tracker's Y position is below your water level in the map - you enable special fog settings or something like that.

By default, the trigger will only run once and disable itself, otherwise it will start triggering things every frame which is not perfect. You can enable it again by sending an `Activate` even to it.

Although often you might want to trigger things when the object crosses the limit in either direction. Like turning the fog on and off in the example above. In that case you would want to use the `Cross Mode` option that will trigger once every time the limits are crossed.

### Platform Trigger

*Sends Events to Udon Behaviours based on the User's Platform*

![](/files/-MVMS7NbqXMessFh9HiP)

**Parameters**

* **Fire On Start**: Specifies whether the events should be sent on start
* **Desktop Events**: A list of Udon Behaviours and Events to send if the Player is on Desktop
* **VR Events**: A list of Udon Behaviours and Events to send if the Player is in VR

**Events**

* **Trigger**: Fires the events specified in the Desktop/VR Events Lists

**Usage / Examples**

By default this trigger will fire on the world load, unless the `Fire On Start` is unchecked.

Generally speaking this behaviours is aimed at providing better cross-platform support. Its pretty common to have separate versions of game mechanics, pickups, etc for different platforms that are enabled and disabled on Start.

You can also send the `Trigger` event to `Platform Trigger` to execute it again, e.g. if something happens in your world and you want Desktop / VR react to it differently.

### Start Trigger

*Sends Events to Udon Behaviours on scene start*

![](/files/-MVMJi7j9H0uZtmvaVSP)

**Parameters**

* **Active**: Specifies whether it will fire the triggers or not
* **Networked**: Specifies whether the events should be sent over the network or executed locally (check Usage section below to learn when you might want to use this)
* **Network Target**: Specifies who the network event should be sent to
* **Udon Events List**: A list of Udon Behaviours and Events to send

**Methods**

* **Trigger**: Allows to trigger the events again even after the scene already started

**Usage / Examples**

This is as basic as it gets. This behaviour will send some events when the world loads and nothing else.

However, there is an interesting use-case for `Networked` option. If it is checked, and `Network Target` is set to `Owner` - by default the world Master will receive an event every time a player joins. If set to `All` - every player will receive an event when another player joins, which might be useful in some cases.

### Respawn Trigger

*Sends Events to Udon Behaviours on player respawn*

![](/files/-MVlORJOfnwRJN3mV7-F)

**Parameters**

* **Active**: Specifies whether it will fire the triggers or not
* **One Shot**: Specifies if the trigger can only be activated once
* **Local Only**: Sets the trigger to only react to local player respawns
* **Master Only**: Limits the trigger execution to Master only (everyone will get the trigger effects)
* **Owner Only**: Limits the trigger execution to Owner only (everyone will get the trigger effects)
* **Allowed Users**: Limits the trigger execution to the provided list of users (everyone will get the trigger effects)
* **Networked**: Specifies whether the events should be sent over the network or executed locally (check Usage section below to learn when you might want to use this)
* **Network Target**: Specifies who the network event should be sent to
* **Udon Events List**: A list of Udon Behaviours and Events to send

**Methods**

* **Activate**: Activates the behaviour, so it will start checking the boundaries
* **Deactivate**: Deactivates the behaviour, so it will no perform any checks
* **Toggle**: Toggles the active state, provided for convenience
* **ResetOneShot**: Resets the one shot state allowing the trigger to be activated again
* **TakeOwnership**: Makes local player the owner of the object

## Visuals

### Skybox Adjustment

*Switches skybox materials or smoothly adjusts values of a single skybox material*

![](/files/-MVMOJaKWGba0WyrZ3BL)

**Parameters**

* **Active Skybox**: The skybox material to transition to
* **Active Floats List**: A list of numeric parameters to transition to
* **Active Color List**: A list of color parameters to transition to
* **Active Vector3 List**: A list of Vector3 parameters to transition to
* **Instant Transition**: Makes the skyboxes switch instantly
* **Transition Time**: Defines the time, in seconds, over which the Float/Color/Vector3 values will be transitioned from default values to active and vice-versa

**Events**

* **Trigger**: Performs a transition from default to active. Send it again to transition back

**Usage / Examples**

Main thing to decide on when using this is whether you need to transition instantly or slowly over time. If you want to transition instantly - its very simple

* Select a material you want to swap the skybox to and put it into the `Active Skybox` field
* Send a `Trigger` event from one of the Udon Toolkit's many triggers when you want to switch
* Done

If you want to slowly transition between different settings, e.g. between high exposure and low exposure to imitate the day/night cycle - its a bit of a different setup

* Uncheck the `Instant Transition` checkbox and specify the `Lerp Time` in seconds
* Expand the `Active Floats List` and click `Add Element`
* Choose `_Exposure` from the dropdown and set the value you want to transition to (e.g. 0.2)
* Send a `Trigger` event from one of the Udon Toolkit's triggers when you want to start transitioning
* Done

{% hint style="warning" %}
The Skybox Adjustment behaviour will use currently active values as a starting point (as of version v0.4.5)
{% endhint %}

### Fog Adjustment

*Switches between two different fog settings - instantly, or over time*

![](/files/-MVMMgjuTlB_XvFzmUcD)

**Parameters**

* **Default Fog Color**: First fog color to transition from
* **Default Fog Density**: *Only visible if the fog mode is not Linear*. First fog density to transition from
* **Default Fog Start**: *Only visible if the fog mode is Linear*. First fog start range to transition from
* **Default Fog End**: *Only visible if the fog mode is Linear*. First fog end range to transition from
* **Active Fog Color**: Second fog color to transition to
* **Active Fog Density**: *Only visible if the fog mode is not Linear*. Second fog density to transition to
* **Active Fog Start**: *Only visible if the fog mode is Linear*. Second fog start range to transition to
* **Active Fog End**: *Only visible if the fog mode is Linear*. Second fog end range to transition to
* **Fog Transition Time**: Specifies the duration of the transition, 0 to make it instant
* **Start Active**: Makes it, so the fog will start from the active state, so the first transition will be Active to Default, instead of the other way around
* **Set Initial State**: Applies the Default or Active fog settings, based on whether the `Start Active` checkbox is set

**Events**

* **ActivateFog**: Explicitly transitions from Default to Active state
* **DeactivateFog**: Explicitly transitions from Active to Default state
* **Trigger**: Switches between the two states or starts a smooth transition

**Usage / Examples**

Very similar to [`Skybox Adjustment`](https://github.com/orels1/UdonToolkit/wiki/Misc-Behaviours/#skybox-adjustment) in terms of functionality.\
Set the Default and Active setting and then use something like an `Area Trigger` to switch between them when a player enters or exits a particular area.

If you want to just slowly adjust from current values to the Active values - you don't have to set the `Default` settings at all. Those are only needed if you want to use 2 specific states and toggle between them.

## Advanced

{% hint style="info" %}
This area intended for advanced content creators and people developing their own Udon stuff just to make their life easier and more organized, be advised
{% endhint %}

### UI Readout

*Takes compatible variable values from one Udon Behaviour and displays them in UI Text or UI Slider elements, with formatting support*

![](/files/-MVMSjo3G0hP41bUQqO-)

**Parameters**

* **Active**: Turns the value update on and off
* **Source**: The source of the values
* **Text Readouts List**: A list of variables that can be converted to text and UI Text components to display them i&#x6E;*.* Formats allow you to format the text before displaying. See Usage below
* **Text Readout Formats**: A list of formatting rules applied to each readout value
* **Slider Readouts List**: A list of numeric variables that can be assigned to a value of a UI Slider. Multipliers allow you to adjust the value before assigning it to a slider. See Usage below

**Events**

* **Activate**: Activates the behaviour, so it will start reading fresh values
* **Deactivate**: Deactivates the behaviour, so it stops reading values
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

> The values are grabbed and assigned in `LateUpdate` to have the most up to date values possible

The formatter works by replacing `{0}` in whatever text you provide to each formatter with the value of the variable. So something like `Speed: {0} u/s` for a variable `speed` in the `Target` that has value of `10` will result in `Speed: 10 u/s` being displayed in the text element.

Multipliers work in a similar fashion - by multiplying a corresponding variable value by this multiplier before passing it to a slider.

`UI Readout` is built to provide a quick way of setting up something like a HUD you can attach to player's head via `Unversal Tracker` that would print out all the needed information from something like a flight system, or a combat system of some sorts. It will pre-populate lists of compatible variables for you, so you wouldn't need to dig into the code to figure out what you can display.

Note that this process can be performance heavy, so you don't want many of these behaviours running at all times, use the `Toggle` event to only read values when needed.

### Shader Feeder

*Passes various shader properties to a provided Mesh renderer*

![](/files/-MVMFlok0mAEceLdlLjg)

**Parameters**

* **Active**: Turns the shader property value updates on and off
* **Source**: The shader to get the property names from
* **Targets**: The list of MeshRenderers to pass the property values to
* **Custom Update Rate**: Specifies whether a custom Update Rate should be used, if unchecked - the values will be passed every frame
* **Update Rate**: (Only visible if Custom Update Rate is checked) Specifies how often the values should be set, Per Second or Per Minute (think of it as BPM mode)
* **Set Scene Start Time**: Specifies whether scene start time should be passed on world load
* **Start Time Target**: Name of the property to pass the start time to
* **Set Cycle Length**: (Only visible if Custom Update Rate is checked) Specifies whether the current cycle time (based on a Custom Update Rate) should be passed on Start
* **Cycle Length Target**: Name of the property to pass the cycle length to
* **Set Cycle Start Time**: Specifies whether the current time (which is also the time of the current update cycle start) should be passed
* **Cycle Time Target**: Name of the property to pass the cycle start time to
* **Slider Sources**: List of Slider components and corresponding property names to pass the Slider values to
* **Transform Sources**: List of Transforms and corresponding property names to pass the Transform positions to
* **Text Sources:** List of UI Input Fields and corresponding property names to pass the input fields values to. Values will be cast to `float` so make sure to only enter numbers into them
* **Udon Variables**: List of Udon Behaviours, their variables and corresponding property names to pass their values to. Values will be cast to the provided type. Allowed types: `float`, `int`, `Color`, `Vector2/3/4`

**Usage / Examples**

One of the most common examples would be to use `ShaderFeeder` as a way to pass some object's position to a shader. Player position being one of those, if used in combination with [UniversalTracker in a Player Base Tracking mode](/behaviours/misc-behaviours#universal-tracker).

To do that you will want to add a Transform that follows the Player to the Transform Sources list and choose a property to pass the value too and that's it!

Another popular choice would be to make an object do something on beat, in that case all you need is the Cycle Start Time and Cycle Length to be passed every Update Cycle. Check the Custom Update Rate and select Per Minute as the mode with your target BPM, and you're done!

The `ShaderFeeder` demo scene includes 2 basic shaders for things mentioned above: a Light Bridge of sorts that tracks a transform, and a BPM synced pulse with adjustable hue.

You can also read any public variable of an Udon Behaviour and pass it to the material using the Udon Variables list. Its extremely useful when building game logic driven effects, and I highly encourage you to play around with it!


# Camera System

A Depth of Field camera system with VR Friendly physical controls and a desktop mode

![](https://cdn.vrchat.sh/ut/camera_system/camera_system_header.png)

## Quick Setup Guide

* Follow the general [installation guide](/#requirements)
* Import the PostProcessing package by clicking `Window` -> `Package Manager` in Unity and searching for `PostProcessing`
* Create an empty object in your scene where you want your camera to be spawned
* Click on `Window` -> `UdonToolkit` -> `Camera System Setup` in the unity top bar
* Drag and drop the created empty into the Camera Position field
* Provide a watermark overlay if you want to draw a watermark on top of the pictures people take, e.g. the world logo

{% hint style="warning" %}
The watermark should be a transparent 16:9 image (1920x1080)
{% endhint %}

* You can also add one of the default guide panels near the camera by checking "Add Camera Guide". This option is recommended, so your players know how to use the system
* Click `Setup Layers`
* Click `Create Camera System`
* Click on the created `Camera Lens` object and there `Compile All Udon Sharp Programs` at the bottom of the Udon Behaviour component
* You're done!
* If the positioning isn't exactly how you like it: `Ctrl+Z`, move the empty object and click `Create Camera System` again

Now you can build your project, and the camera should be working!

![](/files/-MVMV7feYEQMCr2F4W5S)

## Full Documentation

![](/files/-MVMTJYz_lB3mKELQlpl)

### **Customization tips**

* You can change the watermark after the creation of Camera System via the setup script by adjusting the material on `Camera Tracker` -> `Sphere` object
* You can change Focus range by adjusting `Focus Distance` in the `Camera Lens Profiles` -> `Camera Lens PP Far` object's PostProcessing settings

  > When adjusting Focus range, don't forget to change the `Auto Focus Distance` on the `Camera Lens` object
* You can adjust minimum focal length by changing the Focal Length in the `Camera Lens Profiles` -> `Camera Lens PP Focal` object's PostProcessing settings
* You can adjust maximum focal length by changing the Focal Length in the `Camera Lens Profiles` -> `Camera Lens PP` object's PostProcessing settings
* You can adjust finger sphere's size and look by changing the `Camera Left Finger` -> `Sphere` object

### **Parameters**

* **Active**: Specifies whether the camera is actively responding to inputs
* **Lens Animator**: An animator that controls Camera PostProcessing profiles
* **Camera Animator**: The main animator that controls Camera UI
* **Pickup**: The camera pickup component
* **Auto Focus**: Specifies whether the auto focus is currently enabled
* **Auto Focus Distance**: Specifies the auto focus max distance (should correspond to the `Camera Lens PP Far` focus distance setting)
* **User Interface**
  * **Desktop UI**: The UI Canvas for Desktop players
  * **VR UI**: The UI Canvas for VR Players
  * **Focus/Focal/Zoom Sliders**: The Focus/Focal/Zoom UI Sliders for Desktop
  * **VR Focus/Focal/Zoom Sliders**: The Focus/Focal/Zoom UI Sliders for VR
  * **Focus/Focal/Zoom Text**: The Focus/Focal/Zoom UI Text that is adjusted to display the currently active control
  * **Focus/Focal/Zoom BG**: The Focus/Focal/Zoom UI Text that is adjusted to display the currently active control
  * **Position Text**: UI Text that displays if the camera exists in World or Player relative space
* **VR Controls**
  * **Vr Finger Reference**: Reference to the finger tracking object to be used for VR interactions
  * **Sphere Radius**: The size of the sphere triggers
  * **Tutorial Sphere**: The position of the tutorial pass sphere trigger
  * **Flip Sphere**: The position of the camera flip sphere trigger
  * **Always On Sphere**: The position of the always on sphere trigger
  * **Focus Sphere**: The position of the autofocus sphere trigger
  * **World Space Sphere**: The position of the world space / local space switch sphere trigger
* **View Sphere**: The Camera View sphere which override's player's desktop view or camera view (based on type of player)
* **Camera Object**: Object that has a Camera component on it
* **Visuals**: The camera visual mesh
* **Finger Sphere**: The Mesh Renderer of the finger collider (`_Enabled` property will be set depending on whether the camera is held)
* **Auto Focus Icon**: Auto Focus Frame in Desktop UI that will be colored `Active Control Color` when enabled
* **Always On Icon**: Always On Frame in Desktop UO that will be colored `Active Control Color` when enabled
* **Active Control Color**: The color used in Desktop UI to signify that control is enabled
* **Drop Target**: The Transform camera will be reparented to when Player relative space lock is enabled
* **Playspace Tracker**: The Universal Tracker instance with `Track Playspace` enabled (camera will automatically call `ResetOffsets` on it whenever it is picked up and dropped)

### **Events**

* **SwitchPosition**: Switches between World and Player relative positioning
* **FlipCamera**: Flips the camera direction and screen
* **SwitchAF**: Toggles Auto Focus on and off
* **SwitchControl**: Switches between currently active controls (Focus/Focal/Zoom)
* **ToggleAlwaysOn**: Toggles the Always On mode (camera stays active on drop when Always On is enabled)
* **ToggleWatermark**: Toggles final image watermark on and off

### **Usage and Examples**

{% hint style="info" %}
You are expected to use `Camera System Setup` UI from `Window` -> `UdonToolkit` -> `Camera System Setup` in the unity top bar to set everything up, consult the top of this page for the required steps. But here is a guide on the manual setup
{% endhint %}

* Drag and drop `UT Camera System` from `UdonToolkit/Camera System` folder into the scene
* Right click and click `Unpack Prefab Completely` (that is very important)
* Move out all the objects inside `UT Camera System` into the root of the scene (also important)
* By default camera is set up to use layer 27 for PostProcessing, and layer 28 for the VR Controls
* VR Controls layer is expected to be set to only collide with itself, otherwise you might get unwanted vr control interactions even if you did not actually press anything with your finger
* Make sure those exist or change them
  * You can set objects inside `Camera Lens Profiles` to any layer that is not `PostProcessing` or `Default`
  * Don't forget to update the target layer of the `PostProcessing Layer` component on the `Camera Lens` -> `Lens Camera` object
  * When using a custom VR Controls layer, make sure to change tha layer of `Camera Left Finger`, as well as the `VR Controls` and `Start Sphere` objects
  * If you changed the VR Controls layer - you'll also need to update it in each `Sphere` object's Area Trigger `Collide With` parameter
* Click `Compile All UdonSharp Programs`
* Check that every UdonBehaviour in the system shows set public variables: check `Camera Lens`, any of the `VR Controls` -> `Sphere` objects, `Camera Tracker` object, any of the `Camera Flip`, `Always On`, `Focus` and `Switch Position` objects
  * If any of them show `No Public Variables` - click `Force Compile Script`
* Click on the `Camera Lens` object and uncheck `Is Active` on the `Parent Constraint`
* Move the Camera Lens wherever you want
* Click `Activate` on the `Parent Constraint`
* Uncheck `Is Active`, expand `Constraint Settings`
* Uncheck `Lock` and set all the `Position Offset` and `Rotation Offset` values to 0, check `Lock` again
* Check `Is Active`
* You should now be ready to use the camera in your world!

{% hint style="info" %}
This is a bit of an involved process, hence why the automated setup was created, the manual guide is provided for reference of what UdonToolkit is actually doing when you click **Create Camera System**
{% endhint %}


# Flight System

Add the power of flight to your world

![](/files/-MQiwUIe5g1Gqz6FWVL1)

{% embed url="<https://youtu.be/b4IVc7wMA6g?t=60>" %}
Flight System preview
{% endembed %}

{% hint style="info" %}
Udon Toolkit's Flight System allows your players to fly like a superman, glide across the ground and hover mid air on both Desktop and VR platforms!
{% endhint %}

**Check it out in my** [**Catch A Flight world**](https://vrchat.com/home/launch?worldId=wrld_65fd7c51-89f7-4c7b-8a4f-2620a16f0a75)**!**

A more developer-oriented test world with more info is [also available here](https://vrchat.com/home/launch?worldId=wrld_842e079c-8ad3-4c99-afa2-ac6afaf78176)

## Features

* Cross-platform (VR/Desktop) flight system with Superman-like controls (point toward direction)
* Ability to glide across the ground maintaining consistent height
* Ability to hover mid-air
* One Handed mode support
* Speed Boost and Flight Restriction mechanics
* Hover and Gliding mechanics extendable with your own logic via provided callback events

## Installation

1. Drag the Flight System prefab from the `UdonToolkit/Flight System` folder into your scene
2. Build a test version
3. Jump to start flying!

{% hint style="warning" %}
Testing in editor requires [CyanEmu](https://github.com/CyanLaser/CyanEmu)
{% endhint %}

## Usage

* **VR**
  * Point your arms slightly forward and up, jump to start the flight. If glide is enabled (it is by default) - point your arms slightly above your head to stop the glide and takeoff
  * Spread your arms to slow down, bring them together to speed up
  * Point where you want to fly. Think about it as if you were a Superman
  * Point back to freeze mid-air
  * When gliding - point upwards, above your head to take off
  * When gliding - spread your arms to slow down and point downward to land
* **VR One Handed**
  * Point the selected one handed tracking source where you want to fly (Head, Left Hand or Right Hand)
  * Press Left Trigger or Right trigger to slow down
  * When gliding - look up to take off
  * When gliding - press down the trigger and look towards the ground to land

{% hint style="info" %}
During glide when going full speed (arms close together, pointing in same direction) you will not be able to land by pointing your arms towards the ground. That is done to help with arm fatigue allowing for extended periods of gliding without getting tired. To land - slow down by spreading your arms and point them towards the ground
{% endhint %}

* **Desktop**
  * Jump to start flying, if glide is enabled (it is by default), press and hold `T` to takeoff
  * Use your mouse to control the direction
  * **There are currently no speed controls on desktop**
  * If hover is enabled (it is by default), press `F` to enter hover mode, you'll freeze in place allowing you to look around and chat with people. Press `F` again to exit hover
  * Press `P` to respawn to the **Reset Position**

## Configuration

{% hint style="success" %}
UdonToolkit's FlightSystem provides many configuration options for you to adjust. I recommend still using the prefab as a starting point for easier configuration though.
{% endhint %}

![](/files/-MQifzvMIKZ2ZI8aE5fW)

### Parameters

* Tracking References
  * **Right Hand**: Right hand tracking object
  * **Left Hand**: Left hand tracking object
  * **Head**: Head tracking object
* General Settings
  * **Max Speed**: Maximum flight speed
  * **Allow Boosting**: Specifies whether the `Boost` event will do anything
  * **Boost Length**: Determines the time of the boost in seconds
  * **Boost Speed**: Maximum speed during boost
  * **Allow Gliding**: Specifies whether gliding along the surface is allowed
  * **Ground Layers**: Layers used to determine the ground altitude used for landing/takeoff speed scaling and gliding
  * **Allow Hover**: Specifies whether hovering is allowed using the hover key

{% hint style="warning" %}
If **Allow Gliding** is checked - Desktop players will need to press the takeoff/landing key in order to enter flight or exit glide
{% endhint %}

* Comfort Settings
  * **Acceleration Smooth Factor**: The lerping amount applied to the acceleration (lower is smoother)
  * **Deceleration Smooth Factor**: The lerping amount applied to deceleration (lower is smoother)
  * **Ground Slow Max Height**: The maximum height at which the takeoff/landing assist will take place. FlightSystem will scale the maximum speed based on the distance from the ground to aid with precise landing and smoother takeoff
  * **Min Ground Speed Modifier**: Specifies the speed threshold at which the system will automatically slow the player down during takeoff/landing. The value of `0.5` will mean that the player will be never be slowed down to less than `50%` of max speed during takeoff/landing

{% hint style="info" %}
It is recommended to always have a relatively high **Deceleration Smooth Factor** as sluggish deceleration can cause the feeling if sluggishness and cause nausea
{% endhint %}

* Desktop Keybinds
  * **Glide Take Off Key**: The takeoff/landing key allowing desktop players to perform those actions
  * **Freeze In Place Key:** The hover key switching between hovering and gliding/flying states if hovering is allowed
  * **Force Respawn Key**: The respawn key which will stop the flight and teleport the player to the **Reset Position**&#x20;
* Initial State
  * **Flight Restricted**: Specifies whether the flight system is turned off by default as if the player was in a restricted zone. You can send an `AllowFlight` event to the system to enable it
  * **Reset Position**: Determines the target to which the player will be teleported when pressing the **Force Respawn Key**
* Callbacks
  * **On Flight Start**: Sends the specified event to the provided UdonBehaviour when the player engages the flight system
  * **On Flight End**: Sends the specified event to the provided UdonBehaviour when the player lands
  * **On Hover Start**: Sends the specified event to the provided UdonBehaviour when desktop hover is started by pressing the **Freeze In Place Key** or when the player spreads their arms to stop in VR
  * **On Hover End**: Sends the specified event to the provided UdonBehaviour when desktop hover is ended by pressing the **Freeze In Place Key** again or when the player brings their arms closer together
  * **On Glide Start**: Sends the specified event to the provided UdonBehaviour when the player enters glide
  * **On Glide End**: Sends a the specified event to the provided UdonBehaviour when the player exists glide (either by taking off or landing)

{% hint style="success" %}
There is an included `FlightSystemDebugger` behaviour with the logging for all the Flight System Callbacks enabled so you can quickly test your environment and when each system engages/disengages
{% endhint %}

{% hint style="warning" %}
**VR currently has a very rudimentary hover mechanic.** I would encourage you to create some extra system for VR specific hover toggle, like maybe a couple interacts that appear if the player slows down, which they can click to switch into a hover mode. That will make the hover / flight switch more intentional and less spontaneous. I might add an example of this at some point in the future
{% endhint %}

### Events

* **Boost**: Raises the **Max Speed** to the **Boost Speed** for **Boost Length** seconds
* **RestrictFlight**: Prohibits players from flying, even if the player is already mid-flight (will make them fall)
* **AllowFlight**: Allows players to fly
* **DisableBoosting**: Disables the boosting behaviour, calling `Boost` after boosting has been disabled will not do anything. This does not affect players that are already boosting. They will stop boosting as their boost timer runs out.
* **EnableBoosting**: Enables the boosting behaviour
* **Stop Boost**: Stops currently active boost
* **DisableGliding**: Disables gliding, players will simply land and take off while close to the ground. The takeoff/landing speed helpers will still work as usual. This will interrupt current glide
* **EnableGliding**: Enables gliding
* **StopGliding:** Stops current glide and grounds the player
* **EnableOneHandedMode**: Enables One Handed Mode for flight controls
* **SetOneHandedTargetHead**: Sets One Handed Mode tracking target to player's Head
* **SetOneHandedTargetLeftHand**: Sets One Handed Mode tracking target to player's Left Hand
* **SetOneHandedTargetRightHand**: Sets One Handed Mode tracking target to player's Right Hand
* **DisableOneHandedMode**: Disables One Handed Mode for flight controls and returns to the default two-handed flight

{% hint style="info" %}
If you are planning to build upon the logic exposed by the Flight System, I would also encourage you to check out the code! Almost every single line is commented, with motivation / explanation which should help you better understand how it all comes together
{% endhint %}

## Demo

**Check it out in my** [**Catch A Flight world**](https://vrchat.com/home/launch?worldId=wrld_65fd7c51-89f7-4c7b-8a4f-2620a16f0a75)**!**

{% hint style="success" %}
A more developer-oriented test world with more info is[ also available here](https://vrchat.com/home/launch?worldId=wrld_842e079c-8ad3-4c99-afa2-ac6afaf78176). The same world is also provided as a demo scene in the `UdonToolkit/Demo/Flight System Demo` scene. Don't hesitate to take a look!
{% endhint %}

## Feedback

{% hint style="info" %}
If you have any suggestions for the FlightSystem or encounter any issues, please do not hesitate to [file an issue on Github](https://github.com/orels1/UdonToolkit/issues/new) or reach out[ in my Discord](https://discord.com/invite/fR869XP), as well as [support the Toolkit on Patreon](https://www.patreon.com/orels1)
{% endhint %}


# Presentation System

A turn-key solution for Virtual Conferences

{% hint style="info" %}
UdonToolkit's Presentation system provides a fully-featured presentation solutions for creating VR Talks, Conferences, or just showing some slides to someone.
{% endhint %}

The system was originally built for the [Prefabs TLX](https://tlx.dev) World Developer conference. You can see it in action in one of the [many talks](https://tlx.dev/talks).

## Features

* A solution to display your presentations with different slide durations, animations and videos
* Slide controls and counters
* Time remaining indicator
* Voice override for the speaker w/ support for extra override zones, e.g., for Q\&A sessions or multiple speakers
* Material swap/object toggles for talk star/end
* Full late-joiner support
* Camera system for showing the speaker
* Stream system for capturing the presentation with talk info and speaker camera overlays
* Personal screen pickup for people to take a closer look
* Laser pointer for the speaker
* Callbacks for all major system events for easier extensibility with your own logic

## Requirements

* TextMeshPro (from Package Manager)
* Cinemachine (from Package Manager)
* UdonSharp v0.19.2 or later
* VRChat SDK 2021.01.28.19.07 or later

## Setup

{% embed url="<https://youtu.be/n_UyLL0rPG0>" %}

* Drag & Drop  the Presentation System prefab from `UdonToolkit/Systems/Presentation System`
* Make sure its in a 0,0,0 spot of the world, its important for some of the tracking objects
* Right click the prefab in the hierarchy and select "Unpack Prefab Completely (Udon does not work well with prefabs)
* Expand the Scene Objects in the hierarchy and move the following objects where you need them
  * **Screen**: the presentation screen. Will display the current slide when the presentation is active
  * **Presentation UI**: the speaker's and host's user interface, place it where your speakers will be talking from
  * **Laser Pointer**: a simple laser pointer pickup to help with explaining the slides content, put near the Presentation UI for ease of use
  * **Presentation Audio Source**: this source will play the presentation audio if it has any. It is a 3D source by default, and is best placed above and in front of where your audience will be sitting. You can make it 2D if you want
  * **Streamer Spot**: a special box where the stream capture user will be teleported when pulling up the stream overlay. Best placed below the stage, slightly behind where the speaker is standing. More on that in the Setting Up Capture section
  * **CM Speaker Cam**: a Cinemachine camera object that captures the speaker. Place somewhere in front of where the speaker will be standing. It will automatically track their head
* Build a test version
* Click Take Control on the UI and try starting a presentation!

## UI Controls

* **Take Control**: takes ownership of the presentation system, shows the UI and removes any currently active voice boosts
* **< and >**: switch between the presentations in the presentations list, does not load the presentation unless Select is pressed
* **Select**: loads the selected presentation
* **Start Talk**: starts the presentation and boosts the speakers's voice ([see below for voice settings](/systems/presentation-system#adjusting-voice-settings))
* **Stop**: stops the presentation and removes the voice boost
* **Next Slide**: switches to the next slide
* **Prev Slide**: switches to the previous slide
* **Play Video**: unpauses an animated or video slide (one that has `Auto` checked, [see below](/systems/presentation-system#adding-your-presentations))
* **Pause**: pauses an animated or video slide
* **Height Adjust + and -**: moves the UI up and down for ease of use

### Personal Screen Controls

* **Desktop**
  * Press `l` to spawn a personal screen in front of you
* **VR**
  * Press both triggers to spawn a personal screen pick up

This screen contains the presentation slides, the view of the speaker and the presentation info. This is helpful for getting a better look at the slides with small text or when generally being far away from the presentation

### Stream Controls

Press `k` to pull up the stream and be teleported to the configured stream spot position. This will override your view with a special overlay that captures the current speaker and the slides.

{% hint style="warning" %}
Before using Stream Overlay - you need to configure the list of users who are allowed to open it up in the `Presentation Stream Controller`
{% endhint %}

## Configuration

### Creating Presentation Videos

{% hint style="info" %}
Before adding presentations, we need to prepare them so they can be dynamically loaded in VRChat.
{% endhint %}

Most of the presentation software allows you to export a presentation as a video file (like an .mp4 video), exceptions are Web-Based solutions, like Google Slides, but you can still export them as PowerPoint presentations and use it to generate a video.

**Presentation videos work best when they:**

* Are exported with a fixed slide time of 3 seconds
* Have maximum of one video per slide
* Only have one animation sequence per slide
  * e.g., if you have a slide where you want to animate things piece by piece - split them into different. Otherwise - they will all be played in a sequence without pauses, which might be undesirable

When you have your presentation files - upload them to YouTube or Vimeo and set them to be accessible via a link. If you are going to use Vimeo - please use their direct links to avoid any issues

### Adding Presentations

* Expand the hierarchy of the Presentation Player object
* Click on the provided example presentation to see its Udon Behaviour
* The core of the presentation object consists of the 5 main elements
  1. **Talk Url**: the url of the video file with the presentation, YouTube or Vimeo direct link works best
  2. **Talk Title**: the title of the presentation that will be used when selecting a talk, and also displayed for the stream capture
  3. **Talk Author**: the name of the speaker, will also be shown on the stream capture
  4. **Talk Length**: this is only displayed to the speaker to help manage time
  5. **Slides**: a list of slide durations that allow the system to switch between slides

So, to add your own presentations - you need to

* Fill out the fields
* Expand the Configuration Wizard dropdown
* Set the Slide Count to the amount of slides in your presentation
* Set the Slide Duration to the time per slide you set when exporting the video
* Click Configure Talk
* Duplicate the object and repeat for each presentation

When you're done creating individual presentation objects - do the following

* Click on the Presentation Player
* Click Populate Talks button
* This should fill the Talks List with all of your talk objects (they must be parented to the Presentation Player)
* You can expand the Talks list by clicking on the dropdown to see all of your talks. You can move them around to change their order in the presentation UI

{% hint style="success" %}
If you do not have any videos or animations - you're done! You should be able to make a test build and try out your presentations in game!

If you **DO** have animations - look below
{% endhint %}

#### If you have videos or animations on your slides

1. Expand the slides dropdown on the individual presentation and check Auto checkbox near all slides that have animations or videos.
2. Edit the Slide Durations for those slides as follows:

* **For videos**
  * PowerPoint will export slides with videos exactly the matching the length of the videos, so just set those slide durations to the lengths of the included videos
* **For animations**
  * This one is a bit more manual. Open the video you exported and see how much time the slide takes with all the animations included. Most time 3 seconds will be just enough. Otherwise - set the Slide Duration of that slide to the appropriate amount

### Customizing the Screen

{% hint style="info" %}
The Presentation System allows you perform a couple of actions when the presentation starts and ends. This is also where you can adjust the default placeholder material for the screen
{% endhint %}

**Presentation Screen Controller** allows you to swap materials on your screen mesh (or any other mesh) when the talk starts and ends

![](/files/-MU1YngSC1VjsIr5NfHn)

If you don't want to swap any materials - uncheck the `Swap Materials` option.

You can configure `Presentation Placeholder` material however you like, or you can swap it to a different material entirely: just drag the new material onto your screen and into the `Default Mats` column for the `Screen` mesh in the list - and you're done!

You can also toggle some objects on and off when the presentation Starts or Ends. For example, you can disable your screen object, and drop it into the `OnTalkStart` and `OnTalkEnd` lists to be enabled and disabled respectively. This will make it so the screen with the slides is only visible when the presentation is going. That would look something like this

![](/files/-MU1ZEbiAGsdbdpEMM7w)

{% hint style="warning" %}
If you do not want any objects to toggle - you can uncheck `Toggle Objects`, note that it will also stop the Speaker Camera from being turned on when the presentation starts starts.
{% endhint %}

### Adjusting Voice Settings

{% hint style="info" %}
By default the Presentation System will boost the voice range of the speaker up to 250 units of distance. It will only boost the person who is currently in control of the presentation UI and clicked the `Start` button.
{% endhint %}

![](/files/-MU1agVj951NeJ-TzvN9)

Most of the configuration is meant to be done by simply sliding the Range Boost slider left and right. You can also use the Gain Tweak parameter to directly affect the volume boost without affecting range, but that can make the speaker too loud when up close, so be careful with it.

{% hint style="success" %}
You can restrict the voice boost only to a set of particular players by clicking `Restrict Access` button and adding the player names to the `Allowed Users` list.
{% endhint %}

Another way of boosting someone's voice is by using `Presentation Voice Zones`, which will boost the volume of anyone who enters its trigger collider. One zone is provided as an example inside `Scene Objects`. It is disabled by default and will not affect anything unless enabled. You also need to check the `Use Zones` checkbox on the `Presentation Voice Controller` for zones to work.

A good use case for zones might be a Q\&A spot where people can come up to and ask their questions to the speaker. That way everyone in the room can hear them

## Stream and Video Capture

{% hint style="info" %}
A big part of creating a presentation is providing a good recording / stream for the people who cannot attend the event personally. UdonToolkit's presentaiton system is fully outfitted to handle that with a Stream/Capture overlay system
{% endhint %}

The system works as follows: when a users presses the stream overlay key, provided they are in the whitelist, they will be teleported to a special streamer spot and get their desktop view overtaken by a special stream overlay. They can press the hotkey again to exit that view.

Default hotkey: `k`

### Configuration

* Move the Streamer Spot object to be positioned below and behind the speaker in such a way that the Streamer Spot Spawn is orientated towards the speaker to provide the best audio capture experience
* Put the Presentation Stream Controller somewhere far from your main world area, by default it is put all the way at x: 1000 y: 1000 to not interfere with the main location
* Inside of the `Presentation Stream Controller` configure the following parameters
  * Set the Reset Position to your world spawn point, the user will be teleported there when exiting the stream view
  * Set the Engage Key to whatever you like
  * Populated the Allowed Users list with the usernames of people who should be able to enable stream overlay, or uncheck Restrict Access to allow everyone to do that (not recommended)
* Inside of the Presentation Stream Controller - find the Stream object and customize the stream overlay UI to your liking

{% hint style="success" %}
For the best viewing experience - record the video with the audio capture set to mono, as spatialization sounds very odd when re-watching the recordings
{% endhint %}

## Full Reference

{% hint style="info" %}
The Configuration guide above includes the core information that should let you configure everything and get started with the presentation system. But if you want to extend it or learn about every single exposed parameter, here's a full reference
{% endhint %}

### Presentation Player

*The top-level behaviour that controls the general flow and sends events to everything else*

#### Parameters

* **Talks**: a list of `TLXTalk` objects that contain the individual talks data
* **Main Player**: the `PresentationVideoPlayer` object that will be active for everyone in the room, responsible for loading and displaying the slides
* **Lookahead Player**: the `PresentationVideoPlayer` object that is only active for the current speaker and is taking care of the Next Slide preview
* **Restrict Access**: specifies whether only particular users will be able to control the Presentation Player
* **Allowed Users**: Contains the list of users that can take control of the Presentation Player. Only visible when Restrict Access is checked, does not have any effect otherwise
* **Callbacks**: a set of callbacks that will be called when a particular event is handled by a Presentation Player
  * **OnTakenControl**: will fire only for the user that just took control of the Presentation Player, you can set the `Target` to `All` to fire the event on all the clients
  * **OnLostControl**: will fire for every user in the instance when someone takes control of the Presentation Player (does not fire on the new owner of the Presentation Player)
  * **OnTalkStart**: will fire for every user in the instance when the presentation starts
  * **OnTalkEnd**: will fire for every user in the instance when the presentation ends
  * **OnNextSlide**: will fire for the current owner of the Presentation Player when they click Next Slide
  * **OnPrevSlide**: will fire for the current owner of the Presentation Player when they click Prev Slide

{% hint style="danger" %}
Callbacks allow you to specify a network target: **Local**, **Owner** or **All**. If the event can be seen by everyone (based on the description above) you **DO NOT** want to use **All**, as that will cause the event to oversync and fire an extra time for each single user in the instance.

So in this case you only want to use **All** on events that only fire for the owner of the Presentation Player. Others should only use **Owner** or **Local**
{% endhint %}

#### Events

* **TakeControl**: takes ownership of the Presentation Player, fires OnTakenControl for the initiator and OnLostControl for everyone else
* **StartTalk**: starts the currently selected presentation, fires OnTalkStart for everyone
* **StopTalk**: stops the currently active presentation, fires OnTalkEnd for everyone
* **PrevSlide**: goes back a slide, fires OnPrevSlide for the current owner
* **NextSlide**: goes forward a slide, fires OnNextSlide for the current owner
* **NextTalk**: selects next presentation from the Talks list and loads it up
* **PrevTalk**: selects previous presentation from the Talks list and loads it up
* **PauseTalkVideo**: pauses an autoplaying presentation slide for everyone
* **PlayTalkVideo**: resumes the autoplaying presentation slide for everyone
* **SelectTalk**(int newIndex): selects the presentation with the specified index and loads it up

### Presentation Talk

*The presentation description class, contains the information about a single presentation and the list of slides*

#### Parameters

* **Talk Url**: the url of the presentation video file, you generally want this to point to a YouTube or Vimeo url
* **Talk Title**: the title of the presentation, will be used in the talk selector UI and the stream capture overlay
* **Talk Author**: the name of the speaker, will be used in the stream capture overlay
* **Talk Length**: the length of the presentation in minutes, will only be used for a timer that is shown to the speaker to help them manage the presentation time
* **Slides**: the list of the presentation slides, internally consists of two arrays
  * **Slide Duration**: the length of a specific slide, if you have a video or an animation within a slide - you will most likely need to adjust this number
  * **Auto**: whether the slide should be autoplayed, this should be checked for all the slides that contain videos or animations

{% hint style="info" %}
When using Vimeo for video hosting - it is recommended to use direct file links in Talk Url. You can also use your own hosting for the video files and use the direct links inside of the Talk URL. Be weary that event attendees will need to click **Allow Untrusted URLs** in VRChat settings.
{% endhint %}

#### Events

{% hint style="danger" %}
`Presentation Talk` is not built to handle incoming events from anything but the Presentation Player, these are provided solely for reference
{% endhint %}

* **Prepare(int slideIndex)**: loads up the presentation video in both main and lookahead players and seeks them to the provided slide index
* **Play**: plays the first slide of the presentation
* **SeekToSlide(int slideIndex)**: seeks main and lookahead players to the provided slide inde
* **Stop**: stops both main and lookahead players
* **NextSlide**: goes forward a slide
* **PrevSlide**: goes back a slide
* **PlayContinuous**: resumes autoplaying slide playback
* **PauseContinuous**: pauses autoplaying slide playback

### Presentation UI Controller

*Drives the Presentation UI functionality*

#### Parameters

* **Presentation Player**: the reference to the main Presentation Player behaviour
* **Presentation UI**: the Presentation UI root Canvas game object
* **Owner Objects**: these objects will be enabled for the new owner when they Take Control of the Presentation Player
* **Public Objects**: these objects will shown to everyone but the owner of the Presentation Player
* **Owner Collider**: the collider that covers the whole of the Presentation UI canvas to provide proper UI detection
* **Public Collider**: the collider that covers only the Take Control button to minimize the player's laser showing up randomly when they're watching the talk
* **Talk Selector Title**: the TextMeshPro component which displays the presentation title in the Presentation Selector on top of the Presentaiton UI
* **Slide Index Text**: the TextMeshPro component which displays the current slide index to the speaker
* **Time Left Text**: the TextMeshPro component which displays the remaining presentation time to the speaker

#### Events

* **SelectorPrev**: selects the previous presentation in the selector and displays its title to the current owner
* **SelectorNext**: selects the next presentation in the selector and displays its title to the current owner
* **SelectorConfirm**: confirms the presentation selection and tells Presentation Player to load it
* **HandleTakeControl**: enables the Owner Objects and disables the Public Objects, meant to be used in the OnTakenControl callback of the Presentation Player
* **HandleLoseControl**: disables the Owner Objects and enables the Public Objects, meant to be used in the OnLostControl callback of the Presentation Player
* **LowerUi**: lowers the Presentaiton UI panel by 0.05, only allows to go down by 2 units maximum
* **RaiseUi**: raises the Presentation UI panel by 0.05, only allows to go up by 2 units maximum
* **HandleTalkStart**: sets the slide index in the Presentation UI and starts the presentation timer, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: resets the slide index and the presentation timer in the Presentation UI, meant to be used in the OnTalkEnd callback of the Presentation Player
* **HandleNextSlide**: sets the slide index in the Presentation UI to the next slide, meant to be used in the OnNextSlide callback of the Presentation Player
* **HandlePrevSlide**: sets the slide index in the Presentation UI to the previous slide, meant to be used in the OnPrevSlide callback of the Presentation Player

{% hint style="info" %}
As you can see, events that start with `Handle` usually meant to be used inside of a Presentation Player callback, and that is the general naming scheme I'm trying to establish
{% endhint %}

### Presentation Screen Controller

*Reacts to the Presentation Start/End events to swap screen materials or toggle game objects*

#### Parameters

* **Swap Materials**: specifies whether to swap materials inside Material Swap Targets
* **Material Swap Targets**: defines a list of meshes which will have their materials swapped when the Presentation Starts and Ends
  * **Meshes**: meshes to swap materials on
  * **Default Mats**: materials to apply when the presentation is stopped
  * **Active Mats**: materials to apply when the presentation starts
* **Toggle Objects**: specifies whether to toggle game objects on presentation start / end
* **OnTalkStart**: list of objects to be affected when the presentation starts
* **OnTalkEnd**: list of objects to be affected when the presentation ends

#### Events

* **HandleTalkStart**: performs the selected actions in the Behaviour parameters, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: performs the selected actions in the Behaviour parameters, meant to be used in the OnTalkEnd callback of the Presentation Player

### Presentation Voice Controller

*Applies voice adjustments to the players*

#### Parameters

* **Restrict Access**: specifies whether only particular users will be able to receive a voice boost
* **Allowed Users**: contains the list of users that can receive the voice boost. Only visible when Restrict Access is checked, does not have any effect otherwise
* **Range Boost**: sets the amount of extra range added to the player's voice
* **Gain Tweak**: sets the amount of gain adjustment for the player's voice. Be careful not to overuse this setting, it can make players really loud up close
* **Use Zones**: allows usage of the `Presentation Voice Zone` behaviours. They will boost the volume of anyone who enters their trigger area
* **Adjust On Presentation Start**: adjusts the voice of the speaker when they start the presentation, and removes the voice boost when they stop or lose ownership of the Presentation Player

#### Events

* **HandleTalkStart**: boosts the current speaker's voice if the Adjust On Presentation Start is checked, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: removes the current's speaker's voice boost if the Adjust On Presentation Start is checked, meant to be used in the OnTalkEnd callback of the Presentation Player
* **NormalizeVolume**: same as HandleTalkEnd, but is meant to be used in the OnTakenControl callback of the Presentation Player
* **EnterZone(VRCPlayerApi player)**: boosts the voice of the provided player if Use Zones is checked
* **ExitZone(VRCPlayerApi player)**: removes the voice boost of the provided player if Use Zones is checked

### Presentation voice Zone

*Detects players entering / exiting the zone and calls voice adjustment events on the `PresentationVoiceController`*

{% hint style="warning" %}
This behaviour requires a trigger collider, and is recommended to be put on a `MirrorReflection` layer. It will warn you about those things, so you don't miss it
{% endhint %}

#### Parameters

* Presentation Voice Controller: a reference to the `PresentationVoiceController` in your scene

### Presentation Stream Controller

*Provides functionality for capturing the presentation*

#### Parameters

* **Restrict Access**: specifies whether only particular users will be able to toggle the stream overlay
* **Allowed Users**: contains the list of users that can open the stream overlay. Only visible when Restrict Access is checked, does not have any effect otherwise
* **Engage Key**: defines the hotkey for opening the stream overlay
* **Stream Objects**: these objects will be toggled when users presses the Engage Key
* **Stream Camera**: the camera that captures the stream UI
* **Stream Animator**: the stream UI animator which controls Idle/Active states
* **Stream Spot**: the position to which the capture player will be teleported to provide best audio capture position
* **Reset Position**: the position to which the capture player will be teleported when they click the Engage Key again - closing the stream overlay
* **Talk Title**: the UI text element with the presentation title on the stream overlay
* **Talk Author**: the UI text element with the presentation author on the stream overlay

#### Events

* **HandleTalkStart**: sets the presentation title and author, and sets the stream UI animator params, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: sets the stream UI animator params, meant to be used in the OnTalkEnd callback of the Presentation Player
* **ToggleStreamObjects**: toggles the objects specified in the Stream Objects list and teleports the local player to the Stream Spot

### Presentation Personal Screen

*Controls the spawnable screen attendees can use to get a better view of the presentation slides and the speaker*

#### Parameters

* **Screen Objects**: a list of objects to be enabled or disabled when user executes the personal screen hotkey / toggle
* **Main Screen**: the personal screen object which will be moved to the Desktop / VR Spawn Ref defined below
* **Stream Controller**: the Presentation Stream Controller that ensures that the correct animator state is set (personal screen shares the same UI capture as the stream overlay)
* **Screen Collider**: the collider on the personal screen, it will get disabled if the user isn't in VR, as it is not a pickup unless the player is in VR
* **Desktop Button**: defines the desktop hotkey used to spawn the personal screen
* **Desktop Spawn Ref**: the position at which to spawn the personal screen on desktop. Should be attached to a player-tracked object somewhere in front of the player so they can easily see it
* **VR Spawn Ref**: the position at which to spawn the personal screen in VR. Should be attached to a player-tracker object somewhere in front of the player so they can easily pick it up

### Laser Pointer

*Provides a basic laser pointing functionality that will project a laser dot onto a surface*

#### Parameters

* **Laser Line**: the liner renderer to be used for the laser line
* **Laser Dot**: the object to be placed on the surface of the hit object
* **Active**: whether the laser pointer is enabled (mostly used internally)
* **Hit Layers**: the list of layers to be considered for collision

{% hint style="danger" %}
The following components are purely internal and are not meant to be modified by the user
{% endhint %}

### Presentation Video Player

*Controls the `VRCUnityVideoPlayer`*

#### Parameters

* V Player: the VRCUnityVideoPlayer to control
* Curr Url: the currently loaded video url

#### Events

* **LoadAndSeek(float time)**: loads the video on the Curr Url, retries if fails or gets rate limited, then seeks the video to the specified time

### Presentation Camera Target

*Tracks the current speaker's position to serve as a camera target*

#### Parameters

* Source: specifies which tracking source to use

#### Events

* **TakeOwnership**: takes ownership of the camera target, the owner will then be used as a tracking target
* **HandleTalkStart**: sets the target of the PersentationCameraTarget to the current owner and enables tracking, meant to used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: stops the tracking, meant to be used in the OnTalkEnd callback of the Presentation Player


# Attributes Overview

A collection of attributes to create custom UIs for your own behaviours

{% hint style="info" %}
Udon Toolkit is a system of C# attributes which can be utilized to build highly customizable and responsive Inspectors for your behaviours, providing better user/dev experience to anyone using your code
{% endhint %}

Not sure why do you need better inspectors? Check out my TLX Talk!

{% embed url="<https://www.youtube.com/watch?v=R0oYziYlVKg>" %}

If you do not know what C# attributes are and how to use them in your Unity code, [this might be a good guide to start with.](https://www.youtube.com/watch?v=9udeBeQiZSc)

When you're feeling like you are ready to use them, [check out the attributes list](/attributes/attributes-list)


# Attributes List

A collection of attributes to create custom UIs for your own behaviours

## Class Attributes

The following attributes are only available to use with your behaviour classes.

### CustomName

`[CustomName(string name)]`

Sets a custom name shown in the header bar, otherwise the header bar is hidden.

```csharp
[CustomName("My Fancy Controller")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {}
```

![](https://cdn.vrchat.sh/ut/Custom%20Name.png)

### HelpMessage

`[HelpMessage(string message)]`

Displays a box with some text below the controller's header bar and above the rest of the UI.\
Usually used to describe the purpose of the behaviour if it is not obvious, or to warn user about anything in particular.

```csharp
[HelpMessage("This behaviour should be enabled at all times")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {}
```

![](https://cdn.vrchat.sh/ut/Help%20Message.png)

### OnBeforeEditor

`[OnBeforeEditor(string methodName)]`

Calls the specified method every editor update loop before all the editor code was executed. The `SerializedObject` is passed to your method as a parameter which you can use to perform any necessary modifications.

```csharp
[OnBeforeEditor("BeforeEditor")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {
  public void BeforeEditor(SerializedObject obj) {
    // do stuff
  }
}
```

### OnAfterEditor

`[OnAfterEditor(string methodName)]`

Calls the specified method every editor update loop after all the editor code was executed. This will contain all the latest values before they are saved into the object. The `SerializedObject` is passed to your method as a parameter which you can use to perform any necessary modifications.

```csharp
[OnAfterEditor("AfterEditor")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {
  public void AfterEditor(SerializedObject obj) {
    // do stuff
  }
}
```

### OnValuesChanged

`[OnValuesChanged(string methodName)]`

Calls the specified method whenever any value has been changed by the user in the inspector. The `SerializedObject` is passed to your method as a parameter which you can use to perform any necessary modifications.

> If you need to react to a specific value change - use a field-level [`OnValueChanged` attribute](/attributes/attributes-list#onvaluechanged)

```csharp
[OnValuesChanged("ValuesChangeHandler")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {
  public void ValuesChangeHandler(SerializedObject obj) {
    // do stuff
  }
}
```

## Field Attributes

These attributes are meant to go on public fields of your Controller. They serve as UI building blocks and provide an ability to react to value changes, for example if you would like to do something specific in the scene when user checks a checkbox.

### **Attribute Order**

Some attributes affect whether the field is displayed or not, for those - order of the attributes is important. As soon as the editor encounters an attribute that hides a field - the field will be hidden no matter what the consecutive attributes return. The order is from the topmost attribute to the one right before the `public` keyword.

```csharp
// this will hide the whole field based on the value of `someBool`
[SectionHeader("Other Test")]
[HideIf("@someBool")]
public float var;

public bool someBool;
```

Many of these attributes can be combined to form more elaborate UI systems, so something like this is completely normal

```csharp
[OnValueChanged("HandleChange"]
[HelpBox("Value cannot be negative", "CheckValueValidity")]
[HideIf("@!transition")]
[HideLabel]
public float duration;
```

### Common Parameters

Some attributes share parameter names. One of such parameters is `methodName`. In most cases you will be able to pass either a method, or a variable name to it (where it makes sense, there are exceptions, and they are mentioned separately).

```csharp
// Will execute `ShouldShowBox` to determine whether the box should be visible
[HelpBox("", "ShouldShowBox")];
public bool active;

// Must return `bool`
public bool ShouldShowBox() {
  return active;
}
```

To use a variable instead of a method - prefix the variable name with `@`, you can also invert the variable value by adding `!` after that.

```csharp
// Will show if active is `true`
[HelpBox("", "@active")];
public bool active;

// Will show if active is `false`
[HelpBox("", "@!active")];
public bool active;
```

This can be any public variable of a class, doesn't have to be specifically the same variable. You can use something like `[HideInInspector]` for storing the state of the box without displaying it in the editor.

### SectionHeader

`[SectionHeader(string title)]`

Displays a visual separator box with some text

![](/files/-MW6GPNy9cTGqc0jip_I)

```csharp
[SectionHeader("General")]
public Transform sourceTransform;
```

### HelpBox

`[HelpBox(string message, [string methodName])]`

Displays a box with a help message. You can conditionally hide and show the box based on a method or variable. See [Common Parameters](/attributes/attributes-list#common-parameters) for more info.

![](/files/-MW6GYHtcdWmJ_OXCYgB)

```csharp
// Always visible
[HelpBox("This option disables rotation transfer")]
public bool ignoreRotation;

// Only visible if provided variable is set to `true`
[HelpBox("This option disables rotation transfer", "@ignoreRotation")]
public bool ignoreRotation;

// Only visible if provided variable is set to `false`
[HelpBox("This option disables rotation transfer", "@!ignoreRotation")]
public bool ignoreRotation;

// Only visible if the provided method returns `true`
[HelpBox("This option disables rotation transfer", "ShowIgnoreHelp")]
public bool ignoreRotation;

public bool ShowIgnoreHelp() {
  return ignoreRotation;
}
```

### HideIf

`[HideIf(string methodName)]`

Hides the field based on the provided method or variable. See [Common Parameters](/attributes/attributes-list#common-parameters) for more info.

![](/files/-MW6Gn2DOLP6AooX-rca)

```csharp
public bool enableEffects = true;

// Hides the field if `enableEffects` is unchecked
[HideIf("@!enableEffects")]
public float effectsDuration;

public bool skipTransition;

// Hides the field if `skipTransition` is checked
[HideIf("This option disables rotation transfer", "@skipTransition")]
public float transitionDuration;

// Hides the fields if the provided method returns true
public bool enableEffects;
public float effectsDuration;

[HideIf("HideExtras")]
public bool extraOption1;

[HideIf("HideExtras")]
public bool extraOption2;

public bool HideExtras() {
  return enableEffects && effectsDuration > 0;
}
```

### HideLabel

`[HideLabel]`

Simply hides the field label and draws the value field only. Helpful in combinations with things like `Horizontal` attribute

![](/files/-MW6H6LSuRWHq4DIXpcK)

```csharp
// Will show the field only
[HideLabel]
public GameObject active;
```

### Tab Group

`[TabGroup(string name, [string variableName])]`

Creates a tab system which only displays properties assigned to a particular tab.

![](/files/-MW6NiqD0Jz0hDdE4Ipb)

{% hint style="warning" %}
All tabs will be displayed where the `[TabGroup]` will be first encountered. So if your first `[TabGroup]` is added to the 5th property of the object - the whole tab system will show up right above the 5th property. That is the current limitation of the system that might be adjusted at some point
{% endhint %}

```csharp
[TabGroup("Basics")]
public bool basicSettingA;

[TabGroup("Basics")]
public bool basicSettingB;

[TabGroup("Advanced")]
public float advancedSettingA;

[TabGroup("Advanced")]
public float advancedSettingB;

// you can have any amount of properties inside a tab
[TabGroup("Advanced")]
public Vector3 advancedSettingC;
```

Tabs support any kind of attributes within them, e.g. you can use a `[FoldoutGroup]` inside a tab

```csharp
[TabGroup("TabA")]
[FoldoutGroup("Stuff in Tab A")]
public bool vA;

[TabGroup("TabA")]
[FoldoutGroup("Stuff in Tab A")]
public bool vB;


[TabGroup("TabB")]
public float vC;
[TabGroup("TabB")]
public float vD;
```

You can also pass a `variableName` to the `[TabGroup]` to save the currently selected tab index to that variable. You only need to provide that option to the first instance of `TabGroup` in the file.

This is often used to create different "modes" for a behaviour which you can then use in the code.

![](/files/-MW6OwMrXMceQqKfd4Nc)

```csharp
[TabGroup("Collide With Objects", "collisionType")]
public LayerMask collideWith;

[TabGroup("Collide With Players")]
public bool collideWithLocalPlayers;
[TabGroup("Collide With Players")]
public bool collideWithRemotePlaers;

// We save the selected type into this variable to use in Udon code later
[HideInInspector]
public int collisionType;
```

### Foldout Group

`[FoldoutGroup(string name)]`

A general purpose foldout allowing you to nest properties inside of it

![](/files/-MW6TQQwj11vGhol9yrW)

```csharp
[FoldoutGroup("Foldout")]
public float floatInFoldout;

[FoldoutGroup("Foldout")]
public bool boolInFoldout;

// You can nest other layout elements inside the FoldoutGroup
[FoldoutGroup("Foldout")]
[Horizontal("Horizontal In Foldout", true)]
public GameObject objInFoldout;

[FoldoutGroup("Foldout")]
[Horizontal("Horizontal In Foldout", true)]
public float floatInHorizontalInFoldout;

[FoldoutGroup("Foldout")]
[ListView("ListViewInFodlout")]
public UdonSharpBehaviour[] foldoutTargets;

[FoldoutGroup("Foldout")]
[ListView("ListViewInFodlout")]
[Popup("behaviour", "@foldoutTargets")]
public string[] foldoutEvents;
```

### OnValueChanged

`[OnValueChanged(string methodName)]`

Calls provided method when the value of the variable changes, so you can react to it in the UI.\
Has some special behaviour when used together with [ListView](/attributes/attributes-list#listview).

{% hint style="warning" %}
Due to limitations of Udon - the use of **#if !UDONSHARP\_COMPILER && UNITY\_EDITOR** is required for the handler methods
{% endhint %}

For regular fields the method signature should look like `public void MethodName(SerializedProperty value)`

```csharp
// Will call the provided method with the fresh value every time it changes
[OnValueChanged("ToggleLights")]

// Incoming value will be the new value, while the current variable will not be updated yet.
// This allows you to compare old and new values.
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void ToggleLights(SerializedProperty value) {
  // cast to the expected type first
  var val = value?.boolValue;
  if (value) {
    // do something here
  } else {
    // do something here
  }
}
#endif
```

For array type fields the method signature is `public void MethodName(SerializedProperty value, int index)`

```csharp
[OnValueChanged("HandleArrayChange")]
public string[] namesList;

#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleArrayChange(SerializedProperty value, int index) {
  // if value is `null` - the value at `index` was removed
  if (value == null) {
    // handle removal
    return;
  }
  // if `index` is out of range of the current array - a new value was added
  if (index == namesList.length) {
    // handle addition of new value
    return;
  }
  // handle change of an existing value
}
#endif
```

When used with `ListView` the method signature should be different: `public void MethodName(object lValue, object rValue, int index)`, where `lValue` and `rValue` represent the left and right variable of the list view at the changed index.

You only need to attach `[OnValueChanged]` to the first instance of a particular `ListView`.

```csharp
[OnValueChanged("HandleChange")
[ListView("Events List")]
public UdonBehaviour[] udonTargets;

[ListView("Events List")]
public string[] udonEvents;

// Handle a change of row values
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleChange(SerializedProperty lValue, SerializedProperty rValue, int index) {
  // when the value is removed - both will be null
  if (lValue == null && rValue == null) {
    // handle the removal of a row at `index` here
    return;
  }
  // when the value is added - `index` will be out of range of the current list
  if (index == udonTargets.Length) {
    // handle addition of new row
    return;
  }
  var lCasted = (UdonBehaviour) lValue?.objectReferenceValue;
  var rCasted = rValue?.stringValue;
  // handle change of an existing values
}
#endif
```

If you wish to get a full value instead of just the changed elements in both Array types and the `ListView` powered blocks, you will need to use a different signature.

* For regular arrays: `public void MethodName(SerializedProperty[] value)`
* For `ListView` blocks: `public void MethodName(SerializedProperty[] lValue, SerializedProperty[] rValue)`

If you also want to update some other value on the same object inside the change handler function - you need to accept an incoming `SerializedObject` as well

* For regular arrays: `public void MethodName(SerializedObject obj, SerializedProperty[] value)`
* For `ListView` blocks: `public void MethodName(SeriazliedObject obj, SerializedProperty[] lValue, SerializedProperty[] rValue)`

You can then use it to update properties like `obj.FindProperty("someProp").floatValue = 0.01f`

```csharp
[OnValueChanged("HandleArrayChange")]
public string[] namesList;

// Log current array value as `Values: array[0], array[1]...`
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleArrayChange(SerializedProperty[] value) {
  var casted = value.ToList();
  // don't forget to grab the value of needed type or cast it from i.objectReferenceType as <yourTargetType>
  Debug.LogFormat("Values: {0}", string.Join(", ", casted.Select(i => i.floatValue).ToArray()));
}
#endif

// Works for `ListView too
[OnValueChanged("Hand`leChange")
[ListView("Events List")]
public UdonBehaviour[] udonTargets;

[ListView("Events List")]
public string[] udonEvents;

// Log current `ListView` value as `Event: udonEvents[i], Target udonTargets[i].name`
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleChange(SerializedProperty[] lValue, SerializedProperty[] rValue) {
  var cLeft = leftVal.ToList();
  var cRight = rightVal.ToList();
  var eventNames = new List<string>();
  for (int i = 0; i < cLeft.Count; i++) {
    var behName = cLeft[i].objectReferenceValue != null
      ? (cRight[i].objectReferenceValue as UdonBehaviour).name
      : "null";
    eventNames.Add($"Event: {cRight[i].stringValue}, Target: {behName}");
  }
  Debug.Log(string.Join("\n", eventNames));
}
#endif

[OnValueChanged("HandleSliderChange")] [RangeSlider(0, 2)]
public float someFloat = 0.2f;

public bool someBoolProp;

// Modify another property in response to a value change
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleSliderChange(SerializedObject obj, SerializedProperty value) {
  obj.FindProperty("someBoolProp").boolValue = value.floatValue > 1;
}
#endif
```

{% hint style="info" %}
Check CustomUISample.cs for live examples!
{% endhint %}

### Horizontal

`[Horizontal(string groupName, [bool showHeader])]`

Combines multiple fields into a horizontal group based on the provided name.\
You can define variables in any order, they can even have other variables between them. The fetching is done purely by the group name.

![](/files/-MW6JbQvqwUOUYbZK0kh)

You can also pass a `showHeader` bool to display the group name in the UI.

![](/files/-MW6JYVJPtYqPIdbVces)

```csharp
// When using object types its pretty common to use a `[HideLabel]` attribute to make the UI cleaner
[Horizontal("Group")]
public GameObject varA;

[Horizontal("Group")]
public string varB;

// You can have many groups
[Horizontal("OtherGroup")]
public int varC;

[Horizontal("OtherGroup")]
public int varD;

// Display the group name in the UI
[Horizontal("I'm a group!", true)]
public int varE;

[Horizontal("I'm a group!")]
public int varF;

// You can have more than 2 elements in a group!
[Horizontal("I'm a group!")]
public int varG;

```

### ListView

`[ListView(string name, [string addMethodName], [string addButtonText])]`

Combines arrays into a connected list of elements.

This is a cornerstone attribute of UdonToolkit. Due to dictionaries not being exposed in Udon we have to split things into separate arrays which makes navigating logically connected pieces of data very annoying, as well as forcing you to manually keep track of having enough elements in both arrays.

ListView covers that use case and provides some extras when combined with the [Popup attribute](/attributes/attributes-list#popup).

![](/files/-MW6KH8vAY0nQqy7s865)

```csharp
[ListView("Udon Events List")]
public UdonBehaviour[] udonTargets;

[ListView("Udon Events List")]
public string[] udonEvents;
```

You can provide a custom add method to have full control on how the arrays are populated. It is also a good way to create something in the scene, like an instance of a prefab or a basic GameObject and set it as the list value at the same time.

![](/files/-MW6KRnFMNS74nRz-tV0)

You only need to configure the extra parameters in the first instance of `ListView` for a particular group.

```csharp
// You can also customize the add button text via an extra argument
[ListView("Udon Events List", "AddEvent", "Add new Event")]
public UdonBehaviour[] udonTargets;

// No need to define extra configuration here, the first instance of `ListView` for that group name is going to be used
[ListView("Udon Events List")]
public string[] udonEvents;

// The addition is fully relegated to your custom method, you can do whatevery you find suitable in here
public void AddEvent() {
  var newTargets = udonTargets.ToList();
  newTargets.Add(null);
  udonTargets = newTargets.ToArray();

  var newEvents = events.ToList();
  newEvents.Add($"NewEvent_{events.Length}");
  events = newEvents.ToArray();
}
```

Combining it with a `[Popup]` attribute is the way this is used the most across Udon Toolkit's behaviours. Creating list of Udon events and Animator triggers becomes a matter of a single line of code that handles everything behind the scenes.

![](/files/-MW6K_UYIEgDAwJLCyHO)

```csharp
[ListView("Udon Events List")]
public UdonBehaviour[] udonTargets;

// You can combine many attributes together, here we use `Popup` to automatically populate the events list for us
[ListView("Udon Events List")]
[Popup("behaviour", "@udonTargets", true)]
public string[] udonEvents;
```

You can read more about `[Popup]` and how it can be populated [right here](/attributes/attributes-list#popup)

### RangeSlider

`[RangeSlider(float min, float max)]`

Provides a slider to control the value in a predefined range, supports both floats and ints.

![](/files/-MW6KwsUVaYZDVT1SMr-)

```csharp
[RangeSlider(1, 10)]
public float floatValue;

// If the value is int - slider will auto snap to only int values
[RangeSlider(3, 20)
public int intValue;
```

### Popup

`[Popup(string methodName)]`

Shows a popup with options to choose from and support for multiple sources of data. Only supported on `string` and `int` variable types, as well as their array variants at this time.

![](/files/-MW6L_G54H5TRquMjDwy)

If used on an `int` or `int[]` it will save the index of the selected option in the list - into your target variable. So on the image above - selecting `fizz` will save `2` into `popupVar`

You can provide a method, or a variable, to populate the popup options list. See [Common Parameters](/attributes/attributes-list#common-parameters) for more info.

```csharp
// Use a variable to populate the popup options
[SectionHeader("Popups")] [Popup("@popupOptions")]
public string popupVar;

[NonSerialized] public string[] popupOptions = {"foo", "bar", "fizz", "buzz"};

// You can also use a method to calculate options dynamically
[SectionHeader("Popups")] [Popup("GetOptions")]
public string popupVar;

public string[] GetOptions() {
  return new [] { "foo", "bar", "fizz", "buzz" };
}

// The method can accept a serialized property if you want to dynamically generate the options
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public string[] GetOptions(SerializedProperty prop) {
  return new [] { $"options for {prop.name}" };
}
#endif
```

By providing an explicit `sourceType` other than `method` you can use the built-in Toolkit's ability to fetch a list of Animator Triggers, Udon Behaviour Custom Events or Shader Properties of a particular type. If there are no triggers or events available - the UI will inform you about it.

There are plans to add more sources in the future, like Material properties and other native elements.

You can combine this with [`[ListView]` attribute](/attributes/attributes-list#listview) to achieve dictionary-style results for Udon Behaviours. When used inside `ListView` - the popup method will be called with the `Serialized Property` of the current element on the opposing array (see example below).

![](/files/-MW6MSRG6torLvE8CFrI)

```csharp
// a namespace to work with Lists
#if !COMPILER_UDONSHARP && UNITY_EDITOR
using System.Collections.Generic;
#endif
// We use `[Horizontal]` to visually connect the source and the popup
[Horizontal("AnimationTrigger")] [HideLabel]
public Animator animator;

// Using an Animator source type to auto fetch Triggers from the `animator` variable
[Horizontal("AnimationTrigger")] [Popup("animator, "@animator", true)]
public string somePopupVar;

[Horizontal("BehaviourTrigger")] [HideLabel]
public UdonBehaviour behaviour;

// Using an UdonBehaviour source type to auto fetch Custom Events from the `behaviour` variable
[Horizontal("BehaviourTrigger")] [Popup("behaviour", "@behaviour", true)]
public string behaviourPopupVar;

[Horizontal("ShaderProperty")] [HideLabel]
public Shader shader;

// Using a Shader source type to auto fetch `float` Shader Properties from the `shader` variable
[Horizontal("ShaderProperty")] [Popup("shader", "@shader", true)]
public string shaderPopupVar;

// You can also specify which type of shader proeprty to grab
[Popup("shader", "@shader","vector", false)]
public string shaderPopupVectorVar;

[ListView("Test List")]
public Transform[] transforms;

[ListView("Test List")]
[Popup("GetChildrenOptions")]
public string[] selectedChildren;

#if !COMPILER_UDONSHARP && UNITY_EDITOR
// prop in this case is the element of `transforms` array on the current index
public string[] GetChildrenOptions(SerializedProperty prop) {
  var transObj = (Transform) prop.objectReferenceValue;
  if (transObj == null) {
    return new[] { "-- no transform set --"};
  }
  var childList = new List<string>();
  for (int i = 0; i < transObj.childCount; i++) {
    var child = transObj.GetChild(i);
    childList.Add(child.name);
  }

  return childList.ToArray();
}
#endif
```

### Toggle

`[Toggle([string label])]`

Toggle-type button for boolean fields.

![](/files/-MW6M_m5Ww6g6HM-6pgh)

Combines well with things like [`[HideIf]` attribute](/attributes/attributes-list#hideif) to toggle parts of the UI on and off to hide things that are unused in a particular toggle state.

```csharp
// Use the variable name as the toggle label
[Toggle]
public bool addSFX;

// Combine with [HideIf] to hide a variable that is irrelevant if the toggle is `false`
[HideIf("@!addSFX")]
public float someThirdVar;

// Use a custom toggle label
[Toggle("Custom Label")]
public bool extraToggle;
```

### Disabled

`[Disabled([string methodName])]`

Disables the editing of the provided field completely, or based on the provided `methodName`. Useful when manually populating fields via editor scripts (like with `OnValueChanged`) or in runtime.

When used with `ListView` it should be put on the first field in the list view.

```csharp
// Make a field read only
[Disabled]
public bool toggleThings;

// Make a field read only based on a method return value
[Disabled("GetDisabled")]
public float disabledFloat;

public bool GetDisabled() {
  return gameObject.activeSelf;
}

public bool allowEditing;

// Make a list view read only
[ListView("Stuff")][Disabled("@!allowEditing")]
public Transform[] objects;

[ListView("Stuff")]
public string[] objectDescriptions;
```

## Method Attributes

These attributes are meant to be added to your public methods and are handled separately. The UI for these is rendered after the main inspector is done, so they will always be at the bottom.

### Button

`[Button(string text)]`

Displays a button that calls the specified method in editor only. For example if you want to write some setup logic - you can use this attribute to provide a handy button for it.

![](/files/-MW6VkOFycdw1W0Ed3nV)

```csharp
[Button("Helper Button")]
public void CustomButton() {
  Debug.Log("Pressed the Helper Button");
}
```


# Creating Custom Attributes

Udon Toolkit can be extended with your own custom attributes! All you need is inherit from one of the classes provided below

### Example

Here is a sample of an attribute that achieves several goals

* Changes the background of the element to a yellow color
* Adds a header on top
* Adds a reset button on the bottom

The final look we want to achieve

![](/files/-MW72PbAEAhR5OYJrwp0)

Based on that - we need to do a couple of things

1. Create a new file for your attributes, let's say `MyCustomAttributes.cs` anywhere in your project
2. Add an `#if UNITY_EDITOR` at the beginning, followed by an empty line and an `#endif`
3. Between those you will create your new class Inheriting from the `UTPropertyAttribute`

```csharp
using UnityEditor;
using UnityEngine;
using System; // This in particular is very important
// As it will be used down th eline

#if UNITY_EDITOR
public class MyAttribute : UTPropertyAttribute {
}
#endif
```

Another  very important thing is that we need a separate version of the attribute for the VRChat's build system (otherwise we'll get build errors, and those aren't fun!)

1. Change an `#endif` to `#else`
2. Add a separate version of `MyAttribute` that inherits from `Attribute` (that is why we needed `using System;` earlier)

```csharp
using UnityEditor;
using UnityEngine;
using System; // This in particular is very important
// As it will be used down th eline

#if UNITY_EDITOR
public class MyAttribute : UTPropertyAttribute {
}
#else
// this just tells unity to use this attribute on fields
[AttributeUsage(AttributeTargets.Field)]
public class MyAttribute : Attribute {
  public MyAttribute() { // since we do not have any parameters
  // we create an empty constructor
  }
}
#endif
```

{% hint style="info" %}
The above process is required, because VRChat will need some version of the attribute to build the code. In this case we are using a dummy empty attribute as nothing we're doing here is important for the final build.

You can check the last third of the `UTAttributes` file to see how to handle attributes with parameters in a similar fashion
{% endhint %}

With that - let's add it to our behaviour code

1. Create a new UdonBehaviour and make a script
2. Inside of the script - make a new public variable, for example `public float someVariable`
3. Add the `MyAttribute` attribute to it

```csharp
public class CustomAttributeTest : UdonSharpBehaviour {
  [MyAttribute] public float someVariable;
  private void Start() {
  }
}
```

If you check the Behaviour now - nothing would change! It will just be a normal field.

Now we can override some methods to make it look different! Let's start with the things **above** or **before** the field. For this we'll use the `BeforeGUI` method.

1. Add a `public override void BeforeGUI(SerializedProperty property)` method
2. Inside of it - add our header label
3. Follow that with a new `GUI.color` assignment. Which will color everything that is rendered below it - with that color

```csharp
#if UNITY_EDITOR
public class MyAttribute : UTPropertyAttribute {
  public override void BeforeGUI(SerializedProperty property) {
    EditorGUILayout.LabelField("HEADER TEXT", EditorStyles.largeLabel);
    GUI.color = new Color(0.98f, 0.92f, 0.35f);
  }
}
#endif
```

You should now see the field with a header and colored with a shade of yellow!

![](/files/-MW7KaJZ11l_-JQtDJBM)

The only problem is - if you have any other fields bellow the `someVariable` they will also be colored in this yellow color, as it will override everything it encounters. Try it out: let's add another variable below the `someVaribale`.

```csharp
public class CustomAttributeTest : UdonSharpBehaviour {
  [MyAttribute] public float someVariable;
  
  public bool anotherVariable;
  
  private void Start() {
  }
}
```

![](/files/-MW7LQLUa8zEMvv9q8KT)

Now that's not really what we want. Let's fix that by adding another method override that would be called **below** or **after** the field. This one is called `AfterGUI`!

1. Add a `public override void AfterGUI(SerializedProperty property)` method
2. Reset the color back to white by assigning to the `GUI.color` again

```csharp
#if UNITY_EDITOR
public class MyAttribute : UTPropertyAttribute {
  public override void BeforeGUI(SerializedProperty property) {
    EditorGUILayout.LabelField("HEADER TEXT", EditorStyles.largeLabel);
    GUI.color = new Color(0.98f, 0.92f, 0.35f);
  }

  public override void AfterGUI(SerializedProperty property) {
    GUI.color = Color.white;
  }
}
#endif
```

Now its fixed and the 2nd property doesn't get colored.

![](/files/-MW7Luke8dLXOmHo4gPy)

The only thing left to do now is to add a button!

1. Add a `GUILayout.Button("Reset Value")` wrapped in an if
2. Reset the value of our property to `0` inside of that if

```csharp
#if UNITY_EDITOR
public class MyAttribute : UTPropertyAttribute {
  public override void BeforeGUI(SerializedProperty property) {
    EditorGUILayout.LabelField("HEADER TEXT", EditorStyles.largeLabel);
    GUI.color = new Color(0.98f, 0.92f, 0.35f);
  }

  public override void AfterGUI(SerializedProperty property) {
    GUI.color = Color.white;
    if (GUILayout.Button("Reset Value")) {
      property.floatValue = 0;
    }
  }
}
#endif
```

![](/files/-MW72l-nBamup7-i0hN0)

And we're done! Read below to see what else you can do by overriding methods like `OnGUI` and `GetVisible`

{% hint style="warning" %}
One important thing to note here! You might've noticed that we used a `property.floatValue` to reset our value. That will only work for... well... variables that are of `float` type. If you'll want to work with a `bool` or an `int` or some other property type - you'll need to check the documentation for `serializedProperty` [to learn how to access and change those!](https://docs.unity3d.com/2018.4/Documentation/ScriptReference/SerializedProperty.html)

For those who know about serialized properties - do not worry, you do not need to call `ApplyModifiedProperties` yourself - UdonToolkit's editor will do it for you!
{% endhint %}

{% hint style="info" %}
The final code is provided in the `Demo/CustomAttributeSample` folder, so feel free to use it as a starting point!
{% endhint %}

### UTPropertyAttribute

The base class for an attribute that would modify the display logic for a particular property.

It has a couple methods that you can override

#### GetVisible

`public virtual bool GetVisible(SerializedProperty property)`

This method determines if the property will be visible in the inspector. The attributes will be called from top to bottom, from left to right. As soon as at least one of the attributes returns `false` in the `GetVisible` method - the property won't be drawn and the code will proceed to the next one.

```csharp
// HideIf attribute implementation
public override bool GetVisible(SerializedProperty property) {
  isVisible = UTUtils.GetVisibleThroughAttribute(property, methodName, true);
  return isVisible;
}
```

#### BeforeGUI

`public virtual void BeforeGUI(SerializedProperty property)`

This method will be called right before drawing the field. It is useful for adding headers and other special info right above the actual field.

```csharp
// SectionHeader attribute implementation
public override void BeforeGUI(SerializedProperty property) {
  UTStyles.RenderSectionHeader(text);
}
```

#### OnGUI

`public virtual void OnGUI(SerializedProperty property)`

This method **replaces** the drawing of the serialized property with the logic provided within it.

{% hint style="info" %}
UT will always use the first provided OnGUI method. So if the property has multiple attributes that override OnGUI - only the first one will be called
{% endhint %}

```csharp
// Toggle attribute implementation
public override void OnGUI(SerializedProperty property) {
  var text = String.IsNullOrWhiteSpace(label) ? property.displayName : label;      
  property.boolValue = GUILayout.Toggle(property.boolValue, text, "Button");
}
```

#### AfterGUI

public virtual void AfterGUI(SerializedProperty property)

This method will be called right after drawing the field. You can add extra information, buttons or anything else that is related to the field above in here.

```csharp
// HelpBox attribute implementation
public override void AfterGUI(SerializedProperty property) {
  UTStyles.RenderNote(text);
}
```

### UTVisualAttribute

The base class for purely data-storing attributes that are generally used to alter general looks of the editor UI and not just the single field it is attached too. Things like `ListView` or `TabGroup` are inherited from `UTVisualAttribute`

While inheriting from this attribute does not do anything on its own - it is useful if you are planning to extend the `UTEditor` class itself.

Internally its just an empty C# attribute

```csharp
  /// <summary>
  /// These attributes are used to pass dat to custom logic in the UTEditor
  /// </summary>
  [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
  public class UTVisualAttribute : Attribute {
  }
```


# Guides Overview

Udon Toolkit might be a bit weird to use at first, so here are some examples of real-world things you might want to build with it.

The basic principle of most things in the Toolkit is that there are `Triggers` and `Actions`.\
`Triggers` react to player's input and call `Actions` to do things in the world, if you think about Toolkit in those terms - you should be up and running in notime!


# Mirror Toggle

An example of setting up a mirror toggle via UI Button

* Add a VRC Mirror prefab, disable it to save performance by default
* Add a `Universal Action` to the Mirror object
* Make sure `Active` is checked, check `Fire Object Toggles`, click on the `Game Objects List` and click `Add Element`
* Drag your Mirror object into the empty field, select `Toggle` in the dropdown on the right

![](/files/-MVMVuIuKJKGPNFStSeL)

* Add a `UI -> Button` to your scene, set the `Render Mode` to `World Space` and scale it way down and place it where you want
* Add a `Ui Shape` component to the Canvas
* Change canvas layer to `Default`
* Click on your Button inside the Canvas and add a new `On Click()` event by clicking the small `+` icon in the bottom right
* Drag and Drop your Mirror object into the empty field that appeared
* In the dropdown next to `Runtime Only` select `UdonBehaviour -> SendCustomEvent` (its all the way at the bottom)
* Type `Trigger` in the text field

![](/files/-MVMVzTvT8rtzp3V5sav)

You're done! Now you can toggle your mirror via UI button in Udon.

{% hint style="warning" %}
The major thing to note here is to make sure that the component, and the game object its on, is always enabled if you're using the `Delay` parameter, otherwise it will never fire as the Update method will never be called.
{% endhint %}


# Teleport Players

This guide describes 2 of the most common ways to teleport players

### Interact Teleport

*Teleport the player when they click on an object, e.g. a door knob*

* Add Sphere to your scene, this will act as your door knob
* Check the `Is Trigger` box on the Sphere Collider
* Add an Interact Trigger component to it and click "Convert to UdonBehaviour"

![](/files/-MVQJw24R0p0ZwrHchQ8)

* Create a new Empty GameObject and put it where you want your player to be teleported to
* Name it something nice, like "Room Teleport"
* Make sure you are in the Local view and rotate the object so the blue (Z) arrow is pointing where you want the player to look after the teleport

{% embed url="<https://gfycat.com/wigglycorruptgoral>" %}

* Add a Teleporter component to this new Game Object and click "Convert to Udon Behaviour"
* Click "Teleport Player" and drag and drop the GameObject itself into the Teleport Target field

{% embed url="<https://gfycat.com/poorsecondarybluebreastedkookaburra>" %}

* Select the Sphere you added originally
* Click on the "Udon Events List" foldout in the Interact Trigger
* Drag and Drop the "Room Teleport" object onto the "Udon Events List" foldout header&#x20;
* It should now say "Udon Events List \[1]" and you should have your Room Teleport there with a `Trigger` event selected to the right

{% embed url="<https://gfycat.com/maleinexperiencedbat>" %}

{% hint style="success" %}
That's it! Now when the player clicks on your sphere - they will get teleported!

Check out the [full documentation of the Teleporter](/behaviours/misc-behaviours#teleporter) to learn more about it
{% endhint %}

### Area Trigger Teleport

*Teleports the player when they enter a trigger*

* Add a new Empty Game object to the scene and position it roughly where you want your trigger to go, e.g. a portal entrance
* Name it something nice like "Portal Trigger"
* Click on the Layer dropdown on the top of the inspector and set it to MirrorReflection layer (to avoid issues with player's interaction laser)
* Add a Box Collider component to the Portal Trigger and check "Is Trigger" checkbox

![](/files/-MVQTqx3k43LYlWKlnfe)

* Set the collider size the way you need it
* Add an Area Trigger component and click "Convert To Udon Behaviour"
* Click "Collide with Players" and check "Collide with Local Players" checkbox

{% embed url="<https://gfycat.com/delectabletastyintermediateegret>" %}

Now for the teleport exit

* Add a new Empty GameObject and call it something nice, for example "Portal Exit"
* Add a Teleporter component to it and click Teleport Players
* Drag and drop the Portal Exit object itself into the Teleport Target field

{% embed url="<https://gfycat.com/weirdangryduckling>" %}

Now to connect it all

* Select your Portal Trigger and expand the Enter Events List foldout by clicking on it
* Drag and drop your Portal Exit object onto the Enter Events List foldout header
* It should appear in the list with `Trigger` selected as an event to send

{% embed url="<https://gfycat.com/impishhardtofinddiscus>" %}

{% hint style="success" %}
That's it! Now when the player enters the trigger - they will get teleported!

Check out the [full documentation of the Teleporter](/behaviours/misc-behaviours#teleporter) to learn more about it
{% endhint %}


# Migration to v1.0.0

### For Users

{% hint style="danger" %}
When updating to v1.0 from any v0.x.x release - you MUST follow these steps, or your projects will get compile errors
{% endhint %}

To upgrade, do the following

* Duplicate your main scene as a precaution (if you can)
* Make a new empty scene and open it
* Save the project
* Delete the UdonToolkit folder completely
* Follow the [Installation Instructions](/#installation)
* Open the duplicate scene and see if everything works
* If it does - feel free to go back to your main scene and delete the duplicate

If you do get any issues - please do not hesitate to [reach out on Discord](https://discord.com/invite/fR869XP)

If you'll want to roll back - you should be able to follow the instructions again and import the older version of the toolkit (latest v0.x.x release). That operation **should** work, but generally speaking only forward upgrades are supported.

### For Developers

You do not need to use `[UTEditor]` attribute anymore! You can safely remove it as it is not being used by anything. I left it in the code just so you do not get any errors. But feel free to remove it when you'll get a moment.

{% hint style="info" %}
v1.0+ handles the custom attribute UI a bit differently than before, check out the new [Custom Attribute](/attributes/creating-custom-attributes) docs to learn more about it!
{% endhint %}


# Migration to v0.4.0

{% hint style="warning" %}
Since version 0.4.0 UdonToolkit requires UdonSharp 0.18 and above!
{% endhint %}

### Migration Guide

* Grab the [latest U# release here](https://github.com/MerlinVR/UdonSharp/releases) and import that first!
* Create a new empty scene and open it
* Click File -> Save Project
* Download the [latest UdonToolkit release here](https://github.com/orels1/UdonToolkit/releases)
* Remove the UdonToolkit folder completely while you are in that test scene
* Import the new UdonToolkit and wait for everything to compile
* Open `Edit` -> `Project Settings` -> `UdonSharp` and in the `Default Behaviour Editor` select `UdonToolkit Editor`
* Open your scene again and everything should work as expected

{% hint style="warning" %}
If you are using the **CameraSystem** - it is recommended to check that the `Start Sphere` and all the `Sphere` objects inside `VR Controls` have `Area Trigger` program assets assigned, unity seems to occasionally lose references to that particular asset
{% endhint %}

You will notice a `Legacy` folder inside UdonToolkit now which hosts all the old controllers and the legacy editor

### Controller Migration Guide

{% hint style="info" %}
You can take a look at all the U# behaviours in the **Misc** folder to see how you can use UdonToolkit now
{% endhint %}

Starting with v0.4.0 you don't need controllers anymore, and you can use all the UdonToolkit's attributes directly in the U# code!

There are some small differences mainly centered around Udon's limited support for things like Enums and cases where you want to perform some complex editor logic.

> All variables that you want to use UdonToolkit attributes with must have a `[UTEditor]` attribute on them that is last in the group

```csharp
// This will work
[SectionHeader("General")] [UTEditor]
public bool active = true;

// This won't work
[SectionHeader("Some other group")]
public float test = 0.01f;
```

This is due to some limitations of unity PropertyDrawers that do not allow proper modifier stacking

{% hint style="info" %}
Popup attribute now has new udon-friendly versions that use string names instead of Enums
{% endhint %}

```csharp
// Using an Animator source type to auto fetch Triggers from the `animator` variable
[Horizontal("AnimationTrigger")] [Popup("animator, "@animator", true)] [UdonPublic]
public string somePopupVar;
```

More examples in the [Popup attribute documentation](https://github.com/orels1/UdonToolkit/wiki/Attributes#popup)

{% hint style="danger" %}
If you want to execute editor only code in case of OnValueChanged, etc. you need to use directives
{% endhint %}

It is pretty rare that you'll have to use `#if`, but sometimes its unavoidable, especially when you want to use things like [OnValueChanged](https://github.com/orels1/UdonToolkit/wiki/Attributes#onvaluechanged) attribute.

Basically the logic behind this is as follows: you should use `#if !COMPILER_UDONSHARP && UNITY_EDITOR` if:

* You want to work with methods and types not exposed to udon
* You want to execute some logic in editor outside of playmode

In other cases - you should never need `#if` around your attribute related code

```csharp
[OnValueChanged("ToggleLights")] [UTEditor]
public bool lightsActive;

#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void ToggleLights(SerializedProperty value) {
  // get the value of expected type first
  var val = value?.boolValue;
  if (value) {
    // do something here
  } else {
    // do something here
  }
}
#endif
```

As usual, check behaviours in the `Misc` folder as a reference to how things should be built. [UniversalAction](/behaviours/misc-behaviours#universal-action) is a good overall example.


# UT Controllers

Create custom behaviour UIs for your own code

UdonToolkit is not just a set of prebuilt behaviours, but also a UI system built using Unity's Custom Property Drawers and Attributes that allows you to quickly create a user-friendly UI.

{% hint style="danger" %}
As of UdonToolkit v0.4.0 (with UdonSharp v0.18.0+) the Controllers are **DEPRECATED** you can now use the Attributes directly in UdonSharp code!
{% endhint %}

#### Check the [Migration to v0.4.0 docs](/extras/migration-to-v0.4.0#migration-guide)

If you're not familiar with C# Attributes, I highly recommend [taking a look at this](https://docs.unity3d.com/Manual/Attributes.html).

But the tl;dr looks like this:

* Look through the list of available Attributes in the Sidebar
* Find something that seems interesting to you
* Add `using UdonToolkit` to the top of your U# behaviour code
* Add it to your field alongside a `[UTEditor]` attribute that should always be last in the list
* Enjoy the perks of UdonToolkit UI system!

[Full list of attributes is available here](https://github.com/orels1/UdonToolkit/wiki/Attributes)


# UdonToolkit v0.x

Purpose-built Udon Behaviours and tools to make your own

![](/files/-MQXLbgwmR6AspZPJYrD)

Udon Toolkit is a project aimed at simplifying usage of Udon by providing a set of easy to use purpose-built behaviours, from generic triggers and actions, to more complete systems like Key Items, Cabinet Drawers, etc.

This also includes a system of attributes that allow you to utilize the same UI elements when building your own behaviours!

If you find that something is broken or works not as you would expect - please ping me in the [discord server](https://discord.com/invite/fR869XP), or better - create a [new issue](https://github.com/orels1/UdonToolkit/issues/new).

## Requirements

* [VRC SDK3 with Udon](https://vrchat.com/home/download)
* [Udon Sharp Compiler](https://github.com/Merlin-san/UdonSharp) (v0.18+)
* \[Optional] Post Processing from Unity Package Manager **(required for the Camera System)**

## Installation

* Grab the latest package [from releases](https://github.com/orels1/UdonToolkit/releases)
* Import it into the project
  * If you're upgrading from version prior to 0.4.0 - you'll need to close and open the scene again after import
* Open your `Project Settings` -> `UdonSharp` and in the `Default Behaviour Editor` select `UdonToolkit Editor`

{% hint style="warning" %}
It is required to use the default path when importing as for now asset paths are hardcoded
{% endhint %}

## How to use

* Browse the list of available behaviours in [the behaviours section](/v0.x/behaviours/overview)
* Select a Game Object you want to use (or make a new one)
* Click `Add Component` and type the name of a Behaviour
* A new component with all the corresponding UI and parameters should appear
* If it is your first time using Udon Toolkit, you might want to click "Compile All Behaviours"&#x20;

{% hint style="info" %}
Tip: You can click on the `?` icon in the Behaviour header to open the docs for that particular script
{% endhint %}

## Known Issues

* Prefabs are fundamentally broken in Udon as of August 2020. Toolkit will show a warning if you will try to use a prefab, it is **highly recommended** to unpack the prefab before editing anything, as values can just reset to whatever they were before when entering play mode

{% hint style="success" %}
An alternative to that might be to utilize [Unity Presets](https://docs.unity3d.com/2018.4/Documentation/Manual/Presets.html) which allow you to save the values of the current behaviour into a file
{% endhint %}

## Advanced

### UI System

UdonToolkit is not just a set of prebuilt behaviours, but also a UI system built using Unity's Custom Property Drawers and Attributes that allows you to quickly create user-friendly UI. You can [read more here](/v0.x/attributes/attributes-overview).

## Thanks ❤

* To VRC Team for making Udon which actually made me learn C# and Unity Editor Tools development
* To [Merlin ](https://github.com/MerlinVR)for making [Udon Sharp](https://github.com/Merlin-san/UdonSharp) without which none of this would've been possible


# Behaviours Overview

General info on UdonToolkit behaviours

{% hint style="info" %}
Udon Toolkit provides a selection of Udon Behaviours for you to utilize as building blocks in the world
{% endhint %}

Select a category of behaviours in the list on the left

<div align="center"><img src="/files/-MQXVlCwm-bwDOoayssM" alt=""></div>

You'll see a list of all available behaviours in that category on the right

![](/files/-MQXWIom0uATmJfj2ziZ)

### How to read the docs

* **Parameters** list describes all the public variables and settings exposed to you, some of them are UI only, so if you're planning to modify them via other udon behaviours - make sure to check the actual `UdonBehaviour` component on the same object.
* **Events** list describes all the exposed custom events the component expects to receive. Most things that are expected to be "Triggered" in some way usually have a `Trigger` event exposed. The full list of events can also be seen at the bottom of the behaviour UI with a convenience button for you to use while testing in editor.
* **Usage / Examples** is a free-form section with some personal usage advice and other notes

### Overall approach

You can generally split the things below into `Triggers` and `Actions` (plus some extras), one group captures player interactions in some way - the other performs some actions in response.

Most of the time you would add a bunch of `Universal Action` behaviours to the scene and then trigger them from all the different trigger behaviours listed on this page. But this system can also easily interact with other things in your world outside of Udon Toolkit! So don't feel limited by the `Universal Action` and use the `Udon Events` list with other behaviours to mix and match stuff.

### Terms

* **Event** means Udon Custom Event
* **To Trigger** means to send a Custom Event or specifically send a `Trigger` custom event
* **Action** means some kind of reaction to your triggers, generally refers to using Universal Action behaviour

{% hint style="info" %}
Check out the included Demo scene for reference usage!
{% endhint %}


# Misc Behaviours

A collection of small, general / single purpose built behaviours

## Movement / Tracking

### Universal Tracker

*Tracks the local player's Tracking Data source, or a Bone transform and copies it over to the target transform*

![](https://cdn.vrchat.sh/ut/ub/universal_tracker.png)

**Parameters**

* **Track Bone**: Switches between tracking a Tracking Data source (Head/Hands) and tracking Bones
* **Bone**: *Only visible when Track Bone is checked*. Specifies which Bone to track
* **Track Player Base**: Switches to player base tracking mode where the Target Transform will follow the player's ground position
* **Track Playspace**: Switches to playspace tracking mode. Allows Target Transform to persist in players playspace, only moving when the player moves with the stick
* **Tracking Target**: *Only visible when Track Bone is unchecked*. Specifies which Tracking Data target to use
* **Target Transform**: Transform to copy the tracked Bone / Tracking Data position and rotation to
* **Track Position**: Specifies whether the position should be copied
* **Track Rotation**: Specifies whether the rotation should be copied
* **Tracking Correction**: Allows to apply an extra X/Y/Z rotation to the transform after copying it over
* **Follow Main Camera**: *For editor use only*. Makes the object follow the main camera, helpful for editor testing

**Events**

* **Reset Offsets**: Resets the player offsets from their player base tracker. Call this to calibrate player offsets at least once, otherwise the tracking will be wrong

**Usage / Examples**

The basic usage of this component is intended to be things like attaching objects to player's hands, head or both.

### Lerped Follower

*Makes an object follow another object with smooth interpolation*

![](https://cdn.vrchat.sh/ut/ub/lerped_follower.png)

**Parameters**

* **Source Transform**: The source of position and rotation to copy from
* **Target Transform**: The target of position and rotation to copy to
* **Lerp Position**: Specifies whether the position should be interpolated (smoothed) or instant
* **Lerp Rotation**: Specifies whether the rotation should be interpolated (smoothed) or instant
* **Lerp Speed**: A speed with which Target will achieve Source's position and rotation
* **Ignore Rotation**: Specifies whether to copy rotation or completely ignore it

**Usage / Examples**

Generally good feeling lerp value for scene objects seems to be at around 5-15, for the player attached object (like with Universal Tracker) higher values are recommended.

Ignore rotation can be used in cases where you want to have the object look at a particular spot or generally not rotate with the Source object. Thins like Rotation / LookAt or Aim constraints come to mind as good combinations.

## Triggers / Actions

### Universal Action

*The bread and butter of any world reactivity, allows you to perform various actions in response to an incoming `Trigger` event. Think about it as a VRC SDK2 Triggers Lite*

![Empty Action](https://cdn.vrchat.sh/ut/ub/universal_action_basic.png)

**Parameters**

* **Active**: Determines if the behaviour is running, since toggling game objects on and off only means they won't run `Update`
* **Fire After Delay**: *Object must be enabled at all times for this to work*. Specifies whether the triggers should be fired after a delay
* **Delay Length**: *Only visible when Fire After Delay is checked*. Sets the delay length in seconds
* **Fire Animation Triggers**: Specifies whether the Animation Triggers should be executed
* **Animator Triggers List**: A list of Animators and Triggers to fire, you can specify the same Animator multiple times to fire multiple triggers
* **Fire Udon Events**: Specifies whether the Udon Events should be sent
* **Networked**: When checked - Udon Events will be sent to targets set in Network Targets
* **Network Target**: Specifies who to send the network events to
* **Udon Events List**: A list of Udon Behaviours and Events to call
* **Fire Object Toggles**: Specifies whether to Enable/Disable/Toggle objects from the Game Objects List
* **Game Objects List**: A list of Game Objects to Enable, Disable or Toggle
* **Fire Collider Toggles**: Specifies whether to Enable/Disable/Toggle the colliders from the Colliders List
* **Colliders List**: A list of Colliders to Enable, Disable or Toggle
* **Fire Audio Events**: Specifies whether to Play the sound clips provided in the Audio List
* **Audio List**: A list of Audio Sources and Audio Clips to play through them

{% hint style="warning" %}
Make sure to only use Networked option when executing this trigger locally, e.g. via a UI Button, otherwise you might cause oversync
{% endhint %}

**Events**

* **Trigger**: Triggers this Universal Action
* **Activate**: Activates the behaviour, so it will react to Trigger events
* **Deactivate**: Deactivates the behaviour, so it will no longer react
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

Universal Action will probably be the most used behaviour in your world, as its basically a simplified trigger from SDK2. It is not as deep as the original triggers, though, so you are expected to combine it with more specific behaviours from Udon Toolkit or other places to extend its functionality.

This component makes it pretty trivial to do things via UI Buttons simply by calling a `SendCustomEvent` on it with `Trigger` as the event name.

You can check out a guide on setting up a basic mirror [right here](/v0.x/guides/mirror-toggle) which should provide a good example of Universal Action usage.

The major thing to note here is to make sure that the component, and the game object its on is always enabled if you're using the `Delay` parameter, otherwise it will never fire as the Update method will never be called.

### Secret Actions

*Sends events to udon behaviours based on the player's name*

![](https://storage.googleapis.com/cdn.vrchat.sh/ut/ub/secret_actions.png)

**Parameters**

* **Active**: Determines if the actions will be executed on Start
* **Actions List**: A list of Player Names and Udon Behaviours to send `Trigger` events to

**Events**

* **Trigger**: Executes the logic again, you will probably want this to be called from the network to run for everyone again

**Usage / Examples**

Ever wanted to have something special activate only for particular users? This behaviour does exactly that! Combine this with Universal Action to do something special for that user, or specify behaviours of your own.

Make sure whatever you're trying to send an event to actually expects the `Trigger` event. Most of Udon Toolkit will work with this.

If sending events once isn't enough - you can use the `Trigger` event to fire it again.

### Networked Trigger

*A "proxy" of sorts that expects a "Trigger" event and sends Networked events to specified behaviours*

![](https://cdn.vrchat.sh/ut/ub/networked_trigger.png)

**Parameters**

* **Event Target**: Specifies who to send the events to
* **Udon Events List**: A list of Udon Behaviours and Events to send to them

**Methods**

* **Trigger**: Fires the events on the provided behaviour over the network

**Usage / Examples**

Think about this as a middle-man between local only things and the network. While its not that useful on its own - it might serve as a nice layer between UI Buttons, and some other behaviours, that you want to call for everyone in the room, or their owners.

{% hint style="info" %}
You can achieve the same results by using Universal Action and checking the Networked checkbox, but this is lighter on performance, so this separation is just a way to simplify things.
{% endhint %}

### Area Trigger

*Sends events to other behaviours when an object enters and exits a trigger collider*

![](https://cdn.vrchat.sh/ut/ub/area_trigger_base.png)

**Parameters**

* **Active**: Specifies whether the triggers will fire, as just disabling the object doesn't stop it from reacting to enter/exit events
* **Collide With**: Specifies the list of layers to filter out the objects you don't want to react to
* **Collide With Local Players**: Specifies whether the event would fire on local player collisions
* **Collide With Remote Players**: Specifies whether the event would fire on remote player collisions
* **Networked**: Specifies whether the events should be sent over the network or only fired locally
* **Network Target**: Specifies who to send the network events to
* **Enter Events List**: A list of Udon Behaviours and Events to send when the correct object enters the trigger collider
* **Exit Events List**: A list of Udon Behaviours and Events to send when the correct object exits the trigger collider

**Events**

* **Activate**: Activates the behaviour, so it will react to objects
* **Deactivate**: Deactivates the behaviour, so it will no longer react
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

This is an equivalent of VRC SDK2's `Trigger Enter` and `Trigger Exit` triggers

Some things to note when using Area Triggers

* Do not use the `LocalPlayer` or `Player` layers in the `Collide With` option, use the `Collide With Local Players` and `Collide With Remote Players` checkboxes instead
* Generally speaking you would want the `Area Trigger` to be on the `MirrorReflection` layer to avoid some weird vrchat issues, unless you have a case where you need a custom layer
* Do not use the `Networked` option if you are using `Collide With Remote Players`, as this will make everyone send events to everyone all the time, causing oversync and possibly desyncing people in the instance.

{% hint style="info" %}
A good rule of thumb when it comes to things like collisions: "if everyone can see object X enter trigger Y - you can keep everything local"
{% endhint %}

### Interact Trigger

*Makes something react to player interaction, VRC SDK2-style*

![](https://cdn.vrchat.sh/ut/ub/interact_trigger.png)

**Parameters**

* **Active**: Specifies whether the object can be interacted with
* **Interaction Text**: Sets the text shown when hovering over the object
* **Proximity**: Sets the interaction distance of the object
* **Networked**: Specifies whether the Udon Events should be sent to other players
* **Network Target**: Specifies who to send the Udon Events to
* **Udon Events**: A list of Udon Behaviours and Events to send

**Methods**

* **Interact**: Sends events to provided Udon Behaviours
* **Activate**: Activates the behaviour, so the object will become interactive
* **Deactivate**: Deactivates the behaviour, so the object will no longer be interactive
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

This is an almost one-to-one equivalent of the VRC SDK2 `Interact` trigger. Use in combination with `Universal Action` to do something when user clicks on an object.

`Activate`/`Deactivate`/`Toggle` events will also allow you to disable the highlight on the object, if you don't want it to be interactive anymore.

### Object Boundary Trigger

*Sends Events to Udon Behaviours if a target object crosses a particular threshold and disables itself (by default)*

![](https://cdn.vrchat.sh/ut/ub/object_boundary_base.png)

**Parameters**

* **Active**: Toggles the threshold checks on and off
* **Target**: The target object to observe
* **Cross Mode**: Makes this trigger run every time the Target crosses the threshold (either way), also makes it, so the trigger doesn't disable itself after the threshold is crossed
* **Test Mode**: Specifies whether the trigger will fire if ALL or ANY of the specifies boundaries are crossed
* **Boundary List**: A list of conditions and coordinates to check against, e.g. `Above Y: 10` will make the trigger fire events if the Target gets above 10 units on Y coordinate
* **Networked**: Specifies whether the Udon Events should be sent to other players
* **Network Target**: Specifies who to send the Udon Events to
* **Udon Events**: A list of Udon Behaviours and Events to send

**Events**

* **Trigger**: Fires the events specified in the Udon Events list
* **Activate**: Activates the behaviour, so it will start checking the boundaries
* **Deactivate**: Deactivates the behaviour, so it will no perform any checks
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

While this might seem confusing at first, the gist of this one is to trigger something when object's position exceeds one of the limits. A good example of it would be using a player head tracker and making it so if the tracker's Y position is below your water level in the map - you enable special fog settings or something like that.

By default, the trigger will only run once and disable itself, otherwise it will start triggering things every frame which is not perfect. You can enable it again by sending an `Activate` even to it.

Although often you might want to trigger things when the object crosses the limit in either direction. Like turning the fog on and off in the example above. In that case you would want to use the `Cross Mode` option that will trigger once every time the limits are crossed.

### Platform Trigger

*Sends Events to Udon Behaviours based on the User's Platform*

![](https://cdn.vrchat.sh/ut/ub/platform_trigger.png)

**Parameters**

* **Fire On Start**: Specifies whether the events should be sent on start
* **Desktop Events**: A list of Udon Behaviours and Events to send if the Player is on Desktop
* **VR Events**: A list of Udon Behaviours and Events to send if the Player is in VR

**Events**

* **Trigger**: Fires the events specified in the Desktop/VR Events Lists

**Usage / Examples**

By default this trigger will fire on the world load, unless the `Fire On Start` is unchecked.

Generally speaking this behaviours is aimed at providing better cross-platform support. Its pretty common to have separate versions of game mechanics, pickups, etc for different platforms that are enabled and disabled on Start.

You can also send the `Trigger` event to `Platform Trigger` to execute it again, e.g. if something happens in your world and you want Desktop / VR react to it differently.

## Visuals

### Skybox Adjustment

*Switches skybox materials or smoothly adjusts values of a single skybox material*

![](https://cdn.vrchat.sh/ut/ub/skybox_adjustment_base.png)

**Parameters**

* **Active Skybox**: The skybox material to transition to
* **Active Floats List**: A list of numeric parameters to transition to
* **Active Color List**: A list of color parameters to transition to
* **Active Vector3 List**: A list of Vector3 parameters to transition to
* **Instant Transition**: Makes the skyboxes switch instantly
* **Transition Time**: Defines the time, in seconds, over which the Float/Color/Vector3 values will be transitioned from default values to active and vice-versa

**Events**

* **Trigger**: Performs a transition from default to active. Send it again to transition back

**Usage / Examples**

Main thing to decide on when using this is whether you need to transition instantly or slowly over time. If you want to transition instantly - its very simple

* Select a material you want to swap the skybox to and put it into the `Active Skybox` field
* Send a `Trigger` event from one of the Udon Toolkit's many triggers when you want to switch
* Done

If you want to slowly transition between different settings, e.g. between high exposure and low exposure to imitate the day/night cycle - its a bit of a different setup

* Uncheck the `Instant Transition` checkbox and specify the `Lerp Time` in seconds
* Expand the `Active Floats List` and click `Add Element`
* Choose `_Exposure` from the dropdown and set the value you want to transition to (e.g. 0.2)
* Send a `Trigger` event from one of the Udon Toolkit's triggers when you want to start transitioning
* Done

{% hint style="warning" %}
The Skybox Adjustment behaviour will use currently active values as a starting point (as of version v0.4.5)
{% endhint %}

### Fog Adjustment

*Switches between two different fog settings - instantly, or over time*

![](https://cdn.vrchat.sh/ut/ub/fog_adjustment_base.png)

**Parameters**

* **Default Fog Color**: First fog color to transition from
* **Default Fog Density**: *Only visible if the fog mode is not Linear*. First fog density to transition from
* **Default Fog Start**: *Only visible if the fog mode is Linear*. First fog start range to transition from
* **Default Fog End**: *Only visible if the fog mode is Linear*. First fog end range to transition from
* **Active Fog Color**: Second fog color to transition to
* **Active Fog Density**: *Only visible if the fog mode is not Linear*. Second fog density to transition to
* **Active Fog Start**: *Only visible if the fog mode is Linear*. Second fog start range to transition to
* **Active Fog End**: *Only visible if the fog mode is Linear*. Second fog end range to transition to
* **Fog Transition Time**: Specifies the duration of the transition, 0 to make it instant
* **Start Active**: Makes it, so the fog will start from the active state, so the first transition will be Active to Default, instead of the other way around
* **Set Initial State**: Applies the Default or Active fog settings, based on whether the `Start Active` checkbox is set

**Events**

* **ActivateFog**: Explicitly transitions from Default to Active state
* **DeactivateFog**: Explicitly transitions from Active to Default state
* **Trigger**: Switches between the two states or starts a smooth transition

**Usage / Examples**

Very similar to [`Skybox Adjustment`](https://github.com/orels1/UdonToolkit/wiki/Misc-Behaviours/#skybox-adjustment) in terms of functionality.\
Set the Default and Active setting and then use something like an `Area Trigger` to switch between them when a player enters or exits a particular area.

If you want to just slowly adjust from current values to the Active values - you don't have to set the `Default` settings at all. Those are only needed if you want to use 2 specific states and toggle between them.

## Advanced

{% hint style="info" %}
This area intended for advanced content creators and people developing their own Udon stuff just to make their life easier and more organized, be advised
{% endhint %}

### UI Readout

*Takes compatible variable values from one Udon Behaviour and displays them in UI Text or UI Slider elements, with formatting support*

![](https://cdn.vrchat.sh/ut/ub/ui_readout_base.png)

**Parameters**

* **Active**: Turns the value update on and off
* **Source**: The source of the values
* **Text Readouts List**: A list of variables that can be converted to text and UI Text components to display them in
* **Text Readout Formats**: A list of formatting rules applied to each readout value. **It is very important to have the same amount of elements in both List and Format arrays**. More on that below
* **Slider Readouts List**: A list of numeric variables that can be assigned to a value of a UI Slider
* **Slider Readouts Multipliers**: A list of multipliers that each variable value will be multiplied by, same rule as formats apply

**Events**

* **Activate**: Activates the behaviour, so it will start reading fresh values
* **Deactivate**: Deactivates the behaviour, so it stops reading values
* **Toggle**: Toggles the active state, provided for convenience

**Usage / Examples**

> The values are grabbed and assigned in `LateUpdate` to have the most up to date values possible

**Important**: always make sure Formats and Multipliers lists have at last as many elements as Text Readouts and Slider Readouts lists respectively. The UI should generate the lists for you filled with default values, but it doesn't hurt to double check. Each row of the Formats / Multipliers list corresponds to the same row in the Text/Slider lists.

The formatter works by replacing `{0}` in whatever text you provide to each formatter with the value of the variable. So something like `Speed: {0} u/s` for a variable `speed` in the `Target` that has value of `10` will result in `Speed: 10 u/s` being displayed in the text element.

Multipliers work in a similar fashion - by multiplying a corresponding variable value by this multiplier before passing it to a slider.

`UI Readout` is built to provide a quick way of setting up something like a HUD you can attach to player's head via `Unversal Tracker` that would print out all the needed information from something like a flight system, or a combat system of some sorts. It will pre-populate lists of compatible variables for you, so you wouldn't need to dig into the code to figure out what you can display.

Note that this process can be performance heavy, so you don't want many of these behaviours running at all times, use the `Toggle` event to only read values when needed.

### Shader Feeder

*Passes various shader properties to a provided Mesh renderer*

![](https://cdn.vrchat.sh/ut/ub/shader_feeder.png)

**Parameters**

* **Active**: Turns the shader property value updates on and off
* **Source**: The shader to get the property names from
* **Targets**: The list of MeshRenderers to pass the property values to
* **Custom Update Rate**: Specifies whether a custom Update Rate should be used, if unchecked - the values will be passed every frame
* **Update Rate**: (Only visible if Custom Update Rate is checked) Specifies how often the values should be set, Per Second or Per Minute (think of it as BPM mode)
* **Set Scene Start Time**: Specifies whether scene start time should be passed on world load
* **Start Time Target**: Name of the property to pass the start time to
* **Set Cycle Length**: (Only visible if Custom Update Rate is checked) Specifies whether the current cycle time (based on a Custom Update Rate) should be passed on Start
* **Cycle Length Target**: Name of the property to pass the cycle length to
* **Set Cycle Start Time**: Specifies whether the current time (which is also the time of the current update cycle start) should be passed
* **Cycle Time Target**: Name of the property to pass the cycle start time to
* **Slider Sources**: List of Slider components and corresponding property names to pass the Slider values to
* **Transform Sources**: List of Transforms and corresponding property names to pass the Transform positions to

**Usage / Examples**

One of the most common examples would be to use `ShaderFeeder` as a way to pass some object's position to a shader. Player position being one of those, if used in combination with [UniversalTracker in a Player Base Tracking mode](/v0.x/behaviours/misc-behaviours#universal-tracker).

To do that you will want to add a Transform that follows the Player to the Transform Sources list and choose a property to pass the value too and that's it!

Another popular choice would be to make an object do something on beat, in that case all you need is the Cycle Start Time and Cycle Length to be passed every Update Cycle. Check the Custom Update Rate and select Per Minute as the mode with your target BPM, and you're done!

The `ShaderFeeder` demo scene includes 2 basic shaders for things mentioned above: a Light Bridge of sorts that tracks a transform, and a BPM synced pulse with adjustable hue.


# Camera System

A Depth of Field camera system with VR Friendly physical controls and a desktop mode

![](https://cdn.vrchat.sh/ut/camera_system/camera_system_header.png)

## Quick Setup Guide

* Follow the general [installation guide](/v0.x#requirements)
* Import the PostProcessing package by clicking `Window` -> `Package Manager` in Unity and searching for `PostProcessing`
* Create an empty object in your scene where you want your camera to be spawned
* Click on `Window` -> `UdonToolkit` -> `Camera System Setup` in the unity top bar
* Drag and drop the created empty into the Camera Position field
* Provide a watermark overlay if you want to draw a watermark on top of the pictures people take, e.g. the world logo

{% hint style="warning" %}
The watermark should be a transparent 16:9 image (1920x1080)
{% endhint %}

* You can also add one of the default guide panels near the camera by checking "Add Camera Guide". This option is recommended, so your players know how to use the system
* Click `Setup Layers`
* Click `Create Camera System`
* Click on the created `Camera Lens` object and there `Compile All Udon Sharp Programs` at the bottom of the Udon Behaviour component
* You're done!
* If the positioning isn't exactly how you like it: `Ctrl+Z`, move the empty object and click `Create Camera System` again

Now you can build your project, and the camera should be working!

![](https://cdn.vrchat.sh/ut/camera_system/camera_system_setup.png)

## Full Documentation

![](https://storage.googleapis.com/cdn.vrchat.sh/ut/camera_system/camera_lens.png)

### **Customization tips**

* You can change the watermark after the creation of Camera System via the setup script by adjusting the material on `Camera Tracker` -> `Sphere` object
* You can change Focus range by adjusting `Focus Distance` in the `Camera Lens Profiles` -> `Camera Lens PP Far` object's PostProcessing settings

  > When adjusting Focus range, don't forget to change the `Auto Focus Distance` on the `Camera Lens` object
* You can adjust minimum focal length by changing the Focal Length in the `Camera Lens Profiles` -> `Camera Lens PP Focal` object's PostProcessing settings
* You can adjust maximum focal length by changing the Focal Length in the `Camera Lens Profiles` -> `Camera Lens PP` object's PostProcessing settings
* You can adjust finger sphere's size and look by changing the `Camera Left Finger` -> `Sphere` object

### **Parameters**

* **Active**: Specifies whether the camera is actively responding to inputs
* **Lens Animator**: An animator that controls Camera PostProcessing profiles
* **Camera Animator**: The main animator that controls Camera UI
* **Pickup**: The camera pickup component
* **Auto Focus**: Specifies whether the auto focus is currently enabled
* **Auto Focus Distance**: Specifies the auto focus max distance (should correspond to the `Camera Lens PP Far` focus distance setting)
* **Desktop UI**: The UI Canvas for Desktop players
* **VR UI**: The UI Canvas for VR Players
* **Focus/Focal/Zoom Sliders**: The Focus/Focal/Zoom UI Sliders for Desktop
* **VR Focus/Focal/Zoom Sliders**: The Focus/Focal/Zoom UI Sliders for VR
* **Focus/Focal/Zoom Text**: The Focus/Focal/Zoom UI Text that is adjusted to display the currently active control
* **Focus/Focal/Zoom BG**: The Focus/Focal/Zoom UI Text that is adjusted to display the currently active control
* **Position Text**: UI Text that displays if the camera exists in World or Player relative space
* **View Sphere**: The Camera View sphere which override's player's desktop view or camera view (based on type of player)
* **Camera Object**: Object that has a Camera component on it
* **Visuals**: The camera visual mesh
* **Finger Sphere**: The Mesh Renderer of the finger collider (`_Enabled` property will be set depending on whether the camera is held)
* **Auto Focus Icon**: Auto Focus Frame in Desktop UI that will be colored `Active Control Color` when enabled
* **Always On Icon**: Always On Frame in Desktop UO that will be colored `Active Control Color` when enabled
* **Active Control Color**: The color used in Desktop UI to signify that control is enabled
* **Drop Target**: The Transform camera will be reparented to when Player relative space lock is enabled
* **Playspace Tracker**: The Universal Tracker instance with `Track Playspace` enabled (camera will automatically call `ResetOffsets` on it whenever it is picked up and dropped)

### **Events**

* **SwitchPosition**: Switches between World and Player relative positioning
* **FlipCamera**: Flips the camera direction and screen
* **SwitchAF**: Toggles Auto Focus on and off
* **SwitchControl**: Switches between currently active controls (Focus/Focal/Zoom)
* **ToggleAlwaysOn**: Toggles the Always On mode (camera stays active on drop when Always On is enabled)
* **ToggleWatermark**: Toggles final image watermark on and off

### **Usage and Examples**

{% hint style="info" %}
You are expected to use `Camera System Setup` UI from `Window` -> `UdonToolkit` -> `Camera System Setup` in the unity top bar to set everything up, consult the top of this page for the required steps. But here is a guide on the manual setup
{% endhint %}

* Drag and drop `UT Camera System` from `UdonToolkit/Camera System` folder into the scene
* Right click and click `Unpack Prefab Completely` (that is very important)
* Move out all the objects inside `UT Camera System` into the root of the scene (also important)
* By default camera is set up to use layer 27 for PostProcessing, and layer 28 for the VR Controls
* VR Controls layer is expected to be set to only collide with itself, otherwise you might get unwanted vr control interactions even if you did not actually press anything with your finger
* Make sure those exist or change them
  * You can set objects inside `Camera Lens Profiles` to any layer that is not `PostProcessing` or `Default`
  * Don't forget to update the target layer of the `PostProcessing Layer` component on the `Camera Lens` -> `Lens Camera` object
  * When using a custom VR Controls layer, make sure to change tha layer of `Camera Left Finger`, as well as the `VR Controls` and `Start Sphere` objects
  * If you changed the VR Controls layer - you'll also need to update it in each `Sphere` object's Area Trigger `Collide With` parameter
* Click `Compile All UdonSharp Programs`
* Check that every UdonBehaviour in the system shows set public variables: check `Camera Lens`, any of the `VR Controls` -> `Sphere` objects, `Camera Tracker` object, any of the `Camera Flip`, `Always On`, `Focus` and `Switch Position` objects
  * If any of them show `No Public Variables` - click `Force Compile Script`
* Click on the `Camera Lens` object and uncheck `Is Active` on the `Parent Constraint`
* Move the Camera Lens wherever you want
* Click `Activate` on the `Parent Constraint`
* Uncheck `Is Active`, expand `Constraint Settings`
* Uncheck `Lock` and set all the `Position Offset` and `Rotation Offset` values to 0, check `Lock` again
* Check `Is Active`
* You should now be ready to use the camera in your world!

{% hint style="info" %}
This is a bit of an involved process, hence why the automated setup was created, the manual guide is provided for reference of what UdonToolkit is actually doing when you click **Create Camera System**
{% endhint %}


# Flight System

Add the power of flight to your world

![](/files/-MQiwUIe5g1Gqz6FWVL1)

{% embed url="<https://youtu.be/b4IVc7wMA6g?t=60>" %}
Flight System preview
{% endembed %}

{% hint style="info" %}
Udon Toolkit's Flight System allows your players to fly like a superman, glide across the ground and hover mid air on both Desktop and VR platforms!
{% endhint %}

**Check it out in my** [**Catch A Flight world**](https://vrchat.com/home/launch?worldId=wrld_65fd7c51-89f7-4c7b-8a4f-2620a16f0a75)**!**

A more developer-oriented test world with more info is [also available here](https://vrchat.com/home/launch?worldId=wrld_842e079c-8ad3-4c99-afa2-ac6afaf78176)

## Features

* Cross-platform (VR/Desktop) flight system with Superman-like controls (point toward direction)
* Ability to glide across the ground maintaining consistent height
* Ability to hover mid-air
* One Handed mode support
* Speed Boost and Flight Restriction mechanics
* Hover and Gliding mechanics extendable with your own logic via provided callback events

## Installation

1. Drag the Flight System prefab from the `UdonToolkit/Flight System` folder into your scene
2. Build a test version
3. Jump to start flying!

{% hint style="warning" %}
Testing in editor requires [CyanEmu](https://github.com/CyanLaser/CyanEmu)
{% endhint %}

## Usage

* **VR**
  * Point your arms slightly forward and up, jump to start the flight. If glide is enabled (it is by default) - point your arms slightly above your head to stop the glide and takeoff
  * Spread your arms to slow down, bring them together to speed up
  * Point where you want to fly. Think about it as if you were a Superman
  * Point back to freeze mid-air
  * When gliding - point upwards, above your head to take off
  * When gliding - spread your arms to slow down and point downward to land
* **VR One Handed**
  * Point the selected one handed tracking source where you want to fly (Head, Left Hand or Right Hand)
  * Press Left Trigger or Right trigger to slow down
  * When gliding - look up to take off
  * When gliding - press down the trigger and look towards the ground to land

{% hint style="info" %}
During glide when going full speed (arms close together, pointing in same direction) you will not be able to land by pointing your arms towards the ground. That is done to help with arm fatigue allowing for extended periods of gliding without getting tired. To land - slow down by spreading your arms and point them towards the ground
{% endhint %}

* **Desktop**
  * Jump to start flying, if glide is enabled (it is by default), press and hold `T` to takeoff
  * Use your mouse to control the direction
  * **There are currently no speed controls on desktop**
  * If hover is enabled (it is by default), press `F` to enter hover mode, you'll freeze in place allowing you to look around and chat with people. Press `F` again to exit hover
  * Press `P` to respawn to the **Reset Position**

## Configuration

{% hint style="success" %}
UdonToolkit's FlightSystem provides many configuration options for you to adjust. I recommend still using the prefab as a starting point for easier configuration though.
{% endhint %}

![](/files/-MQifzvMIKZ2ZI8aE5fW)

### Parameters

* Tracking References
  * **Right Hand**: Right hand tracking object
  * **Left Hand**: Left hand tracking object
  * **Head**: Head tracking object
* General Settings
  * **Max Speed**: Maximum flight speed
  * **Allow Boosting**: Specifies whether the `Boost` event will do anything
  * **Boost Length**: Determines the time of the boost in seconds
  * **Boost Speed**: Maximum speed during boost
  * **Allow Gliding**: Specifies whether gliding along the surface is allowed
  * **Ground Layers**: Layers used to determine the ground altitude used for landing/takeoff speed scaling and gliding
  * **Allow Hover**: Specifies whether hovering is allowed using the hover key

{% hint style="warning" %}
If **Allow Gliding** is checked - Desktop players will need to press the takeoff/landing key in order to enter flight or exit glide
{% endhint %}

* Comfort Settings
  * **Acceleration Smooth Factor**: The lerping amount applied to the acceleration (lower is smoother)
  * **Deceleration Smooth Factor**: The lerping amount applied to deceleration (lower is smoother)
  * **Ground Slow Max Height**: The maximum height at which the takeoff/landing assist will take place. FlightSystem will scale the maximum speed based on the distance from the ground to aid with precise landing and smoother takeoff
  * **Min Ground Speed Modifier**: Specifies the speed threshold at which the system will automatically slow the player down during takeoff/landing. The value of `0.5` will mean that the player will be never be slowed down to less than `50%` of max speed during takeoff/landing

{% hint style="info" %}
It is recommended to always have a relatively high **Deceleration Smooth Factor** as sluggish deceleration can cause the feeling if sluggishness and cause nausea
{% endhint %}

* Desktop Keybinds
  * **Glide Take Off Key**: The takeoff/landing key allowing desktop players to perform those actions
  * **Freeze In Place Key:** The hover key switching between hovering and gliding/flying states if hovering is allowed
  * **Force Respawn Key**: The respawn key which will stop the flight and teleport the player to the **Reset Position**&#x20;
* Initial State
  * **Flight Restricted**: Specifies whether the flight system is turned off by default as if the player was in a restricted zone. You can send an `AllowFlight` event to the system to enable it
  * **Reset Position**: Determines the target to which the player will be teleported when pressing the **Force Respawn Key**
* Callbacks
  * **On Flight Start**: Sends the specified event to the provided UdonBehaviour when the player engages the flight system
  * **On Flight End**: Sends the specified event to the provided UdonBehaviour when the player lands
  * **On Hover Start**: Sends the specified event to the provided UdonBehaviour when desktop hover is started by pressing the **Freeze In Place Key** or when the player spreads their arms to stop in VR
  * **On Hover End**: Sends the specified event to the provided UdonBehaviour when desktop hover is ended by pressing the **Freeze In Place Key** again or when the player brings their arms closer together
  * **On Glide Start**: Sends the specified event to the provided UdonBehaviour when the player enters glide
  * **On Glide End**: Sends a the specified event to the provided UdonBehaviour when the player exists glide (either by taking off or landing)

{% hint style="success" %}
There is an included `FlightSystemDebugger` behaviour with the logging for all the Flight System Callbacks enabled so you can quickly test your environment and when each system engages/disengages
{% endhint %}

{% hint style="warning" %}
**VR currently has a very rudimentary hover mechanic.** I would encourage you to create some extra system for VR specific hover toggle, like maybe a couple interacts that appear if the player slows down, which they can click to switch into a hover mode. That will make the hover / flight switch more intentional and less spontaneous. I might add an example of this at some point in the future
{% endhint %}

### Events

* **Boost**: Raises the **Max Speed** to the **Boost Speed** for **Boost Length** seconds
* **RestrictFlight**: Prohibits players from flying, even if the player is already mid-flight (will make them fall)
* **AllowFlight**: Allows players to fly
* **DisableBoosting**: Disables the boosting behaviour, calling `Boost` after boosting has been disabled will not do anything. This does not affect players that are already boosting. They will stop boosting as their boost timer runs out.
* **EnableBoosting**: Enables the boosting behaviour
* **Stop Boost**: Stops currently active boost
* **DisableGliding**: Disables gliding, players will simply land and take off while close to the ground. The takeoff/landing speed helpers will still work as usual. This will interrupt current glide
* **EnableGliding**: Enables gliding
* **StopGliding:** Stops current glide and grounds the player
* **EnableOneHandedMode**: Enables One Handed Mode for flight controls
* **SetOneHandedTargetHead**: Sets One Handed Mode tracking target to player's Head
* **SetOneHandedTargetLeftHand**: Sets One Handed Mode tracking target to player's Left Hand
* **SetOneHandedTargetRightHand**: Sets One Handed Mode tracking target to player's Right Hand
* **DisableOneHandedMode**: Disables One Handed Mode for flight controls and returns to the default two-handed flight

{% hint style="info" %}
If you are planning to build upon the logic exposed by the Flight System, I would also encourage you to check out the code! Almost every single line is commented, with motivation / explanation which should help you better understand how it all comes together
{% endhint %}

## Demo

**Check it out in my** [**Catch A Flight world**](https://vrchat.com/home/launch?worldId=wrld_65fd7c51-89f7-4c7b-8a4f-2620a16f0a75)**!**

{% hint style="success" %}
A more developer-oriented test world with more info is[ also available here](https://vrchat.com/home/launch?worldId=wrld_842e079c-8ad3-4c99-afa2-ac6afaf78176). The same world is also provided as a demo scene in the `UdonToolkit/Demo/Flight System Demo` scene. Don't hesitate to take a look!
{% endhint %}

## Feedback

{% hint style="info" %}
If you have any suggestions for the FlightSystem or encounter any issues, please do not hesitate to [file an issue on Github](https://github.com/orels1/UdonToolkit/issues/new) or reach out[ in my Discord](https://discord.com/invite/fR869XP), as well as [support the Toolkit on Patreon](https://www.patreon.com/orels1)
{% endhint %}


# Presentation System

A turn-key solution for Virtual Conferences

{% hint style="info" %}
UdonToolkit's Presentation system provides a fully-featured presentation solutions for creating VR Talks, Conferences, or just showing some slides to someone.
{% endhint %}

The system was originally built for the [Prefabs TLX](https://tlx.dev) World Developer conference. You can see it in action in one of the [many talks](https://tlx.dev/talks).

## Features

* A solution to display your presentations with different slide durations, animations and videos
* Slide controls and counters
* Time remaining indicator
* Voice override for the speaker w/ support for extra override zones, e.g., for Q\&A sessions or multiple speakers
* Material swap/object toggles for talk star/end
* Full late-joiner support
* Camera system for showing the speaker
* Stream system for capturing the presentation with talk info and speaker camera overlays
* Personal screen pickup for people to take a closer look
* Laser pointer for the speaker
* Callbacks for all major system events for easier extensibility with your own logic

## Requirements

* TextMeshPro (from Package Manager)
* Cinemachine (from Package Manager)
* UdonSharp v0.19.2 or later
* VRChat SDK 2021.01.28.19.07 or later

## Setup

{% embed url="<https://youtu.be/n_UyLL0rPG0>" %}

* Drag & Drop  the Presentation System prefab from `UdonToolkit/Systems/Presentation System`
* Make sure its in a 0,0,0 spot of the world, its important for some of the tracking objects
* Right click the prefab in the hierarchy and select "Unpack Prefab Completely (Udon does not work well with prefabs)
* Expand the Scene Objects in the hierarchy and move the following objects where you need them
  * **Screen**: the presentation screen. Will display the current slide when the presentation is active
  * **Presentation UI**: the speaker's and host's user interface, place it where your speakers will be talking from
  * **Laser Pointer**: a simple laser pointer pickup to help with explaining the slides content, put near the Presentation UI for ease of use
  * **Presentation Audio Source**: this source will play the presentation audio if it has any. It is a 3D source by default, and is best placed above and in front of where your audience will be sitting. You can make it 2D if you want
  * **Streamer Spot**: a special box where the stream capture user will be teleported when pulling up the stream overlay. Best placed below the stage, slightly behind where the speaker is standing. More on that in the Setting Up Capture section
  * **CM Speaker Cam**: a Cinemachine camera object that captures the speaker. Place somewhere in front of where the speaker will be standing. It will automatically track their head
* Build a test version
* Click Take Control on the UI and try starting a presentation!

## UI Controls

* **Take Control**: takes ownership of the presentation system, shows the UI and removes any currently active voice boosts
* **< and >**: switch between the presentations in the presentations list, does not load the presentation unless Select is pressed
* **Select**: loads the selected presentation
* **Start Talk**: starts the presentation and boosts the speakers's voice ([see below for voice settings](/v0.x/systems/presentation-system#adjusting-voice-settings))
* **Stop**: stops the presentation and removes the voice boost
* **Next Slide**: switches to the next slide
* **Prev Slide**: switches to the previous slide
* **Play Video**: unpauses an animated or video slide (one that has `Auto` checked, [see below](/v0.x/systems/presentation-system#adding-your-presentations))
* **Pause**: pauses an animated or video slide
* **Height Adjust + and -**: moves the UI up and down for ease of use

### Personal Screen Controls

* **Desktop**
  * Press `l` to spawn a personal screen in front of you
* **VR**
  * Press both triggers to spawn a personal screen pick up

This screen contains the presentation slides, the view of the speaker and the presentation info. This is helpful for getting a better look at the slides with small text or when generally being far away from the presentation

### Stream Controls

Press `k` to pull up the stream and be teleported to the configured stream spot position. This will override your view with a special overlay that captures the current speaker and the slides.

{% hint style="warning" %}
Before using Stream Overlay - you need to configure the list of users who are allowed to open it up in the `Presentation Stream Controller`
{% endhint %}

## Configuration

### Creating Presentation Videos

{% hint style="info" %}
Before adding presentations, we need to prepare them so they can be dynamically loaded in VRChat.
{% endhint %}

Most of the presentation software allows you to export a presentation as a video file (like an .mp4 video), exceptions are Web-Based solutions, like Google Slides, but you can still export them as PowerPoint presentations and use it to generate a video.

**Presentation videos work best when they:**

* Are exported with a fixed slide time of 3 seconds
* Have maximum of one video per slide
* Only have one animation sequence per slide
  * e.g., if you have a slide where you want to animate things piece by piece - split them into different. Otherwise - they will all be played in a sequence without pauses, which might be undesirable

When you have your presentation files - upload them to YouTube or Vimeo and set them to be accessible via a link. If you are going to use Vimeo - please use their direct links to avoid any issues

### Adding Presentations

* Expand the hierarchy of the Presentation Player object
* Click on the provided example presentation to see its Udon Behaviour
* The core of the presentation object consists of the 5 main elements
  1. **Talk Url**: the url of the video file with the presentation, YouTube or Vimeo direct link works best
  2. **Talk Title**: the title of the presentation that will be used when selecting a talk, and also displayed for the stream capture
  3. **Talk Author**: the name of the speaker, will also be shown on the stream capture
  4. **Talk Length**: this is only displayed to the speaker to help manage time
  5. **Slides**: a list of slide durations that allow the system to switch between slides

So, to add your own presentations - you need to

* Fill out the fields
* Expand the Configuration Wizard dropdown
* Set the Slide Count to the amount of slides in your presentation
* Set the Slide Duration to the time per slide you set when exporting the video
* Click Configure Talk
* Duplicate the object and repeat for each presentation

When you're done creating individual presentation objects - do the following

* Click on the Presentation Player
* Click Populate Talks button
* This should fill the Talks List with all of your talk objects (they must be parented to the Presentation Player)
* You can expand the Talks list by clicking on the dropdown to see all of your talks. You can move them around to change their order in the presentation UI

{% hint style="success" %}
If you do not have any videos or animations - you're done! You should be able to make a test build and try out your presentations in game!

If you **DO** have animations - look below
{% endhint %}

#### If you have videos or animations on your slides

1. Expand the slides dropdown on the individual presentation and check Auto checkbox near all slides that have animations or videos.
2. Edit the Slide Durations for those slides as follows:

* **For videos**
  * PowerPoint will export slides with videos exactly the matching the length of the videos, so just set those slide durations to the lengths of the included videos
* **For animations**
  * This one is a bit more manual. Open the video you exported and see how much time the slide takes with all the animations included. Most time 3 seconds will be just enough. Otherwise - set the Slide Duration of that slide to the appropriate amount

### Customizing the Screen

{% hint style="info" %}
The Presentation System allows you perform a couple of actions when the presentation starts and ends. This is also where you can adjust the default placeholder material for the screen
{% endhint %}

**Presentation Screen Controller** allows you to swap materials on your screen mesh (or any other mesh) when the talk starts and ends

![](/files/-MU1YngSC1VjsIr5NfHn)

If you don't want to swap any materials - uncheck the `Swap Materials` option.

You can configure `Presentation Placeholder` material however you like, or you can swap it to a different material entirely: just drag the new material onto your screen and into the `Default Mats` column for the `Screen` mesh in the list - and you're done!

You can also toggle some objects on and off when the presentation Starts or Ends. For example, you can disable your screen object, and drop it into the `OnTalkStart` and `OnTalkEnd` lists to be enabled and disabled respectively. This will make it so the screen with the slides is only visible when the presentation is going. That would look something like this

![](/files/-MU1ZEbiAGsdbdpEMM7w)

{% hint style="warning" %}
If you do not want any objects to toggle - you can uncheck `Toggle Objects`, note that it will also stop the Speaker Camera from being turned on when the presentation starts starts.
{% endhint %}

### Adjusting Voice Settings

{% hint style="info" %}
By default the Presentation System will boost the voice range of the speaker up to 250 units of distance. It will only boost the person who is currently in control of the presentation UI and clicked the `Start` button.
{% endhint %}

![](/files/-MU1agVj951NeJ-TzvN9)

Most of the configuration is meant to be done by simply sliding the Range Boost slider left and right. You can also use the Gain Tweak parameter to directly affect the volume boost without affecting range, but that can make the speaker too loud when up close, so be careful with it.

{% hint style="success" %}
You can restrict the voice boost only to a set of particular players by clicking `Restrict Access` button and adding the player names to the `Allowed Users` list.
{% endhint %}

Another way of boosting someone's voice is by using `Presentation Voice Zones`, which will boost the volume of anyone who enters its trigger collider. One zone is provided as an example inside `Scene Objects`. It is disabled by default and will not affect anything unless enabled. You also need to check the `Use Zones` checkbox on the `Presentation Voice Controller` for zones to work.

A good use case for zones might be a Q\&A spot where people can come up to and ask their questions to the speaker. That way everyone in the room can hear them

## Stream and Video Capture

{% hint style="info" %}
A big part of creating a presentation is providing a good recording / stream for the people who cannot attend the event personally. UdonToolkit's presentaiton system is fully outfitted to handle that with a Stream/Capture overlay system
{% endhint %}

The system works as follows: when a users presses the stream overlay key, provided they are in the whitelist, they will be teleported to a special streamer spot and get their desktop view overtaken by a special stream overlay. They can press the hotkey again to exit that view.

Default hotkey: `k`

### Configuration

* Move the Streamer Spot object to be positioned below and behind the speaker in such a way that the Streamer Spot Spawn is orientated towards the speaker to provide the best audio capture experience
* Put the Presentation Stream Controller somewhere far from your main world area, by default it is put all the way at x: 1000 y: 1000 to not interfere with the main location
* Inside of the `Presentation Stream Controller` configure the following parameters
  * Set the Reset Position to your world spawn point, the user will be teleported there when exiting the stream view
  * Set the Engage Key to whatever you like
  * Populated the Allowed Users list with the usernames of people who should be able to enable stream overlay, or uncheck Restrict Access to allow everyone to do that (not recommended)
* Inside of the Presentation Stream Controller - find the Stream object and customize the stream overlay UI to your liking

{% hint style="success" %}
For the best viewing experience - record the video with the audio capture set to mono, as spatialization sounds very odd when re-watching the recordings
{% endhint %}

## Full Reference

{% hint style="info" %}
The Configuration guide above includes the core information that should let you configure everything and get started with the presentation system. But if you want to extend it or learn about every single exposed parameter, here's a full reference
{% endhint %}

### Presentation Player

*The top-level behaviour that controls the general flow and sends events to everything else*

#### Parameters

* **Talks**: a list of `TLXTalk` objects that contain the individual talks data
* **Main Player**: the `PresentationVideoPlayer` object that will be active for everyone in the room, responsible for loading and displaying the slides
* **Lookahead Player**: the `PresentationVideoPlayer` object that is only active for the current speaker and is taking care of the Next Slide preview
* **Restrict Access**: specifies whether only particular users will be able to control the Presentation Player
* **Allowed Users**: Contains the list of users that can take control of the Presentation Player. Only visible when Restrict Access is checked, does not have any effect otherwise
* **Callbacks**: a set of callbacks that will be called when a particular event is handled by a Presentation Player
  * **OnTakenControl**: will fire only for the user that just took control of the Presentation Player, you can set the `Target` to `All` to fire the event on all the clients
  * **OnLostControl**: will fire for every user in the instance when someone takes control of the Presentation Player (does not fire on the new owner of the Presentation Player)
  * **OnTalkStart**: will fire for every user in the instance when the presentation starts
  * **OnTalkEnd**: will fire for every user in the instance when the presentation ends
  * **OnNextSlide**: will fire for the current owner of the Presentation Player when they click Next Slide
  * **OnPrevSlide**: will fire for the current owner of the Presentation Player when they click Prev Slide

{% hint style="danger" %}
Callbacks allow you to specify a network target: **Local**, **Owner** or **All**. If the event can be seen by everyone (based on the description above) you **DO NOT** want to use **All**, as that will cause the event to oversync and fire an extra time for each single user in the instance.

So in this case you only want to use **All** on events that only fire for the owner of the Presentation Player. Others should only use **Owner** or **Local**
{% endhint %}

#### Events

* **TakeControl**: takes ownership of the Presentation Player, fires OnTakenControl for the initiator and OnLostControl for everyone else
* **StartTalk**: starts the currently selected presentation, fires OnTalkStart for everyone
* **StopTalk**: stops the currently active presentation, fires OnTalkEnd for everyone
* **PrevSlide**: goes back a slide, fires OnPrevSlide for the current owner
* **NextSlide**: goes forward a slide, fires OnNextSlide for the current owner
* **NextTalk**: selects next presentation from the Talks list and loads it up
* **PrevTalk**: selects previous presentation from the Talks list and loads it up
* **PauseTalkVideo**: pauses an autoplaying presentation slide for everyone
* **PlayTalkVideo**: resumes the autoplaying presentation slide for everyone
* **SelectTalk**(int newIndex): selects the presentation with the specified index and loads it up

### Presentation Talk

*The presentation description class, contains the information about a single presentation and the list of slides*

#### Parameters

* **Talk Url**: the url of the presentation video file, you generally want this to point to a YouTube or Vimeo url
* **Talk Title**: the title of the presentation, will be used in the talk selector UI and the stream capture overlay
* **Talk Author**: the name of the speaker, will be used in the stream capture overlay
* **Talk Length**: the length of the presentation in minutes, will only be used for a timer that is shown to the speaker to help them manage the presentation time
* **Slides**: the list of the presentation slides, internally consists of two arrays
  * **Slide Duration**: the length of a specific slide, if you have a video or an animation within a slide - you will most likely need to adjust this number
  * **Auto**: whether the slide should be autoplayed, this should be checked for all the slides that contain videos or animations

{% hint style="info" %}
When using Vimeo for video hosting - it is recommended to use direct file links in Talk Url. You can also use your own hosting for the video files and use the direct links inside of the Talk URL. Be weary that event attendees will need to click **Allow Untrusted URLs** in VRChat settings.
{% endhint %}

#### Events

{% hint style="danger" %}
`Presentation Talk` is not built to handle incoming events from anything but the Presentation Player, these are provided solely for reference
{% endhint %}

* **Prepare(int slideIndex)**: loads up the presentation video in both main and lookahead players and seeks them to the provided slide index
* **Play**: plays the first slide of the presentation
* **SeekToSlide(int slideIndex)**: seeks main and lookahead players to the provided slide inde
* **Stop**: stops both main and lookahead players
* **NextSlide**: goes forward a slide
* **PrevSlide**: goes back a slide
* **PlayContinuous**: resumes autoplaying slide playback
* **PauseContinuous**: pauses autoplaying slide playback

### Presentation UI Controller

*Drives the Presentation UI functionality*

#### Parameters

* **Presentation Player**: the reference to the main Presentation Player behaviour
* **Presentation UI**: the Presentation UI root Canvas game object
* **Owner Objects**: these objects will be enabled for the new owner when they Take Control of the Presentation Player
* **Public Objects**: these objects will shown to everyone but the owner of the Presentation Player
* **Owner Collider**: the collider that covers the whole of the Presentation UI canvas to provide proper UI detection
* **Public Collider**: the collider that covers only the Take Control button to minimize the player's laser showing up randomly when they're watching the talk
* **Talk Selector Title**: the TextMeshPro component which displays the presentation title in the Presentation Selector on top of the Presentaiton UI
* **Slide Index Text**: the TextMeshPro component which displays the current slide index to the speaker
* **Time Left Text**: the TextMeshPro component which displays the remaining presentation time to the speaker

#### Events

* **SelectorPrev**: selects the previous presentation in the selector and displays its title to the current owner
* **SelectorNext**: selects the next presentation in the selector and displays its title to the current owner
* **SelectorConfirm**: confirms the presentation selection and tells Presentation Player to load it
* **HandleTakeControl**: enables the Owner Objects and disables the Public Objects, meant to be used in the OnTakenControl callback of the Presentation Player
* **HandleLoseControl**: disables the Owner Objects and enables the Public Objects, meant to be used in the OnLostControl callback of the Presentation Player
* **LowerUi**: lowers the Presentaiton UI panel by 0.05, only allows to go down by 2 units maximum
* **RaiseUi**: raises the Presentation UI panel by 0.05, only allows to go up by 2 units maximum
* **HandleTalkStart**: sets the slide index in the Presentation UI and starts the presentation timer, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: resets the slide index and the presentation timer in the Presentation UI, meant to be used in the OnTalkEnd callback of the Presentation Player
* **HandleNextSlide**: sets the slide index in the Presentation UI to the next slide, meant to be used in the OnNextSlide callback of the Presentation Player
* **HandlePrevSlide**: sets the slide index in the Presentation UI to the previous slide, meant to be used in the OnPrevSlide callback of the Presentation Player

{% hint style="info" %}
As you can see, events that start with `Handle` usually meant to be used inside of a Presentation Player callback, and that is the general naming scheme I'm trying to establish
{% endhint %}

### Presentation Screen Controller

*Reacts to the Presentation Start/End events to swap screen materials or toggle game objects*

#### Parameters

* **Swap Materials**: specifies whether to swap materials inside Material Swap Targets
* **Material Swap Targets**: defines a list of meshes which will have their materials swapped when the Presentation Starts and Ends
  * **Meshes**: meshes to swap materials on
  * **Default Mats**: materials to apply when the presentation is stopped
  * **Active Mats**: materials to apply when the presentation starts
* **Toggle Objects**: specifies whether to toggle game objects on presentation start / end
* **OnTalkStart**: list of objects to be affected when the presentation starts
* **OnTalkEnd**: list of objects to be affected when the presentation ends

#### Events

* **HandleTalkStart**: performs the selected actions in the Behaviour parameters, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: performs the selected actions in the Behaviour parameters, meant to be used in the OnTalkEnd callback of the Presentation Player

### Presentation Voice Controller

*Applies voice adjustments to the players*

#### Parameters

* **Restrict Access**: specifies whether only particular users will be able to receive a voice boost
* **Allowed Users**: contains the list of users that can receive the voice boost. Only visible when Restrict Access is checked, does not have any effect otherwise
* **Range Boost**: sets the amount of extra range added to the player's voice
* **Gain Tweak**: sets the amount of gain adjustment for the player's voice. Be careful not to overuse this setting, it can make players really loud up close
* **Use Zones**: allows usage of the `Presentation Voice Zone` behaviours. They will boost the volume of anyone who enters their trigger area
* **Adjust On Presentation Start**: adjusts the voice of the speaker when they start the presentation, and removes the voice boost when they stop or lose ownership of the Presentation Player

#### Events

* **HandleTalkStart**: boosts the current speaker's voice if the Adjust On Presentation Start is checked, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: removes the current's speaker's voice boost if the Adjust On Presentation Start is checked, meant to be used in the OnTalkEnd callback of the Presentation Player
* **NormalizeVolume**: same as HandleTalkEnd, but is meant to be used in the OnTakenControl callback of the Presentation Player
* **EnterZone(VRCPlayerApi player)**: boosts the voice of the provided player if Use Zones is checked
* **ExitZone(VRCPlayerApi player)**: removes the voice boost of the provided player if Use Zones is checked

### Presentation voice Zone

*Detects players entering / exiting the zone and calls voice adjustment events on the `PresentationVoiceController`*

{% hint style="warning" %}
This behaviour requires a trigger collider, and is recommended to be put on a `MirrorReflection` layer. It will warn you about those things, so you don't miss it
{% endhint %}

#### Parameters

* Presentation Voice Controller: a reference to the `PresentationVoiceController` in your scene

### Presentation Stream Controller

*Provides functionality for capturing the presentation*

#### Parameters

* **Restrict Access**: specifies whether only particular users will be able to toggle the stream overlay
* **Allowed Users**: contains the list of users that can open the stream overlay. Only visible when Restrict Access is checked, does not have any effect otherwise
* **Engage Key**: defines the hotkey for opening the stream overlay
* **Stream Objects**: these objects will be toggled when users presses the Engage Key
* **Stream Camera**: the camera that captures the stream UI
* **Stream Animator**: the stream UI animator which controls Idle/Active states
* **Stream Spot**: the position to which the capture player will be teleported to provide best audio capture position
* **Reset Position**: the position to which the capture player will be teleported when they click the Engage Key again - closing the stream overlay
* **Talk Title**: the UI text element with the presentation title on the stream overlay
* **Talk Author**: the UI text element with the presentation author on the stream overlay

#### Events

* **HandleTalkStart**: sets the presentation title and author, and sets the stream UI animator params, meant to be used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: sets the stream UI animator params, meant to be used in the OnTalkEnd callback of the Presentation Player
* **ToggleStreamObjects**: toggles the objects specified in the Stream Objects list and teleports the local player to the Stream Spot

### Presentation Personal Screen

*Controls the spawnable screen attendees can use to get a better view of the presentation slides and the speaker*

#### Parameters

* **Screen Objects**: a list of objects to be enabled or disabled when user executes the personal screen hotkey / toggle
* **Main Screen**: the personal screen object which will be moved to the Desktop / VR Spawn Ref defined below
* **Stream Controller**: the Presentation Stream Controller that ensures that the correct animator state is set (personal screen shares the same UI capture as the stream overlay)
* **Screen Collider**: the collider on the personal screen, it will get disabled if the user isn't in VR, as it is not a pickup unless the player is in VR
* **Desktop Button**: defines the desktop hotkey used to spawn the personal screen
* **Desktop Spawn Ref**: the position at which to spawn the personal screen on desktop. Should be attached to a player-tracked object somewhere in front of the player so they can easily see it
* **VR Spawn Ref**: the position at which to spawn the personal screen in VR. Should be attached to a player-tracker object somewhere in front of the player so they can easily pick it up

### Laser Pointer

*Provides a basic laser pointing functionality that will project a laser dot onto a surface*

#### Parameters

* **Laser Line**: the liner renderer to be used for the laser line
* **Laser Dot**: the object to be placed on the surface of the hit object
* **Active**: whether the laser pointer is enabled (mostly used internally)
* **Hit Layers**: the list of layers to be considered for collision

{% hint style="danger" %}
The following components are purely internal and are not meant to be modified by the user
{% endhint %}

### Presentation Video Player

*Controls the `VRCUnityVideoPlayer`*

#### Parameters

* V Player: the VRCUnityVideoPlayer to control
* Curr Url: the currently loaded video url

#### Events

* **LoadAndSeek(float time)**: loads the video on the Curr Url, retries if fails or gets rate limited, then seeks the video to the specified time

### Presentation Camera Target

*Tracks the current speaker's position to serve as a camera target*

#### Parameters

* Source: specifies which tracking source to use

#### Events

* **TakeOwnership**: takes ownership of the camera target, the owner will then be used as a tracking target
* **HandleTalkStart**: sets the target of the PersentationCameraTarget to the current owner and enables tracking, meant to used in the OnTalkStart callback of the Presentation Player
* **HandleTalkEnd**: stops the tracking, meant to be used in the OnTalkEnd callback of the Presentation Player


# Attributes Overview

A collection of attributes to create custom UIs for your own behaviours

{% hint style="info" %}
Udon Toolkit is a system of C# attributes which can be utilized to build highly customizable and responsive Inspectors for your behaviours, providing better user/dev experience to anyone using your code
{% endhint %}

Not sure why do you need better inspectors? Check out my TLX Talk!

{% embed url="<https://www.youtube.com/watch?v=R0oYziYlVKg>" %}

If you do not know what C# attributes are and how to use them in your Unity code, [this might be a good guide to start with.](https://www.youtube.com/watch?v=9udeBeQiZSc)

When you're feeling like you are ready to use them, [check out the attributes list](/v0.x/attributes/attributes-list)


# Attributes List

A collection of attributes to create custom UIs for your own behaviours

## Class Attributes

The following attributes are only available to use with your behaviour classes.

### CustomName

`[CustomName(string name)]`

Sets a custom name shown in the header bar, otherwise the header bar is hidden.

```csharp
[CustomName("My Fancy Controller")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {}
```

![](https://cdn.vrchat.sh/ut/Custom%20Name.png)

### HelpMessage

`[HelpMessage(string message)]`

Displays a box with some text below the controller's header bar and above the rest of the UI.\
Usually used to describe the purpose of the behaviour if it is not obvious, or to warn user about anything in particular.

```csharp
[HelpMessage("This behaviour should be enabled at all times")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {}
```

![](https://cdn.vrchat.sh/ut/Help%20Message.png)

### OnBeforeEditor

`[OnBeforeEditor(string methodName)]`

Calls the specified method every editor update loop before all the editor code was executed. The `SerializedObject` is passed to your method as a parameter which you can use to perform any necessary modifications.

```csharp
[OnBeforeEditor("BeforeEditor")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {
  public void BeforeEditor(SerializedObject obj) {
    // do stuff
  }
}
```

### OnAfterEditor

`[OnAfterEditor(string methodName)]`

Calls the specified method every editor update loop after all the editor code was executed. This will contain all the latest values before they are saved into the object. The `SerializedObject` is passed to your method as a parameter which you can use to perform any necessary modifications.

```csharp
[OnAfterEditor("AfterEditor")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {
  public void AfterEditor(SerializedObject obj) {
    // do stuff
  }
}
```

### OnValuesChanged

`[OnValuesChanged(string methodName)]`

Calls the specified method whenever any value has been changed by the user in the inspector. The `SerializedObject` is passed to your method as a parameter which you can use to perform any necessary modifications.

> If you need to react to a specific value change - use a field-level [`OnValueChanged` attribute](/v0.x/attributes/attributes-list#onvaluechanged)

```csharp
[OnValuesChanged("ValuesChangeHandler")]
public class MyUdonSharpBehaviour : UdonSharpBehaviour {
  public void ValuesChangeHandler(SerializedObject obj) {
    // do stuff
  }
}
```

## Field Attributes

These attributes are meant to go on public fields of your Controller. They serve as UI building blocks and provide an ability to react to value changes, for example if you would like to do something specific in the scene when user checks a checkbox.

{% hint style="info" %}
**Note**: these attributes use a property modifier approach, as unity doesn't allow stacking multiple property drawers otherwise. Make sure to use them in combination with a `[UTEditor]` or an `[UTEditor]` attribute, or they will not do anything. Both of those should go last in the chain, right before the `public` keyword.
{% endhint %}

### **Attribute Order**

Some attributes affect whether the field is displayed or not, for those - order of the attributes is important. Attributes are evaluated in reverse - from last (the one before the `public`/`private` keywords) to first. You can manually adjust the order of the attribute without changing its actual position by adding an extra `int` parameter at the end. Most attributes will respect the visibility value passed to them and hide themselves. Here's an example.

```csharp
// This will not hide the SectionHeader but will hide the field itself
[HideIf("@someBool")]
[SectionHeader("Test")]
[UTEditor]
public float foo;

// This will hide the SectionHeader and the field itself
[SectionHeader("Other Test")]
[HideIf("@someBool")]
[UTEditor]
public float var;

public bool someBool;
```

This kind of behaviour is intentional and is helpful in cases where you only want to hide the field but leave the Section Header for the fields below, etc.

Many of these attributes can be combined to form more elaborate UI systems, so something like this is completely normal

```csharp
[OnValueChanged("HandleChange"]
[HelpBox("Value cannot be negative", "CheckValueValidity")]
[HideIf("@!transition")]
[HideLabel] [UTEditor]
public float duration;
```

### Common Parameters

Some attributes share parameter names. One of such parameters is `methodName`. In most cases you will be able to pass either a method, or a variable name to it (where it makes sense, there are exceptions, and they are mentioned separately).

```csharp
// Will execute `ShouldShowBox` to determine whether the box should be visible
[HelpBox("", "ShouldShowBox")] [UTEditor];
public bool active;

// Must return `bool`
public bool ShouldShowBox() {
  return active;
}
```

To use a variable instead of a method - prefix the variable name with `@`, you can also invert the variable value by adding `!` after that.

```csharp
// Will show if active is `true`
[HelpBox("", "@active")] [UTEditor];
public bool active;

// Will show if active is `false`
[HelpBox("", "@!active")] [UTEditor];
public bool active;
```

This can be any public variable of a class, doesn't have to be specifically the same variable. You can use something like `[HideInInspector]` for storing the state of the box without displaying it in the editor.

### SectionHeader

`[SectionHeader(string title)]`

Displays a visual separator box with some text

```csharp
[SectionHeader("General")][UTEditor]
public Transform sourceTransform;
```

![](https://cdn.vrchat.sh/ut/Section%20Header.png)

### HelpBox

`[HelpBox(string message, [string methodName])]`

Displays a box with a help message. You can conditionally hide and show the box based on a method or variable. See [Common Parameters](/v0.x/attributes/attributes-list#common-parameters) for more info.

```csharp
// Always visible
[HelpBox("This option disables rotation transfer")]
[UTEditor]
public bool ignoreRotation;

// Only visible if provided variable is set to `true`
[HelpBox("This option disables rotation transfer", "@ignoreRotation")]
[UTEditor]
public bool ignoreRotation;

// Only visible if provided variable is set to `false`
[HelpBox("This option disables rotation transfer", "@!ignoreRotation")]
[UTEditor]
public bool ignoreRotation;

// Only visible if the provided method returns `true`
[HelpBox("This option disables rotation transfer", "ShowIgnoreHelp")]
[UTEditor]
public bool ignoreRotation;

public bool ShowIgnoreHelp() {
  return ignoreRotation;
}
```

![](https://cdn.vrchat.sh/ut/Help%20Box.png)

### HideIf

`[HideIf(string methodName)]`

Hides the field based on the provided method or variable. See [Common Parameters](/v0.x/attributes/attributes-list#common-parameters) for more info.

```csharp
public bool enableEffects = true;

// Hides the field if `enableEffects` is unchecked
[HideIf("@!enableEffects")]
[UTEditor]
public float effectsDuration;

public bool skipTransition;

// Hides the field if `skipTransition` is checked
[HideIf("This option disables rotation transfer", "@skipTransition")]
[UTEditor]
public float transitionDuration;

// Hides the fields if the provided method returns true
public bool enableEffects;
public float effectsDuration;

[HideIf("HideExtras")]
[UTEditor]
public bool extraOption1;

[HideIf("HideExtras")]
[UTEditor]
public bool extraOption2;

public bool HideExtras() {
  return enableEffects && effectsDuration > 0;
}
```

### HideLabel

`[HideLabel]`

Simply hides the field label and draws the value field only. Helpful in combinations with things like `Horizontal` attribute

```csharp
// Will show the checkbox only
[HideLabel] [UTEditor]
public bool active;
```

### OnValueChanged

`[OnValueChanged(string methodName)]`

Calls provided method when the value of the variable changes, so you can react to it in the UI.\
Has some special behaviour when used together with [ListView](/v0.x/attributes/attributes-list#listview).

{% hint style="warning" %}
Due to limitations of Udon - the use of **#if !UDONSHARP\_COMPILER && UNITY\_EDITOR** is required for the handler methods
{% endhint %}

For regular fields the method signature should look like `public void MethodName(SerializedProperty value)`

```csharp
// Will call the provided method with the fresh value every time it changes
[OnValueChanged("ToggleLights")] [UTEditor]
public bool lightsActive;

// Incoming value will be the new value, while the current variable will not be updated yet.
// This allows you to compare old and new values.
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void ToggleLights(SerializedProperty value) {
  // cast to the expected type first
  var val = value?.boolValue;
  if (value) {
    // do something here
  } else {
    // do something here
  }
}
#endif
```

For array type fields the method signature is `public void MethodName(SerializedProperty value, int index)`

```csharp
[OnValueChanged("HandleArrayChange")] [UTEditor]
public string[] namesList;

#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleArrayChange(SerializedProperty value, int index) {
  // if value is `null` - the value at `index` was removed
  if (value == null) {
    // handle removal
    return;
  }
  // if `index` is out of range of the current array - a new value was added
  if (index == namesList.length) {
    // handle addition of new value
    return;
  }
  // handle change of an existing value
}
#endif
```

When used with `ListView` the method signature should be different: `public void MethodName(object lValue, object rValue, int index)`, where `lValue` and `rValue` represent the left and right variable of the list view at the changed index.

You only need to attach `[OnValueChanged]` to the first instance of a particular `ListView`.

```csharp
[OnValueChanged("HandleChange")
[ListView("Events List")][UTEditor]
public UdonBehaviour[] udonTargets;

[ListView("Events List")][UTEditor]
public string[] udonEvents;

// Handle a change of row values
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleChange(SerializedProperty lValue, SerializedProperty rValue, int index) {
  // when the value is removed - both will be null
  if (lValue == null && rValue == null) {
    // handle the removal of a row at `index` here
    return;
  }
  // when the value is added - `index` will be out of range of the current list
  if (index == udonTargets.Length) {
    // handle addition of new row
    return;
  }
  var lCasted = (UdonBehaviour) lValue?.objectReferenceValue;
  var rCasted = rValue?.stringValue;
  // handle change of an existing values
}
#endif
```

If you wish to get a full value instead of just the changed elements in both Array types and the `ListView` powered blocks, you will need to use a different signature.

* For regular arrays: `public void MethodName(SerializedProperty[] value)`
* For `ListView` blocks: `public void MethodName(SerializedProperty[] lValue, SerializedProperty[] rValue)`

If you also want to update some other value on the same object inside the change handler function - you need to accept an incoming `SerializedObject` as well

* For regular arrays: `public void MethodName(SerializedObject obj, SerializedProperty[] value)`
* For `ListView` blocks: `public void MethodName(SeriazliedObject obj, SerializedProperty[] lValue, SerializedProperty[] rValue)`

You can then use it to update properties like `obj.FindProperty("someProp").floatValue = 0.01f`

```csharp
[OnValueChanged("HandleArrayChange")] [UTEditor]
public string[] namesList;

// Log current array value as `Values: array[0], array[1]...`
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleArrayChange(SerializedProperty[] value) {
  var casted = value.ToList();
  // don't forget to grab the value of needed type or cast it from i.objectReferenceType as <yourTargetType>
  Debug.LogFormat("Values: {0}", string.Join(", ", casted.Select(i => i.floatValue).ToArray()));
}
#endif

// Works for `ListView too
[OnValueChanged("Hand`leChange")
[ListView("Events List")][UTEditor]
public UdonBehaviour[] udonTargets;

[ListView("Events List")][UTEditor]
public string[] udonEvents;

// Log current `ListView` value as `Event: udonEvents[i], Target udonTargets[i].name`
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleChange(SerializedProperty[] lValue, SerializedProperty[] rValue) {
  var cLeft = leftVal.ToList();
  var cRight = rightVal.ToList();
  var eventNames = new List<string>();
  for (int i = 0; i < cLeft.Count; i++) {
    var behName = cLeft[i].objectReferenceValue != null
      ? (cRight[i].objectReferenceValue as UdonBehaviour).name
      : "null";
    eventNames.Add($"Event: {cRight[i].stringValue}, Target: {behName}");
  }
  Debug.Log(string.Join("\n", eventNames));
}
#endif

[OnValueChanged("HandleSliderChange")] [RangeSlider(0, 2) [UTEditor]
public float someFloat = 0.2f;

public bool someBoolProp;

// Modify another property in response to a value change
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void HandleSliderChange(SerializedObject obj, SerializedProperty value) {
  obj.FindProperty("someBoolProp").boolValue = value.floatValue > 1;
}
#endif
```

{% hint style="info" %}
Check CustomUISample.cs for live examples!
{% endhint %}

### Horizontal

`[Horizontal(string groupName)]`

Combines two fields into a horizontal group based on the provided name.\
You can define variables in any order, they can even have other variables between them. The fetching is done purely by the group name.

```csharp
// When using object types its pretty common to use a `[HideLabel]` attribute to make the UI cleaner
[Horizontal("Group")] [HideLabel] [UTEditor]
public GameObject varA;

// Only 2 variables per group are supported at this time
[Horizontal("Group")] [HideLabel] [UTEditor]
public string varB;

// You can have many groups in a controller
[Horizontal("OtherGroup")] [Udon Public]
public int varC;

[Horizontal("OtherGroup")] [Udon Public]
public int varD;
```

![](https://cdn.vrchat.sh/ut/Horizontal%20Group.png)

### ListView

`[ListView(string name, [string addMethodName], [string addButtonText])]`

Combines two arrays into a connected list of elements.

This is a cornerstone attribute of UdonToolkit. Due to dictionaries not being exposed in Udon we have to split things into separate arrays which makes navigating logically connected pieces of data very annoying, as well as forcing you to manually keep track of having enough elements in both arrays.

ListView covers that use case and provides some extras when combined with the [Popup attribute](/v0.x/attributes/attributes-list#popup).

```csharp
[ListView("Udon Events List")] [UTEditor]
public UdonBehaviour[] udonTargets;

[ListView("Udon Events List")] [UTEditor]
public string[] udonEvents;
```

![](https://cdn.vrchat.sh/ut/List%20View%20Basic.png)

You can provide a custom add method to have full control on how the arrays are populated. It is also a good way to create something in the scene, like an instance of a prefab or a basic GameObject and set it as the list value at the same time.

You only need to configure the extra parameters in the first instance of `ListView` for a particular group.

```csharp
// You can also customize the add button text via an extra argument
[ListView("Udon Events List", "AddEvent", "Add new Event")] [UTEditor]
public UdonBehaviour[] udonTargets;

// No need to define extra configuration here, the first instance of `ListView` for that group name is going to be used
[ListView("Udon Events List")] [UTEditor]
public string[] udonEvents;

// The addition is fully relegated to your custom method, you can do whatevery you find suitable in here
public void AddEvent() {
  var newTargets = udonTargets.ToList();
  newTargets.Add(null);
  udonTargets = newTargets.ToArray();

  var newEvents = events.ToList();
  newEvents.Add($"NewEvent_{events.Length}");
  events = newEvents.ToArray();
}
```

![](https://cdn.vrchat.sh/ut/List%20View%20Custom.png)

Combining it with a `[Popup]` attribute is the way this is used the most across Udon Toolkit's behaviours. Creating list of Udon events and Animator triggers becomes a matter of a single line of code that handles everything behind the scenes.

```csharp
[ListView("Udon Events List")] [UTEditor]
public UdonBehaviour[] udonTargets;

// You can combine many attributes together, here we use `Popup` to automatically populate the events list for us
[ListView("Udon Events List")]
[Popup("behaviour", "@udonTargets", true)]
public string[] udonEvents;
```

![](https://cdn.vrchat.sh/ut/List%20View%20Popup.png)

You can read more about `[Popup]` and how it can be populated [right here](/v0.x/attributes/attributes-list#popup)

### RangeSlider

`[RangeSlider(float min, float max)]`

Provides a slider to control the value in a predefined range, supports both floats and ints.

```csharp
[RangeSlider(1, 10)] [UTEditor]
public float floatValue;

// If the value is int - slider will auto snap to only int values
[RangeSlider(3, 20)] [UTEditor]
public int intValue;
```

![](https://storage.googleapis.com/cdn.vrchat.sh/ut/Range%20Slider.png)

### Popup

`[Popup(string methodName)]`

Shows a popup with options to choose from and support for multiple sources of data. Only supported on string variable types at this time.

You can provide a method, or a variable, to populate the popup options list. See [Common Parameters](/v0.x/attributes/attributes-list#common-parameters) for more info.

```csharp
// Use a variable to populate the popup options
[SectionHeader("Popups")] [Popup("@popupOptions")] [UTEditor]
public string popupVar;

[NonSerialized] public string[] popupOptions = {"foo", "bar", "fizz", "buzz"};

// You can also use a method to calculate options dynamically
[SectionHeader("Popups")] [Popup("GetOptions")] [UTEditor]
public string popupVar;

public string[] GetOptions() {
  return new [] { "foo", "bar", "fizz", "buzz" };
}

// The method can accept a serialized property if you want to dynamically generate the options
#if !COMPILER_UDONSHARP && UNITY_EDITOR
public string[] GetOptions(SerializedProperty prop) {
  return new [] { $"options for {prop.name}" };
}
#endif
```

![](https://cdn.vrchat.sh/ut/Popup%20Basic.png)

{% hint style="danger" %}
**PopupSource** and **ShaderPropTypes** are both DEPRECATED since UdonToolkit v0.4.0, documentation is provided for legacy reasons
{% endhint %}

**DEPRECATED** `[Popup(PopupSource sourceType, string methodName, [[ShaderPropType shaderPropType], [bool hideLabel]])]`

`[Popup(PopupSource sourceType, string methodName, [[ShaderPropType shaderPropType], [bool hideLabel]])]`

By providing an explicit `sourceType` other than `Method` you can use the built-in Toolkit's ability to fetch a list of Animator Triggers, Udon Behaviour Custom Events or Shader Properties of a particular type. If there are no triggers or events available - the UI will inform you about it.

There are plans to add more sources in the future, like Material properties and other native elements.

You can combine this with [`[ListView]` attribute](/v0.x/attributes/attributes-list#listview) to achieve dictionary-style results for Udon Behaviours. When used inside `ListView` - the popup method will be called with the `Serialized Property` of the current element on the opposing array (see example below).

```csharp
// a namespace to work with Lists
#if !COMPILER_UDONSHARP && UNITY_EDITOR
using System.Collections.Generic;
#endif
// We use `[Horizontal]` to visually connect the source and the popup
[Horizontal("AnimationTrigger")] [HideLabel] [UTEditor]
public Animator animator;

// Using an Animator source type to auto fetch Triggers from the `animator` variable
[Horizontal("AnimationTrigger")] [Popup("animator, "@animator", true)] [UTEditor]
public string somePopupVar;

[Horizontal("BehaviourTrigger")] [HideLabel] [UTEditor]
public UdonBehaviour behaviour;

// Using an UdonBehaviour source type to auto fetch Custom Events from the `behaviour` variable
[Horizontal("BehaviourTrigger")] [Popup("behaviour", "@behaviour", true)] [UTEditor]
public string behaviourPopupVar;

[Horizontal("ShaderProperty")] [HideLabel] [UTEditor]
public Shader shader;

// Using a Shader source type to auto fetch `float` Shader Properties from the `shader` variable
[Horizontal("ShaderProperty")] [Popup("shader", "@shader", true)] [UTEditor]
public string shaderPopupVar;

// You can also specify which type of shader proeprty to grab
[Popup("shader", "@shader","vector", false)] [UTEditor]
public string shaderPopupVectorVar;

[ListView("Test List")]
public Transform[] transforms;

[ListView("Test List")]
[Popup("GetChildrenOptions")]
public string[] selectedChildren;

#if !COMPILER_UDONSHARP && UNITY_EDITOR
// prop in this case is the element of `transforms` array on the current index
public string[] GetChildrenOptions(SerializedProperty prop) {
  var transObj = (Transform) prop.objectReferenceValue;
  if (transObj == null) {
    return new[] { "-- no transform set --"};
  }
  var childList = new List<string>();
  for (int i = 0; i < transObj.childCount; i++) {
    var child = transObj.GetChild(i);
    childList.Add(child.name);
  }

  return childList.ToArray();
}
#endif
```

![](https://cdn.vrchat.sh/ut/Popup%20Custom%20Source.png)

### Toggle

`[Toggle([string label])]`

Toggle-type button for boolean fields.

Combines well with things like [`[HideIf]` attribute](/v0.x/attributes/attributes-list#hideif) to toggle parts of the UI on and off to hide things that are unused in a particular toggle state.

```csharp
// Use the variable name as the toggle label
[Toggle] [UTEditor]
public bool addSFX;

// Combine with [HideIf] to hide a variable that is irrelevant if the toggle is `false`
[HideIf("@!addSFX")] [Udon Public]
public float someThirdVar;

// Use a custom toggle label
[Toggle("Custom Label")] [UTEditor]
public bool extraToggle;
```

![s](https://cdn.vrchat.sh/ut/Toggle.png)

### UTEditor

`[UTEditor]`

A helper attribute that allows UdonToolkit's stackable Attribute system to works. Most attributes won't work without it.

{% hint style="info" %}
**TL;DR:** Unity doesn't like having multiple `PropertyDrawer` attributes, so UdonToolkit technically tries to behave like a single `PropertyDrawer` with a bunch of modifiers stacked on top. Due to that the modifiers need a "base" to work off, `[UTEditor]` is that base.[ I have a full talk that goes over it](https://www.youtube.com/watch?v=R0oYziYlVKg)
{% endhint %}

Has no visual effects by itself.

```csharp
// This will display a section header
[SectionHeader("General")] [UTEditor]
public bool active = true;
```

### Disabled

`[Disabled([string methodName])]`

Disables the editing of the provided field completely, or based on the provided `methodName`. Useful when manually populating fields via editor scripts (like with `OnValueChanged`) or in runtime.

When used with `ListView` it should be put on the first field in the list view.

```csharp
// Make a field read only
[Disabled]
public bool toggleThings;

// Make a field read only based on a method return value
[Disabled("GetDisabled")]
public float disabledFloat;

public bool GetDisabled() {
  return gameObject.activeSelf;
}

public bool allowEditing;

// Make a list view read only
[ListView("Stuff")][Disabled("@!allowEditing")][UTEditor]
public Transform[] objects;

[ListView("Stuff")][UTEditor]
public string[] objectDescriptions;
```

## Method Attributes

These attributes are meant to be added to your public methods and are handled separately. The UI for these is rendered after the main inspector is done, so they will always be at the bottom.

### Button

`[Button(string text, [bool activeInEditMode])]`

Displays a button that calls the specified method, useful for in-editor testing.

The main use case here is to expose your custom events as buttons in the inspector or provide some extra functionality to speed up iteration process.

Currently, these buttons are disabled unless you enter play mode.

```csharp
// Creates and Interact button to trigger the builtin Udon Interact event
[Button("Interact")]
public void Interact() {
  // UTController base class stores the currently attached UdonBehaviour here
  if (uB == null) return;
  uB.Interact();
}

// Creates a button that will send a custom event to your behaviour on click
[Button("Activate")]
public void Activate() {
  if (uB == nul) return;
  uB.SendCustomEvent("Activate");
}

// Creates a button that will be clickable in edit mode
[Button("Editor Action", true)]
public void DoEditorStuff() {
  Debug.Log("We're doing editor stuff!");
}
```

![](https://cdn.vrchat.sh/ut/Button.png)


# Guides Overview

Udon Toolkit might be a bit weird to use at first, so here are some examples of real-world things you might want to build with it.

The basic principle of most things in the Toolkit is that there are `Triggers` and `Actions`.\
`Triggers` react to player's input and call `Actions` to do things in the world, if you think about Toolkit in those terms - you should be up and running in notime!


# Mirror Toggle

An example of setting up a mirror toggle via UI Button

* Add a VRC Mirror prefab, disable it to save performance by default
* Add a `Universal Action` to the Mirror object
* Make sure `Active` is checked, check `Fire Object Toggles`, click on the `Game Objects List` and click `Add Element`
* Drag your Mirror object into the empty field, select `Toggle` in the dropdown on the right

![](https://cdn.vrchat.sh/ut/ub/universal_action_mirror_toggler.png)

* Add a `UI -> Button` to your scene, set the `Render Mode` to `World Space` and scale it way down and place it where you want
* Add a `Ui Shape` component to the Canvas
* Change canvas layer to `Default`
* Click on your Button inside the Canvas and add a new `On Click()` event by clicking the small `+` icon in the bottom right
* Drag and Drop your Mirror object into the empty field that appeared
* In the dropdown next to `Runtime Only` select `UdonBehaviour -> SendCustomEvent` (its all the way at the bottom)
* Type `Trigger` in the text field

![](https://cdn.vrchat.sh/ut/ub/universal_action_ui_button.png)

You're done! Now you can toggle your mirror via UI button in Udon.

{% hint style="warning" %}
The major thing to note here is to make sure that the component, and the game object its on, is always enabled if you're using the `Delay` parameter, otherwise it will never fire as the Update method will never be called.
{% endhint %}


# Migration to v0.4.0

{% hint style="warning" %}
Since version 0.4.0 UdonToolkit requires UdonSharp 0.18 and above!
{% endhint %}

### Migration Guide

* Grab the [latest U# release here](https://github.com/MerlinVR/UdonSharp/releases) and import that first!
* Create a new empty scene and open it
* Click File -> Save Project
* Download the [latest UdonToolkit release here](https://github.com/orels1/UdonToolkit/releases)
* Remove the UdonToolkit folder completely while you are in that test scene
* Import the new UdonToolkit and wait for everything to compile
* Open `Edit` -> `Project Settings` -> `UdonSharp` and in the `Default Behaviour Editor` select `UdonToolkit Editor`
* Open your scene again and everything should work as expected

{% hint style="warning" %}
If you are using the **CameraSystem** - it is recommended to check that the `Start Sphere` and all the `Sphere` objects inside `VR Controls` have `Area Trigger` program assets assigned, unity seems to occasionally lose references to that particular asset
{% endhint %}

You will notice a `Legacy` folder inside UdonToolkit now which hosts all the old controllers and the legacy editor

### Controller Migration Guide

{% hint style="info" %}
You can take a look at all the U# behaviours in the **Misc** folder to see how you can use UdonToolkit now
{% endhint %}

Starting with v0.4.0 you don't need controllers anymore, and you can use all the UdonToolkit's attributes directly in the U# code!

There are some small differences mainly centered around Udon's limited support for things like Enums and cases where you want to perform some complex editor logic.

> All variables that you want to use UdonToolkit attributes with must have a `[UTEditor]` attribute on them that is last in the group

```csharp
// This will work
[SectionHeader("General")] [UTEditor]
public bool active = true;

// This won't work
[SectionHeader("Some other group")]
public float test = 0.01f;
```

This is due to some limitations of unity PropertyDrawers that do not allow proper modifier stacking

{% hint style="info" %}
Popup attribute now has new udon-friendly versions that use string names instead of Enums
{% endhint %}

```csharp
// Using an Animator source type to auto fetch Triggers from the `animator` variable
[Horizontal("AnimationTrigger")] [Popup("animator, "@animator", true)] [UdonPublic]
public string somePopupVar;
```

More examples in the [Popup attribute documentation](https://github.com/orels1/UdonToolkit/wiki/Attributes#popup)

{% hint style="danger" %}
If you want to execute editor only code in case of OnValueChanged, etc. you need to use directives
{% endhint %}

It is pretty rare that you'll have to use `#if`, but sometimes its unavoidable, especially when you want to use things like [OnValueChanged](https://github.com/orels1/UdonToolkit/wiki/Attributes#onvaluechanged) attribute.

Basically the logic behind this is as follows: you should use `#if !COMPILER_UDONSHARP && UNITY_EDITOR` if:

* You want to work with methods and types not exposed to udon
* You want to execute some logic in editor outside of playmode

In other cases - you should never need `#if` around your attribute related code

```csharp
[OnValueChanged("ToggleLights")] [UTEditor]
public bool lightsActive;

#if !COMPILER_UDONSHARP && UNITY_EDITOR
public void ToggleLights(SerializedProperty value) {
  // get the value of expected type first
  var val = value?.boolValue;
  if (value) {
    // do something here
  } else {
    // do something here
  }
}
#endif
```

As usual, check behaviours in the `Misc` folder as a reference to how things should be built. [UniversalAction](/v0.x/behaviours/misc-behaviours#universal-action) is a good overall example.


# UT Controllers

Create custom behaviour UIs for your own code

UdonToolkit is not just a set of prebuilt behaviours, but also a UI system built using Unity's Custom Property Drawers and Attributes that allows you to quickly create a user-friendly UI.

{% hint style="danger" %}
As of UdonToolkit v0.4.0 (with UdonSharp v0.18.0+) the Controllers are **DEPRECATED** you can now use the Attributes directly in UdonSharp code!
{% endhint %}

#### Check the [Migration to v0.4.0 docs](/v0.x/extras/migration-to-v0.4.0#migration-guide)

If you're not familiar with C# Attributes, I highly recommend [taking a look at this](https://docs.unity3d.com/Manual/Attributes.html).

But the tl;dr looks like this:

* Look through the list of available Attributes in the Sidebar
* Find something that seems interesting to you
* Add `using UdonToolkit` to the top of your U# behaviour code
* Add it to your field alongside a `[UTEditor]` attribute that should always be last in the list
* Enjoy the perks of UdonToolkit UI system!

[Full list of attributes is available here](https://github.com/orels1/UdonToolkit/wiki/Attributes)


