Creating Custom VTI Drivers

From WaffleSlapper's Project Wiki
Revision as of 08:39, 13 February 2024 by Steeveeo (talk | contribs) (Initial tutorial writeup.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)


The VTI World Kit is designed to be easily extensible by both world designers and programmers alike. To this end, custom Drivers are highly recommended and are built to be fairly simple to create. This article serves as a tutorial on how to create a custom Driver. It will assume basic knowledge of UdonSharp.

In this tutorial, we will be replicating the VTIObjectToggle Driver as it demonstrates all the core tenets of how VTI Drivers function.

Create the Script

Create a new U# script and open it in your IDE of choice. Change the inherited class from UdonSharpBehaviour to VTIDriverBase.

public class CustomDriver : VTIDriverBase

Next, we need our control variables so the script can function. As we are turning a GameObject on for a period of time, and then off after it, add a public property for a GameObject a float for the time it will take to reset. For sanity's sake, also add a call in the Start() method to turn the Target off, to make sure it won't fire when the level loads.

public class CustomDriver : VTIDriverBase
{
	public GameObject Target;
	public float ResetTime = 5.0f;

	void Start()
	{
		Target.SetActive(false);
	}
}

Add Core Functionality

The simplest possible Driver only needs to implement the VTIEventPlay() method. This is what is called when an Event is received, all checks have passed, and VTI is attempting to Fire this Driver's Target.

Add a public override void method called VTIEventPlay(). Clear out the base.VTIEventPlay(); line if your IDE added it automatically.

	public override void VTIEventPlay()
	{
		
	}

Next, we need to make the Driver actually do the thing we want it to do, which is to turn on a GameObject, wait, and then turn it off. The first thing we'll need is to call Target.SetActive(true) in our new method. Then, create another method that will do the exact opposite and call Target.SetActive(false).

	public override void VTIEventPlay()
	{
		Target.SetActive(true);
	}

	public void ResetTarget()
	{
		Target.SetActive(false);
	}

For the core of our functionality, we only need to do one more thing: make VTIEventPlay() call ResetTarget() after a delay. We can do this simply by utilizing the built-in SendCustomEventDelayedSeconds() method. Add that into VTIEventPlay() and have it wait for ResetTime seconds.

SendCustomEventDelayedSeconds(nameof(ResetTarget), ResetTime);

As an aside, note the usage of nameof instead of just putting "ResetTarget" in the first argument. This allows for a measure of safety when maintaining projects later on, as if you need to rename ResetTarget() for whatever reason, you can utilize something like Visual Studio's Rename function and not have to worry about manually typing the name change into all the Custom Event calls.

At this end of this section, this is how our script looks:

using UnityEngine;

public class CustomDriver : VTIDriverBase
{
	public GameObject Target;
	public float ResetTime = 5.0f;

	void Start()
	{
		Target.SetActive(false);
	}

	public override void VTIEventPlay()
	{
		Target.SetActive(true);
		SendCustomEventDelayedSeconds(nameof(ResetTarget), ResetTime);
	}

	public void ResetTarget()
	{
		Target.SetActive(false);
	}
}

Dealing With Readiness

For the most basic of Drivers, the Cooldown timer is automatically handled by VTITarget. However, in this case, we're turning an object on and telling VRChat to turn it back off after an arbitrary delay. In some circumstances, you may want your Drivers to be able to overlap themselves when triggered multiple times. But in this case, we have a Custom Event running on a timer, which can cause some level of havoc if we don't tell the parent VTITarget that we're not ready to Fire again.

There are two ways to do this: The IsReady variable, and adding in a custom VTICheckReady() method. The former method is exceptionally simple, and all we really need for this tutorial, so we'll cover that first.

The IsReady Boolean

The IsReady boolean serves as a bit of a "Green Light" to signal the VTI World Kit that this Driver is ready to go or not. Right now, we need to turn that light "red" while we wait for the Custom Event timer to elapse, and then turn it back to "green" when we're done. To do this, simply set IsReady = false; in VTIEventPlay(), and set it back to true in ResetTarget().

	public override void VTIEventPlay()
	{
		IsReady = false;

		Target.SetActive(true);
		SendCustomEventDelayedSeconds(nameof(ResetTarget), ResetTime);
	}

	public void ResetTarget()
	{
		IsReady = true;

		Target.SetActive(false);
	}

And that's it! By default, VTICheckReady() will simply return the value of IsReady unless overridden, so for the purposes of this script, we don't even need to do anything more than this!

VTICheckReady()

However, what if we need to be more complex in our checks to see if we can Fire again or not? In this case, a simple boolean may not be enough. For the purposes of this tutorial, this step is optional, and will only serve as an example of how this can be done in another way.

There's not really much we need to check in a Driver as simple as this, so let's just be super paranoid about the state of the Target GameObject and ensure that it got turned off before allowing VTI to Fire this again. To do this, first add in an override for VTICheckReady().

Next, replace the auto-generated "base" call (if your IDE made one) with a simple expression to return true if both IsReady is true and Target is not currently active. This can look like the below:

	public override bool VTICheckReady()
	{
		return IsReady && !Target.activeSelf;
	}

Closing

And that's basically it! This Driver can now be attached to a VTITarget like any of the base Drivers that came with the VTI World Kit. At a very rough estimate, there are about seven times more words in this tutorial than there are in all of the base Drivers in the Kit combined. Our simple Driver code should now look like this:

using UnityEngine;

public class CustomDriver : VTIDriverBase
{
	public GameObject Target;
	public float ResetTime = 5.0f;

	void Start()
	{
		Target.SetActive(false);
	}

	public override void VTIEventPlay()
	{
		IsReady = false;

		Target.SetActive(true);
		SendCustomEventDelayedSeconds(nameof(ResetTarget), ResetTime);
	}

	public void ResetTarget()
	{
		IsReady = true;

		Target.SetActive(false);
	}
}

Next Steps

This is not all that can be done with a Driver. To continue on, read the next tutorial: User Messaging On Custom VTI Drivers.