API Example -- EmoteR Runtime Setup (boxHead)

This example code works but is somewhat theoretical and includes configuration for demonstration purposes only. Please check the comments for details and remove the additional expression components applied to the emote expressions if you prefer a more practical end result. Please also change the namespace to your standard/required namespace.

NOTE: See the EmoteR API document for API details.

using UnityEngine;
using CrazyMinnow.SALSA;

namespace DemoCode // change this to your custom namespace...
{
    /// <summary>
    /// The boxHead runtime script is included as an example of runtime 
    /// implementation for EmoteR.
    /// This script creates additional (multiple) components on an emote 
    /// which are for demonstration purposes only and will not produce 
    /// good results if left in place. Please remove these additional components
    /// from the lookDown emote for best results.
    /// </summary>
    public class BoxheadEmoterRuntime : MonoBehaviour
    {
        public static void BoxheadEmoterSetup(GameObject selectedObject)
        {
            var emoter = selectedObject.GetComponent<Emoter>();
            var smr = selectedObject.GetComponent<SkinnedMeshRenderer>();
            var qp = selectedObject.GetComponent<QueueProcessor>();

            // quick test to see if this object has the necessary stuff
            if (smr == null
                || smr.sharedMesh == null
                || smr.sharedMesh.blendShapeCount == 0)
            {
                Debug.Log("This object does not have the required components.");
                return;
            }

            if (emoter == null)
                emoter = selectedObject.AddComponent<Emoter>();

            // setup a new queue processor if we didn't find one. recommend using one
            // queue processor per character (especially advanced characters with many
            // components.
            if (qp == null)
                qp = emoter.gameObject.AddComponent<QueueProcessor>();
            emoter.queueProcessor = qp;

            emoter.emotes.Clear();

            // Add an emote expression...
            // NOTE: we are using boxhead's eye-movement blendshapes as emotes...this isn't practical and is only for
            // demonstration purposes.
            emoter.emotes.Add(new EmoteExpression("lookUp", new InspectorControllerHelperData(), true, false, false, 0f));
            emoter.emotes[0].isRandomEmote = true;
            var emote = emoter.emotes[0].expData; // cache the emote expression...
            // create an expression component for the emote exression...
            emote.components[0].name = "lookUp component";
            emote.components[0].durationOn = .5f;
            emote.components[0].durationHold = .2f;
            emote.components[0].durationOff = .3f;
            emote.controllerVars[0].smr = smr;
            emote.controllerVars[0].blendIndex = smr.sharedMesh.GetBlendShapeIndex("lookUp");
            emote.controllerVars[0].minShape = 0f;
            emote.controllerVars[0].maxShape = 1f;

            // Add another emote expression...
            emoter.emotes.Add(new EmoteExpression("lookDown", new InspectorControllerHelperData(), true, false, false, 0f));
            emoter.emotes[1].isRandomEmote = true;
            emote = emoter.emotes[1].expData;
            // create an expression component for the emote exression...
            emote.components[0].name = "lookDown component";
            emote.components[0].durationOn = .5f;
            emote.components[0].durationHold = .2f;
            emote.components[0].durationOff = .3f;
            emote.controllerVars[0].smr = smr;
            emote.controllerVars[0].blendIndex = smr.sharedMesh.GetBlendShapeIndex("lookDown");
            emote.controllerVars[0].minShape = 0f;
            emote.controllerVars[0].maxShape = 1f;  

            // ============================= Optional/theoretical example code:
            // Add another expression component to the second emote 
            // expression...repeat to add unlimited components to an emote expression...
            // See the SALSA example for more information on adding additional 
            // components to an expression...
            emote.controllerVars.Add(new InspectorControllerHelperData()); // see SALSA example  for explanation...
            emote.components.Add(new ExpressionComponent()); // see SALSA example for explanation...
            emote.components[1].name = "sayLarge component";
            emote.components[1].durationOn = .5f;
            emote.components[1].durationHold = .3f;
            emote.components[1].durationOff = .3f;
            // It is necessary to set the easing and control type on subsequently added components.
            emote.components[1].easing = LerpEasings.EasingType.CubicOut;
            emote.components[1].controlType = ExpressionComponent.ControlType.Shape;
            emote.controllerVars[1].smr = smr;
            emote.controllerVars[1].blendIndex = smr.sharedMesh.GetBlendShapeIndex("sayLrg");
            emote.controllerVars[1].minShape = 0f;
            emote.controllerVars[1].maxShape = 1f;
            // etc...

            // Add a third emote expression...
            emoter.emotes.Add(new EmoteExpression("lookLeft", new InspectorControllerHelperData(), true, false, false, 0f));
            emoter.emotes[2].isRandomEmote = true;
            emote = emoter.emotes[2].expData;
            emote.components[0].name = "lookLeft component";
            emote.components[0].durationOn = .5f;
            emote.components[0].durationHold = .2f;
            emote.components[0].durationOff = .3f;
            emote.controllerVars[0].smr = smr;
            emote.controllerVars[0].blendIndex = smr.sharedMesh.GetBlendShapeIndex("lookLeft");
            emote.controllerVars[0].minShape = 0f;
            emote.controllerVars[0].maxShape = 1f;

            // Add a fourth emote exression...
            emoter.emotes.Add(new EmoteExpression("lookRight", new InspectorControllerHelperData(), true, false, false, 0f));
            emoter.emotes[3].isRandomEmote = true;
            emote = emoter.emotes[3].expData;
            emote.components[0].name = "lookRight component";
            emote.components[0].durationOn = .5f;
            emote.components[0].durationHold = .2f;
            emote.components[0].durationOff = .3f;
            emote.controllerVars[0].smr = smr;
            emote.controllerVars[0].blendIndex = smr.sharedMesh.GetBlendShapeIndex("lookRight");
            emote.controllerVars[0].minShape = 0f;
            emote.controllerVars[0].maxShape = 1f;

            // Configure EmoteR's Random Emotes options...
            emoter.useRandomEmotes = true;
            emoter.randomChance = 1f;
            emoter.isChancePerEmote = true;
            emoter.NumRandomEmotesPerCycle = 2;
            emoter.randomEmoteMinTimer = 1.5f;
            emoter.randomEmoteMaxTimer = 3f;
            emoter.useRandomFrac = true;
            emoter.useRandomHoldDuration = true;
            emoter.randomHoldDurationMin = .15f;
            emoter.randomHoldDurationMax = .53f;
            emoter.randomFracBias = .25f;

            // at runtime: apply controller baking...
            emoter.UpdateExpressionControllers();
            // at runtime: update the emote specialty lists...
            emoter.UpdateEmoteLists();
        }
    }
}

Test Your Runtime Code...

This isn't runtime, but you can use it to quickly test to see if your setup code (above) works (at design time) by calling an editor function...something like the following...

Select the boxhead model in your scene and then select the Unity menu item: GameObject > DemoCode > SALSA LipSync > DemoCode Boxhead EmoteR Config (or whatever you have configured the menu item to be in the below script).

using UnityEditor;
using UnityEngine;

namespace DemoCode // change this to your custom namespace...
{
    public class BoxheadEmoterRuntimeTester : Editor
    {
        [MenuItem("GameObject/DemoCode/SALSA LipSync/DemoCode Boxhead Emoter Config")]
        public static void SetupBoxheadEmoter()
        {
            var selectedObject = Selection.activeGameObject;
            // NOTE: BoxheadEmoterRuntime is in the DemoCode namespace by default...
            BoxheadEmoterRuntime.BoxheadEmoterSetup(selectedObject);
        }
    }
}