Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

LEGO Microgame MinifigFaceAnimationController

Discussion in 'Animation' started by kid_lush, Oct 18, 2021.

  1. kid_lush

    kid_lush

    Joined:
    Nov 22, 2020
    Posts:
    1
    Can anyone provide clues to adding a 2D texture face animation as a PNG Sequence to the Lego Microgame with script MinifigFaceAnimationsController.cs?

    public class MinifigFaceAnimationController : MonoBehaviour
    {
    public enum FaceAnimation
    { Blink, Smile, Wink }
    . . .
    if (wholeFrame >= currentAnimationData.textures.Length)
    {
    faceMaterial.SetTexture(shaderTextureId, defaultTexture);
    playing = false;
    }
    else
    {
    faceMaterial.SetTexture(shaderTextureId, currentAnimationData.textures[wholeFrame]);
    showingFrame = wholeFrame;
    }
     
  2. pixelagent

    pixelagent

    Joined:
    Aug 8, 2019
    Posts:
    3
    Wanted to fix this so hopefully this will help.
    /*
    Updated MinifigFaceAnimationController script
    MinifigFaceAnimationController.cs
    */
    using System;
    using System.Collections.Generic;
    using UnityEngine;

    namespace Unity.LEGO.Minifig
    {

    public class MinifigFaceAnimationController : MonoBehaviour
    {
    public enum FaceAnimation
    {
    Accept,
    Blink,
    BlinkTwice,
    Complain,
    Cool,
    Dissatisfied,
    Doubtful,
    Frustrated,
    Laugh,
    Mad,
    Sleepy,
    Smile,
    Surprised,
    Wink
    }

    [Serializable]
    class AnimationData
    {
    public Texture2D[] textures;
    }



    #if UNITY_EDITOR
    [Header("Animation")]
    #endif


    [SerializeField]
    [Tooltip("This is the tag name of your face object. Drag the prefab onto the scene to select the face oject and assign a tag.")]
    private string faceTag = "Face"; // Tag of the child object you want to find


    [HideInInspector,SerializeField]
    private Transform face;
    [SerializeField]
    [Tooltip("The default texture of the minifigure")]
    Texture2D defaultTexture;
    [SerializeField]
    List<FaceAnimation> animations = new List<FaceAnimation>();
    [SerializeField]
    List<AnimationData> animationData = new List<AnimationData>() ;

    Material faceMaterial;

    bool playing;

    AnimationData currentAnimationData;
    float currentFrame;
    int showingFrame;

    float framesPerSecond;


    int shaderTextureId;
    public void Init(Transform face, Texture2D defaultTexture)
    {

    this.face = face;
    this.defaultTexture = defaultTexture;
    }

    public void AddAnimation(FaceAnimation animation, Texture2D[] textures)
    {
    if (!animations.Contains(animation))
    {
    animations.Add(animation);
    var animationData = new AnimationData();
    animationData.textures = textures;
    this.animationData.Add(animationData);
    }
    else
    {
    Debug.LogErrorFormat("Face animation controller already contains animation {0}", animation);
    }
    }

    public bool HasAnimation(FaceAnimation animation)
    {
    return animations.IndexOf(animation) >= 0;
    }

    public void PlayAnimation(FaceAnimation animation, float framesPerSecond = 24.0f)
    {
    var animationIndex = animations.IndexOf(animation);
    if (animationIndex < 0)
    {
    Debug.LogErrorFormat("Face animation controller does not contatin animation {0}", animation);
    return;
    }

    if (framesPerSecond <= 0.0f)
    {
    Debug.LogError("Frames per second must be positive");
    return;
    }

    playing = true;
    currentAnimationData = animationData[animationIndex];
    currentFrame = 0.0f;
    showingFrame = -1;
    this.framesPerSecond = framesPerSecond;

    }

    void Start()
    {

    //FindAndAssignGrandchild();

    FindAndAssignGrandchildByTag();

    // Use the script's own transform as the starting point
    //Transform rootObject = transform;

    // Find the descendant called "Face" under the root object
    //face = rootObject.Find("Face");

    if (face != null)
    {

    Debug.Log("Found object: " + face.gameObject.name);

    Renderer faceRenderer = face.GetComponent<Renderer>();

    // Check if the Renderer component was found
    if (faceRenderer != null)
    {
    // Get the material of the Renderer component
    faceMaterial = faceRenderer.material;

    // Log the name of the found object and the material
    Debug.Log("Found object: " + face.gameObject.name);
    Debug.Log("Material: " + faceMaterial.name);
    }
    else
    {
    Debug.LogError("Renderer component not found on 'Face' object!");
    }
    }


    //faceMaterial = face.GetComponent<Renderer>().material;
    shaderTextureId = Shader.PropertyToID("_BaseMap");
    }

    // Update is called once per frame
    void Update()
    {
    if (playing)
    {
    currentFrame += Time.deltaTime * framesPerSecond;

    var wholeFrame = Mathf.FloorToInt(currentFrame);
    if (wholeFrame != showingFrame)
    {
    if (wholeFrame >= currentAnimationData.textures.Length)
    {
    faceMaterial.SetTexture(shaderTextureId, defaultTexture);
    playing = false;
    }
    else
    {
    faceMaterial.SetTexture(shaderTextureId, currentAnimationData.textures[wholeFrame]);
    showingFrame = wholeFrame;
    }
    }

    }
    }


    // Make the 'animations' property accessible from other scripts
    public void PlayAllAnimationsList()
    {
    List<MinifigFaceAnimationController.FaceAnimation> animationsList = animations;

    // Loop through the animationDataList and print information to the console
    foreach (var animation in animationsList)
    {
    //Debug.Log("Animations: " + animation.ToString() );
    PlayAnimation(animation);
    }
    }

    public void PlayAnimationsFromList(int i)
    {
    List<MinifigFaceAnimationController.FaceAnimation> animationsList = animations;

    //Debug.Log("Animations: " + animationsList );
    PlayAnimation(animationsList);
    }


    private void FindAndAssignGrandchildByTag()
    {
    // Iterate through all children of the GameObject with the script
    for (int i = 0; i < transform.childCount; i++)
    {
    Transform child = transform.GetChild(i);

    // Check if the child has the specified tag
    if (child.CompareTag(faceTag))
    {
    // Assign the transform of the found grandchild to the serialized Transform field
    face = child;

    // Log the name of the found grandchild
    Debug.Log("Found grandchild object with tag '" + faceTag + "': " + child.gameObject.name);

    // Stop searching if you only want to find the first matching child
    // Remove this line if you want to find all matching children
    break;
    }
    }

    if (face == null)
    {
    Debug.LogError("Grandchild object with tag '" + faceTag + "' not found!");
    }
    }

    }

    }


    and an updated MinifigFaceAnimationControllerEditor.cs script


    using UnityEditor;
    using UnityEngine;
    using Unity.LEGO.Minifig;

    namespace Unity.LEGO.EditorExt
    {

    [CustomEditor(typeof(MinifigFaceAnimationController))]

    public class MinifigFaceAnimationControllerEditor : Editor
    {
    MinifigFaceAnimationController controller;
    SerializedProperty AnimationsProp;

    void OnEnable()
    {
    controller = (MinifigFaceAnimationController)target;
    AnimationsProp = serializedObject.FindProperty("animations");
    }

    public override void OnInspectorGUI()
    {

    // Create a new GUIStyle with a bold font
    GUIStyle boldStyle = new GUIStyle(GUI.skin.label);
    boldStyle.fontStyle = FontStyle.Bold;

    // Draw the default inspector
    DrawDefaultInspector();

    if (AnimationsProp != null)
    {

    GUILayout.Label("List animations", boldStyle);

    if (AnimationsProp.arraySize == 0)
    {
    GUILayout.Label("No animations prepared", boldStyle);
    }
    else
    {
    for (var i = 0; i < AnimationsProp.arraySize; ++i)
    {
    EditorGUILayout.BeginHorizontal();

    GUILayout.Label(ObjectNames.NicifyVariableName(((MinifigFaceAnimationController.FaceAnimation)AnimationsProp.GetArrayElementAtIndex(i).enumValueIndex).ToString()));
    EditorGUI.BeginDisabledGroup(!Application.isPlaying);

    // Play button
    if (GUILayout.Button(new GUIContent("Play", !Application.isPlaying ? "Only works in Play Mode" : "")))
    {
    controller.PlayAnimation((MinifigFaceAnimationController.FaceAnimation)(AnimationsProp.GetArrayElementAtIndex(i).enumValueIndex));
    }
    EditorGUI.EndDisabledGroup();
    EditorGUILayout.EndHorizontal();
    }
    }
    }
    }

    }

    }

    The only thing you will need to do is add a tag to the Face gameObject with a reference. Or if you can't unpack the minifigure change the FindAndAssignGrandchildByTag() script to use a find by name script instead.

    https://drive.google.com/file/d/1SCSrisPbwm9WVLvkRNZQHZzkuCUDRcx6/view
     
    Last edited: Dec 1, 2023