UMA Considerations for Runtime Setup

NOTE: Crazy Minnow Studio does not provide support for UMA or other 3rd party integration systems. If you have difficulties with UMA, please utilize the UMA support options for assistance.

Ensure you are familiar with the following documentation:


UMA is bit different to work with due to the way it builds the character at runtime. The DynamicCharacterAvatar (DCA) resolved some of the issues of trying to ease the burden of working with a character system that was difficult or impossible to create a prefab from, but still has its nuances.

If the desire is to simply wire up an UMA character at runtime it is not much different from the workflow described above. However, if an UMA character is wired up and the designer wishes to take advantage of the ability to change the character at runtime (while SALSA Suite is running) there are a couple of considerations to take into account.

Be aware, UMA has many different options and can be hooked up in many ways. The following is an example of how we work with the DCA object and may not work in all situations.

SALSA Suite Cleanup

If any component in the SALSA Suite is running, there are some housekeeping tasks to accomplish. SALSA and EmoteR implement the same mechanism to stop/start their respective modules (EmoteR has one additional requirement). Eyes itself consists of 4 sub-modules and uses a slightly different mechanism to accomplish the shutdown and startup tasks. This is mainly because the sub-modules can be shut off independently.

In addition to shutting the modules down, if using a system like UMA where it is possible to instantly destroy the character, but not the character root object where the SALSA Suite lives, the QueueProcessor can be left with a bunch of missing components, creating null-references. Fortunately, there is a mechanism for dealing with QueueProcessor cleanup as well.

Stop the Running Modules

Stop the SALSA and EmoteR modules (use the normal Unity component enable/disable mechanism) and pay close attention to the slight difference in stopping the Eyes module.

Cleaning up the QueueProcessor must always occur after stopping the main Suite modules. Otherwise, they (SALSA, EmoteR, Eyes) may continue to register components into the QueueProcessor.

GameObject salsaSuiteObject;
salsaSuiteObject.GetComponent<Salsa>().enabled = false;
salsaSuiteObject.GetComponent<Emoter>().enabled = false;
salsaSuiteObject.GetComponent<Eyes>().EnableAll(false);

// Clear the queues...
var qp = salsaSuiteObject.GetComponent<QueueProcessor>();
qp.priorityQueueHeads.Clear();
qp.priorityQueueEyes.Clear();
qp.priorityQueueLipsync.Clear();
qp.priorityQueueEmotes.Clear();
qp.priorityQueueBlinks.Clear();
qp.priorityQueueLids.Clear();

NOTE: In the next version of SALSA Suite v2, the QueueProcessor will gain a new option to flush the queues: QueueProcessor.Flush()

Re-Start the Modules

Before you implement the workflow described earlier in this article to apply your character configuration to your model avatar, ensure you restart the required modules. Also, be sure to do this after the character avatar has been rebuilt. In UMA, you should leverage one of the callback methods available to the UMA DCA system. Please refer to UMA documentation and UMA support mechanisms for more details or questions. Crazy Minnow Studio does not provide support for the UMA product.

NOTE: It is not technically necessary to start the Eyes sub-modules back up when using the UMA OneClick, since it will do this automatically. However, if using a different system this may be required.

// The following should be run after UMA has notified you the avatar is ready.
GameObject salsaSuiteObject;
salsaSuiteObject.GetComponent<Salsa>().enabled = true;
salsaSuiteObject.GetComponent<Emoter>().enabled = true;
salsaSuiteObject.GetComponent<Eyes>().EnableAll(true);

Considerations for Configuring the UMA Avatar

As mentioned above, setting up an UMA character that was running and is instantly rebuilt has some nuances from the normal runtime setup workflow. The avatar was linked up and running and now has been stopped, most likely destroyed, and rebuilt. Having been rebuilt, some of the systems SALSA Suite uses to provide its functionality need to be re-linked -- namely the UMA Expression Player (UEP).

After the Suite modules have been re-enabled and the UMA OneClick setup reapplied, it is necessary to re-configure the UMA OneClick UmaUepDriver component. This is a simple one-line call that re-links the UEP and also re-links the bones used by Eyes for head and eye animation.

