nilllzz.dev Blog

Complete website refresh

After refreshing Capri's presence the rest of the website has been updated as well to match the new style.

New design

I've also finally sat down to create a proper blog as part of the website so I can write down my thoughts and findings that no one ever reads.

It still needs some design/engineering work, but it works pretty well right now.

New Capri Website Presence

test

Using TextMesh Pro Gradient Presets with Unity/VRChat

Creating and using TMP Color Gradient Presets in Unity for VRChat is easy enough and works just like every other Unity project.

After creating a gradient preset, you can assign it in the inspector of a TextMesh Pro component.

Project

Inspector

However, it is not possible to dynamically load a gradient preset using inline styling, as VRChat does not include Resources/ folders during compilation.

Example that does not work:

textMeshProComponent.text = "<gradient=\"Header\">This text will not be colored with the gradient preset.</gradient>";

Arrays and FieldChangeCallback

Are you planning on syncing your data in UdonSharp using FieldChangeCallback and want to use arrays in your synced data?

Be aware that the FieldChangeCallback only fires if the length of the array has changed.
If only the contents of the entries of the array change, but the length of remains the same, Udon does not reassign the array reference.
Since FieldChangeCallback only fires on variable reassignment, it does not fire if only the contents of entries of the array change.

Instead, use OnDeserialization.

Example of code that will not fire the callback:

[UdonSynced, FieldChangeCallback(nameof(MyData))]
private int[] _myData = new int[2];
public int[] MyData {
    get => _myData;
    set {
        _myData = value;
        DoSomething();
    }
}

...

// Running on a different player's client:
Networking.SetOwner(Networking.LocalPlayer, gameObject);

// This will not fire the FieldChangeCallback, since the array reference is not changed.
MyData[0] = 1;
RequestSerialization();

// This will also not fire the callback on the remote player's end. The array length is the same and Udon does not reassign the reference.
// It will run the setter on the local player's end, which can make this even more deceiving.
MyData = new int[2] { 2, 3 };
RequestSerialization();

// This will fire the callback because the length is different.
MyData = new int[3] { 1, 2, 3 };
RequestSerialization();

Instead, set it up like this:

[UdonSynced]
public int[] MyData = new int[2];

...

public override void OnDeserialization() {
    DoSomething(); // This will fire whenever the data is updated, regardless of whether the array reference changed or not.
}

...

// Running on a different player's client:
Networking.SetOwner(Networking.LocalPlayer, gameObject);
MyData[0] = 1;
RequestSerialization();

You can alternatively include the setter so the DoSomething() code runs on the object owner's client immediately instead of having to wait for OnDeserialization.

(1) reference

Partial Classes in UdonSharp?

Can you use C# partial classes in UdonSharp without issues?

Test link: test

"Array.Sort" is not exposed to Udon?

Have you ever wanted to use System.Array.Sort in your UdonSharp project and ran into this error message?

Unity error message

Your code probably looked something like this:

var myArray = new int[] { 1, 2, 3 };
System.Array.Sort(myArray);

The error stems from the fact that C# by default assumes you are using Array.Sort<T>(T[]), NOT Array.Sort(Array), the former of which is not exposed to Udon.

To fix this issue, simply upcast your array to System.Array explicitly before calling Sort:

var myArray = new int[] { 1, 2, 3 };
System.Array.Sort((System.Array)myArray);