Search Unity

Animation via code - examples?

Discussion in 'UI Toolkit' started by Prodigga, Aug 8, 2020.

  1. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Hello

    I would like to create a simple spinning wheel animation in my custom editor window. I understand UIToolkit does not yet support animations, but can be achieved through code.

    I have a custom editor window that instantiates a UXML on enable and add's it to the root. I have access to all the elements, and I noticed each VisualElement has a transform with rotation property.

    What is the recommended way to animation via code in the editor? Subscribe to EditorApplication.update, and drive transform? Should I repaint the window every frame too? VisualElements also seem to be anchored to the top left, so I am not sure what the nicest way is to just spin something around its centre.
     
    Last edited: Aug 8, 2020
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Any changes you make to the "transform" property are applied after the element has been positioned and sized by the layouting engine, so changing it won't affect other elements. Knowing that, you can definitely use the transform for animations.

    As for ticking the animation, the recommended approach with UI Toolkit is to use the UI Toolkit Scheduler:
    Code (CSharp):
    1. myElement.schedule.Execute(() => anyElement.style.top = anyElement.resolvedStyle.top + 1).Every(100); // ms
    Execute() returns an scheduled item object that you can keep around and pause/resume/destroy as you wish to manage your animation this way.

    Finally, for rotations, you should be able to just use transform. If you need centered anchors, the recommended approach is to rotate a 1px by 1px "parent" element with
    justify-content: center
    and
    align-items: center
    style properties that contains your main animated element as a child.
     
  3. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    I wanted to try this idea of having an anchor of 1x1 with child element but the result is a thin line rather than the 100x100 red square.

    My test code.

    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    2.     <ui:VisualElement style="flex-grow: 1; align-items: center; justify-content: center;">
    3.         <ui:VisualElement name="anchor" style="align-items: center; justify-content: center; width: 1px; height: 1px;">
    4.             <ui:VisualElement name="image" style="background-color: rgb(255, 0, 0); width: 100px; height: 100px;" />
    5.         </ui:VisualElement>
    6.     </ui:VisualElement>
    7. </ui:UXML>
    Annotation 2021-06-01 191737.jpg
     
  4. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    Nvm.. I had to set flex-shrink: 0; for the child item.
     
    bugbeeb likes this.
  5. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    A found a problem with rotating an element. It causes other elements to loose hover state. As soon as I remover the animation code the hover works fine again. If I make the animation update (the value in .Every(...)) fast enough I can actually not even get a visible hover state over other elements. If the animation update run slower I can see the hover appear and disappear while moving the mouse over element. So it seems like every "tick" of animation update is causing this.

    In the below image you can see the tabs and list hover do not work when "Environment" tab is active. It is the one which has the animated black rings to the right of image.

    01.gif


    Here is the full code. I tried it with DoTween and it too causes the problems. I've also tried with setting focusable false and picking ignore on all elements involved with the animation.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.UIElements;
    4. using DG.Tweening;
    5.  
    6. namespace Game.UI
    7. {
    8.     public class RotatingElement : VisualElement
    9.     {
    10.         [UnityEngine.Scripting.Preserve]
    11.         public new class UxmlFactory : UxmlFactory<RotatingElement, UxmlTraits> { }
    12.  
    13.         [UnityEngine.Scripting.Preserve]
    14.         public new class UxmlTraits : VisualElement.UxmlTraits
    15.         {
    16.             private readonly UxmlFloatAttributeDescription step = new UxmlFloatAttributeDescription { name = "step", defaultValue = 1f };
    17.             private readonly UxmlIntAttributeDescription delay = new UxmlIntAttributeDescription { name = "delay", defaultValue = 100 };
    18.  
    19.             public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    20.             {
    21.                 base.Init(ve, bag, cc);
    22.                 RotatingElement item = ve as RotatingElement;
    23.                 item.Step = step.GetValueFromBag(bag, cc);
    24.                 item.Delay = delay.GetValueFromBag(bag, cc);
    25.             }
    26.         }
    27.  
    28.         public float Step { get; private set; }    // how much rotation is applied every step
    29.         public int Delay { get; private set; }  // the rotation is applied every this many milliseconds
    30.  
    31.         // ------------------------------------------------------------------------------------------------------------
    32.  
    33.         public RotatingElement()
    34.         {
    35.             Init();
    36.         }
    37.  
    38.         public RotatingElement(float step, int delay)
    39.         {
    40.             Step = step;
    41.             Delay = delay;
    42.             Init();
    43.         }
    44.  
    45.         public RotatingElement(float step, int delay, int contentSize, VisualElement contentEle)
    46.         {
    47.             Step = step;
    48.             Delay = delay;
    49.  
    50.             Init();
    51.  
    52.             contentEle.focusable = false;
    53.             contentEle.pickingMode = PickingMode.Ignore;
    54.             contentEle.style.width = contentSize;
    55.             contentEle.style.height = contentSize;
    56.             Add(contentEle);
    57.         }
    58.  
    59.         public void SetContentSize(int size)
    60.         {
    61.             foreach (var c in Children())
    62.             {
    63.                 c.style.width = size;
    64.                 c.style.height = size;
    65.             }
    66.         }
    67.  
    68.         private void Init()
    69.         {
    70.             pickingMode = PickingMode.Ignore;
    71.             focusable = false;
    72.            
    73.             style.width = 1;
    74.             style.height = 1;
    75.             style.justifyContent = Justify.Center;
    76.             style.alignItems = Align.Center;
    77.  
    78.             //DOTween.To(() => worldTransform.rotation.eulerAngles, x => transform.rotation = Quaternion.Euler(x), new Vector3(0f, 0f, 360f), 1f).SetEase(Ease.Linear).SetLoops(-1);
    79.  
    80.             schedule.Execute(() =>
    81.             {
    82.                 var r = worldTransform.rotation.eulerAngles;
    83.                 r.z += Step;
    84.                 transform.rotation = Quaternion.Euler(r);
    85.             }).Every(Delay); // ms
    86.         }
    87.  
    88.         // ============================================================================================================
    89.     }
    90. }