Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Showcase LinearBlend With Constraits Free Demo

Discussion in 'DOTS Animation' started by NT_Ninetails, May 5, 2021.

  1. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    196
    Steps to understand this:
    1 - Learn how the Linear Blending Example works (maybe i'll make a tutorial?)
    2 - Learn how the Constraints Example (maybe i'll make a tutorial?)



    So this combines the Linear Blend Example and the Animation Constraints example into one example.
    I just got the example to run so i haven't even determined if any features need to be added yet.

    Here are the extra files you need that works with the Unity.Animation.Samples github page( https://github.com/Unity-Technologies/Unity.Animation.Samples)

    In the video i show what the prefab looks like (sorry this is more of a showcase than a tutorial atm but i wanted to share this because I think it's pretty simple and useful to know)

    Here you go, Have Fun :D

    BlendTreeWithConstraintComponent.cs
    Code (CSharp):
    1. using Unity.Animation;
    2. using Unity.DataFlowGraph;
    3. using Unity.Entities;
    4. using Unity.Mathematics;
    5. using Unity.Transforms;
    6. using static ConstraintsSample.ConstraintGraphComponent;
    7.  
    8. #if UNITY_EDITOR
    9. using UnityEngine;
    10. using Unity.Animation.Hybrid;
    11. #endif
    12.  
    13. /*
    14. This is literially a Copy And Paste from the Animation With Contraints with a different name;
    15. the only difference is that I removed the FKGrah because it was conflicting with the BlendTreeGraph
    16. Setup code. NOTE: I didn't make this code, i just restructured it and Unity is responsible for any issues :)
    17. */
    18.  
    19. namespace ConstraintsSample
    20. {
    21. #if UNITY_EDITOR
    22.     [RequireComponent(typeof(RigComponent))]
    23.     public class BlendTreeWithConstraintComponent : MonoBehaviour, IConvertGameObjectToEntity
    24.     {
    25.         public TwoBoneIKSetup LeftArmIK;
    26.         public TwoBoneIKSetup RightArmIK;
    27.         public AimConstraintSetup HeadLookAt;
    28.  
    29.         int3 Convert(in TwoBoneIKSetup setup, RigComponent rig)
    30.         {
    31.             var indices = math.int3(
    32.                 RigGenerator.FindTransformIndex(setup.Root, rig.Bones),
    33.                 RigGenerator.FindTransformIndex(setup.Mid, rig.Bones),
    34.                 RigGenerator.FindTransformIndex(setup.Tip, rig.Bones)
    35.             );
    36.  
    37.             if (indices.x == -1 || indices.y == -1 || indices.z == -1)
    38.                 throw new System.InvalidOperationException("Invalid TwoBoneIK transforms");
    39.  
    40.             return indices;
    41.         }
    42.  
    43.         int Convert(in AimConstraintSetup setup, RigComponent rig)
    44.         {
    45.             var index = RigGenerator.FindTransformIndex(setup.Bone, rig.Bones);
    46.             if (index == -1)
    47.                 throw new System.InvalidOperationException("Invalid AimConstraint Bone");
    48.  
    49.             return index;
    50.         }
    51.  
    52.         public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    53.         {
    54.             var rig = GetComponent<RigComponent>();
    55.  
    56.  
    57.             var ikGraphSetup = new IKGraphSetup
    58.             {
    59.                 LeftArmIK = Convert(LeftArmIK, rig),
    60.                 LeftTargetEntity = conversionSystem.GetPrimaryEntity(LeftArmIK.Target),
    61.                 RightArmIK = Convert(RightArmIK, rig),
    62.                 RightTargetEntity = conversionSystem.GetPrimaryEntity(RightArmIK.Target),
    63.                 HeadIndex = Convert(HeadLookAt, rig),
    64.                 HeadLocalAimAxis = HeadLookAt.LocalAimAxis
    65.             };
    66.  
    67.             dstManager.AddComponentData(entity, ikGraphSetup);
    68.  
    69.             dstManager.AddComponent<DeltaTime>(entity);
    70.         }
    71.     }
    72. #endif
    73.  
    74.     // Creates a graph which constrains the left and right arms using a TwoBoneIK constraint and a head lookat using an AimConstraint.
    75.     // This graph is created in the ProcessLateAnimationGraph system which is evaluated after the TransformSystemGroup making it ideal to
    76.     // do pose corrections.
    77.     [UpdateBefore(typeof(DefaultAnimationSystemGroup))]
    78.     public class BlendWithIKGraphSystem : SampleSystemBase<
    79.         IKGraphSetup,
    80.         IKGraphData,
    81.         ProcessLateAnimationGraph
    82.     >
    83.     {
    84.         protected override IKGraphData CreateGraph(Entity entity, ref Rig rig, ProcessLateAnimationGraph graphSystem,
    85.             ref IKGraphSetup setup)
    86.         {
    87.             var set = graphSystem.Set;
    88.             var graph = graphSystem.CreateGraph();
    89.  
    90.             var data = new IKGraphData();
    91.             data.Graph = graph;
    92.  
    93.             var entityNode = graphSystem.CreateNode(graph, entity);
    94.  
    95.             var leftArmIKNode = graphSystem.CreateNode<TwoBoneIKNode>(graph);
    96.             var leftTargetEntityNode = graphSystem.CreateNode(graph, setup.LeftTargetEntity);
    97.             var leftWorldToRootNode = graphSystem.CreateNode<WorldToRootNode>(graph);
    98.  
    99.             var rightArmIKNode = graphSystem.CreateNode<TwoBoneIKNode>(graph);
    100.             var rightTargetEntityNode = graphSystem.CreateNode(graph, setup.RightTargetEntity);
    101.             var rightWorldToRootNode = graphSystem.CreateNode<WorldToRootNode>(graph);
    102.  
    103.             var headLookAtNode = graphSystem.CreateNode<AimConstraintNode>(graph);
    104.  
    105.             set.Connect(entityNode, leftArmIKNode, TwoBoneIKNode.KernelPorts.Input,
    106.                 NodeSet.ConnectionType.Feedback);
    107.  
    108.             set.Connect(entityNode, leftWorldToRootNode, WorldToRootNode.KernelPorts.Input,
    109.                 NodeSet.ConnectionType.Feedback);
    110.             set.Connect(entityNode, leftWorldToRootNode, WorldToRootNode.KernelPorts.RootEntity,
    111.                 NodeSet.ConnectionType.Feedback);
    112.             set.Connect(leftTargetEntityNode, leftWorldToRootNode,
    113.                 WorldToRootNode.KernelPorts.LocalToWorldToRemap);
    114.             set.Connect(leftWorldToRootNode, WorldToRootNode.KernelPorts.Output, leftArmIKNode,
    115.                 TwoBoneIKNode.KernelPorts.Target);
    116.             set.Connect(leftArmIKNode, TwoBoneIKNode.KernelPorts.Output, rightArmIKNode,
    117.                 TwoBoneIKNode.KernelPorts.Input);
    118.  
    119.             set.Connect(entityNode, rightWorldToRootNode, WorldToRootNode.KernelPorts.Input,
    120.                 NodeSet.ConnectionType.Feedback);
    121.             set.Connect(entityNode, rightWorldToRootNode, WorldToRootNode.KernelPorts.RootEntity,
    122.                 NodeSet.ConnectionType.Feedback);
    123.             set.Connect(rightTargetEntityNode, rightWorldToRootNode,
    124.                 WorldToRootNode.KernelPorts.LocalToWorldToRemap);
    125.             set.Connect(rightWorldToRootNode, WorldToRootNode.KernelPorts.Output, rightArmIKNode,
    126.                 TwoBoneIKNode.KernelPorts.Target);
    127.             set.Connect(rightArmIKNode, TwoBoneIKNode.KernelPorts.Output, headLookAtNode,
    128.                 AimConstraintNode.KernelPorts.Input);
    129.  
    130.             set.Connect(headLookAtNode, AimConstraintNode.KernelPorts.Output, entityNode);
    131.  
    132.             var stream = AnimationStream.FromDefaultValues(rig);
    133.             var leftArmIK = new TwoBoneIKNode.SetupMessage
    134.             {
    135.                 RootIndex = setup.LeftArmIK.x,
    136.                 MidIndex = setup.LeftArmIK.y,
    137.                 TipIndex = setup.LeftArmIK.z,
    138.                 TargetOffset = new RigidTransform(quaternion.identity, float3.zero)
    139.             };
    140.  
    141.             var rightArmIK = new TwoBoneIKNode.SetupMessage
    142.             {
    143.                 RootIndex = setup.RightArmIK.x,
    144.                 MidIndex = setup.RightArmIK.y,
    145.                 TipIndex = setup.RightArmIK.z,
    146.                 TargetOffset = new RigidTransform(quaternion.identity, float3.zero)
    147.             };
    148.  
    149.             var headAim = new AimConstraintNode.SetupMessage
    150.             {
    151.                 Index = setup.HeadIndex,
    152.                 LocalAimAxis = setup.HeadLocalAimAxis,
    153.                 LocalAxesMask = math.bool3(true)
    154.             };
    155.  
    156.             set.SendMessage(leftArmIKNode, TwoBoneIKNode.SimulationPorts.Rig, rig);
    157.             set.SendMessage(leftArmIKNode, TwoBoneIKNode.SimulationPorts.ConstraintSetup, leftArmIK);
    158.             set.SendMessage(leftWorldToRootNode, WorldToRootNode.SimulationPorts.Rig, rig);
    159.  
    160.             set.SendMessage(rightArmIKNode, TwoBoneIKNode.SimulationPorts.Rig, rig);
    161.             set.SendMessage(rightArmIKNode, TwoBoneIKNode.SimulationPorts.ConstraintSetup, rightArmIK);
    162.             set.SendMessage(rightWorldToRootNode, WorldToRootNode.SimulationPorts.Rig, rig);
    163.  
    164.             // Same as above but now applying static corrections to the head bone for a look at
    165.             set.SendMessage(headLookAtNode, AimConstraintNode.SimulationPorts.Rig, rig);
    166.             set.SendMessage(headLookAtNode, AimConstraintNode.SimulationPorts.ConstraintSetup, headAim);
    167.             set.SetPortArraySize(headLookAtNode, AimConstraintNode.KernelPorts.SourcePositions, 1);
    168.             set.SetPortArraySize(headLookAtNode, AimConstraintNode.KernelPorts.SourceOffsets, 1);
    169.             set.SetPortArraySize(headLookAtNode, AimConstraintNode.KernelPorts.SourceWeights, 1);
    170.             set.SetData(headLookAtNode, AimConstraintNode.KernelPorts.SourcePositions, 0,
    171.                 stream.GetLocalToRootTranslation(headAim.Index) + math.float3(0f, -2f, 0.5f));
    172.             set.SetData(headLookAtNode, AimConstraintNode.KernelPorts.SourceOffsets, 0, quaternion.identity);
    173.             set.SetData(headLookAtNode, AimConstraintNode.KernelPorts.SourceWeights, 0, 1f);
    174.  
    175.             return data;
    176.         }
    177.  
    178.         protected override void DestroyGraph(Entity entity, ProcessLateAnimationGraph graphSystem, ref IKGraphData data)
    179.         {
    180.             graphSystem.Dispose(data.Graph);
    181.         }
    182.     }
    183. }
    184.  
    Blend1DLinker.cs
    Code (CSharp):
    1. using Unity.Animation.Hybrid;
    2. using Unity.Entities;
    3. using UnityEngine;
    4.  
    5. /* This is a simplification of the 1D Blend Graph Example.
    6. * Attach this on the Entity that is to be Animated and the script will do the rest
    7. * NOTE: this input system was moved to AnimationBlendingSystem to show how to intergrate this
    8. * with an ECS System
    9. */
    10. [DisallowMultipleComponent]
    11. public class Blend1DLinker : MonoBehaviour, IConvertGameObjectToEntity
    12. {
    13.     public BlendTree1DGraph m_BlendTree1DGraph;
    14.  
    15.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    16.     {
    17.         var RigComponent = GetComponent<RigComponent>();
    18.         if (RigComponent == null) Debug.LogWarning("Blend1DLinker: GameObject \"" + name + "\" has no RigComponent, please add it and try again");
    19.         else
    20.         {
    21.             m_BlendTree1DGraph.PreProcessData(RigComponent);
    22.             m_BlendTree1DGraph.AddGraphSetupComponent(entity, dstManager, conversionSystem);
    23.         }
    24.     }
    25. }
    26.  
    Blend2DLinker.cs
    Code (CSharp):
    1. using Unity.Animation.Hybrid;
    2. using Unity.Entities;
    3. using UnityEngine;
    4. /* This is a simplification of the 2D Blend Graph Example.
    5. * Attach this on the Entity that is to be Animated and the script will do the rest
    6. * NOTE: this input system was moved to AnimationBlendingSystem to show how to intergrate this
    7. * with an ECS System
    8. */
    9. [DisallowMultipleComponent]
    10. public class Blend2DLinker : MonoBehaviour, IConvertGameObjectToEntity
    11. {
    12.     public BlendTree2DGraph m_BlendTree2DGraph;
    13.  
    14.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    15.     {
    16.         var RigComponent = GetComponent<RigComponent>();
    17.         if (RigComponent == null) Debug.LogWarning("Blend2DLinker: GameObject \"" + name + "\" has no RigComponent, please add it and try again");
    18.         else
    19.         {
    20.             m_BlendTree2DGraph.PreProcessData(RigComponent);
    21.             m_BlendTree2DGraph.AddGraphSetupComponent(entity, dstManager, conversionSystem);
    22.         }
    23.     }
    24. }
    25.  
    AnimationBlendingSystem.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Entities;
    3. using Unity.Mathematics;
    4. /* This system is to be an example on how to run the Blend Graph samples in a ECS System.
    5. * Currentlt the we are using the old input system just as a proof of concept. Since the BlendTreeData
    6. * is an abstract of ISampleData which is an abstract of ISystemStateData this is easily burstable.
    7. */
    8. class AnimationBlendingSystem : SystemBase
    9. {
    10.     float ParamXStep = 0.01f;
    11.     float ParamYStep = 0.01f;
    12.  
    13.     protected override void OnUpdate()
    14.     {
    15.         CompleteDependency();
    16.  
    17.  
    18.         /*
    19.             this just updates the BlendTree2DData params. This can
    20.             be done using using Burst code but for this test
    21.             i'm just using the default Input System
    22.          */
    23.         Entities
    24.             .WithoutBurst()
    25.             .ForEach((Entity entity, ref BlendTree2DData data) => {
    26.                 UpdateParameters(ref data);
    27.             })
    28.             .Run();
    29.         /*
    30.            this just updates the BlendTree1DData params. This can
    31.            be done using using Burst code but for this test
    32.            i'm just using the default Input System
    33.         */
    34.         Entities
    35.             .WithoutBurst()
    36.             .ForEach((Entity entity, ref BlendTree1DData data) => {
    37.                 UpdateParameters(ref data);
    38.             })
    39.             .Run();
    40.     }
    41.  
    42.     // Todo: modify this to work with input system
    43.     public void UpdateParameters(ref BlendTree2DData data)
    44.     {
    45.         var deltaX = Input.GetAxisRaw("Horizontal");
    46.         var deltaY = Input.GetAxisRaw("Vertical");
    47.  
    48.         data.paramX = math.clamp(data.paramX + deltaX * ParamXStep, -1.0f, 1.0f);
    49.         data.paramY = math.clamp(data.paramY + deltaY * ParamYStep, -1.0f, 1.0f);
    50.     }
    51.  
    52.     // Todo: modify this to work with input system
    53.     public void UpdateParameters(ref BlendTree1DData data)
    54.     {
    55.         var deltaY = Input.GetAxisRaw("Vertical");
    56.  
    57.         data.paramX = math.clamp(data.paramX + deltaY * ParamXStep, -1.0f, 1.0f);
    58.     }
    59. }
    60.  
     
    FilmBird and Lukas_Kastern like this.
  2. kite3h

    kite3h

    Joined:
    Aug 27, 2012
    Posts:
    197
    It is very simple code.
    The only problem is that NaNException, which is not very important, is thrown.
    If you don't get a NaNException in your work environment, you're probably using a different branch than the one that was released.
    It's not a sample issue, it's a package issue.
    Since NaN also occurs only the first time, it has no effect on execution. It's just that the build doesn't work.

    Of course, if you do not put it in SubScene and convert it directly from the game object state, NaN does not occur. In the end, it's an initialization problem, but shouldn't that have to be initialized in advance in the package?

    Anyway, I am using it by myself, but it is unlikely that general users can use it easily. Paste a graph quickly How long do you have to write a graph node with code like this?
     
    Last edited: May 6, 2021
  3. NT_Ninetails

    NT_Ninetails

    Joined:
    Jan 21, 2018
    Posts:
    196
    You are right that something is wrong with the initialization when using the models and prefabs from the Unity files. When i used my own model i had no errors and it worked just fine. I am a little confused by your question, could you please explain what you mean by "Paste a graph quickly How long do you have to write a graph node with code like this?"