NOTE: This call requires the UMAData structure be passed to it for proper configuration. This means it is necessary to have a link to the already-built avatar. It is preferred to use one of the UMA DCA callbacks to run your configuration script since it provides the data in the callback and you can be sure UMA is ready for you to work with the completed/updated chacater avatar.

GameObject salsaSuiteObject;
salsaSuiteObject.GetComponent<UmaUepDriver>().CharacterCreated(umaData);

IMPORTANT:
Now you can continue to setup your avatar as you would any other runtime character except for one small difference. Since the UmaUepDriver component configuration was called previously, the Eyes.Initialize() method was automatically called. Therefore, you should not call this function again when setting up your avatar. It won't harm anything, but it will build another set of calculation gizmos in your scene, creating objects that are unnecessary. The next SALSA Suite update will fix this issue. See the next section for a wrap-up of all of the above code snippets.

Code Example -- Putting It All Together (UMA)

The following is provided as a mostly-complete set of the above example code for re-wiring an UMA avatar that is built and running SALSA Suite and is then changed and rebuilt by UMA:

// Called by some function or functionality in your application to 'change'
// the UMA avatar.
public void ReconfigureRunningUma()
{
    Debug.Log("SuiteRelink: Initiated.");
    DisableSalsaSuite(); // see below

    // ...call your UMA 'change' here...change race, recipe, etc.
}

// This method should be called by your UMA callback...i.e. CharacterUpdated, etc.
// NOTE: It requires the UMAData structure to complete the re-setup of your avatar.
private void RelinkSalsaSuite(UMAData umaData)
{
    Debug.Log("SuiteRelink: Re-linking.");
    GameObject salsaSuiteObject;

    // re-enable SALSA Suite
    salsaSuiteObject.GetComponent<Salsa>().enabled = true;
    salsaSuiteObject.GetComponent<Emoter>().enabled = true;

    // OneClick runtime setup...
    AudioClip clip = salsaSuiteObject.GetComponent<AudioSource>().clip;
    OneClickUmaDcs.Setup(salsaSuiteObject.gameObject, null);
    OneClickUmaDcsEyes.Setup(salsaSuiteObject.gameObject);

    // reconfigure the UmaUepDriver...
    salsaSuiteObject.GetComponent<UmaUepDriver>().CharacterCreated(umaData);

    // Bake Expression controllers for SALSA and EmoteR...
    Salsa salsa = salsaSuiteObject.GetComponent<Salsa>();
    salsa.UpdateExpressionControllers();
    Emoter emoter = salsaSuiteObject.GetComponent<Emoter>();
    emoter.UpdateExpressionControllers();
    emoter.UpdateEmoteLists();

    // Bake Expression controllers for Eyes...
    // NOTE: We don't call Eyes.Initialize() here...already applied
    //  in the UmaUepDriver call above.
    Eyes eyes = salsaSuiteObject.GetComponent<Eyes>();
    eyes.UpdateRuntimeExpressionControllers(ref eyes.heads);
    eyes.UpdateRuntimeExpressionControllers(ref eyes.eyes);
    eyes.UpdateRuntimeExpressionControllers(ref eyes.blinklids);
    eyes.UpdateRuntimeExpressionControllers(ref eyes.tracklids);
}

private void DisableSalsaSuite()
{
    Debug.Log("SuiteRelink: Disable Suite components.");
    // disable Suite components to prevent writing to queue.
    salsaSuiteObject.GetComponent<Salsa>().enabled = false;
    salsaSuiteObject.GetComponent<Emoter>().enabled = false;
    salsaSuiteObject.GetComponent<Eyes>().EnableAll(false);

    // clear QueueProcessor
    var qp = salsaSuiteObject.GetComponent<QueueProcessor>();
    qp.priorityQueueHeads.Clear();
    qp.priorityQueueEyes.Clear();
    qp.priorityQueueLipsync.Clear();
    qp.priorityQueueEmotes.Clear();
    qp.priorityQueueBlinks.Clear();
    qp.priorityQueueLids.Clear();
}