Search Unity

ECS with GameObject Animator help needed.

Discussion in 'Entity Component System' started by Janosgolya, Jan 5, 2020.

  1. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    Hello,

    It's my first post ever here, so welcome to everyone!. Never posted, because for so many years I've found every answer I needed here without asking! Many thanks for that! But unfortunately, not this time.

    Im trying to simulate flies swarm with ECS. The goal is to make them sit on objects, and after a while return to flying in circles around those targets.
    I was able to achieve this result with MonoBehaviours, but I need thousands of those nasty creatures, so I moved to ECS. So far they fly towards targets using new physics, they bump from colliders, so it was so far so good.

    Now I want to change their animation in GameObject animator component of attached prefab.
    But I get a warrning "Animator is not playing an AnimatorController", a huge FPS drop and no animation played - at this line of code

     animator.AnimatorInstance.SetBool("fly", true);


    So far I figured out that I tried to change the bool on prefab, not on an instance. Is that right?

    Here is my code on the prefab to convert to Entity:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5.  
    6. public class vehicleECS : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
    7. {
    8.  
    9.    
    10.  
    11.  
    12.     [SerializeField] private float speed = 1f;
    13.  
    14.    
    15.  
    16.  
    17.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    18.     {
    19.         dstManager.AddComponentData(entity, new AgentPrefab
    20.         {
    21.             EntityPrefab = conversionSystem.GetPrimaryEntity(gameObject)
    22.         });
    23.  
    24.  
    25.         Animator animator = new Animator { };
    26.  
    27.         animator = gameObject.GetComponent<Animator>();
    28.        
    29.  
    30.  
    31.         dstManager.AddComponentObject(entity, animator);
    32.  
    33.  
    34.         dstManager.AddComponent(entity, typeof(MoveForward));
    35.  
    36.         MoveSpeed moveSpeed = new MoveSpeed { Value = speed };
    37.         dstManager.AddComponentData(entity, moveSpeed);
    38.  
    39.         dstManager.AddComponent(entity, typeof(Velocity));
    40.  
    41.         Velocity velocity = new Velocity { VelocityVector = transform.forward };
    42.  
    43.         dstManager.AddComponentData(entity, velocity);
    44.  
    45.        
    46.         Target target = new Target { };
    47.  
    48.         dstManager.AddComponentData(entity, target);
    49.  
    50.         TargetIndex targetIndex = new TargetIndex { };
    51.  
    52.         dstManager.AddComponentData(entity, targetIndex);
    53.  
    54.         RotationSpeed rotationSpeed = new RotationSpeed { Value = 1f };
    55.         dstManager.AddComponentData(entity, rotationSpeed);
    56.  
    57.         TurnToward turnToward = new TurnToward { };
    58.         dstManager.AddComponentData(entity, turnToward);
    59.  
    60.         Hovering hovering = new Hovering { };
    61.         dstManager.AddComponentData(entity, hovering);
    62.  
    63.         Animation animation = new Animation { };
    64.         dstManager.AddComponentData(entity, animation);
    65.  
    66.         AnimatorComponent animatorComponent = new AnimatorComponent { };
    67.         dstManager.AddComponentData(entity, animatorComponent);
    68.  
    69.  
    70.  
    71.     }
    72.  
    73.     public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    74.     {
    75.         referencedPrefabs.Add(gameObject);
    76.     }
    77.  
    78.  
    79. }
    80.  
    And here is a spawner code:


    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Collections;
    4.  
    5. using UnityEngine;
    6.  
    7. using Unity.Entities;
    8. using Unity.Transforms;
    9. using Unity.Physics;
    10. using Unity.Mathematics;
    11. using Collider = Unity.Physics.Collider;
    12.  
    13. public class SpawnFlies : MonoBehaviour
    14.  
    15.    
    16. {
    17.  
    18.     [SerializeField] private GameObject prefab;
    19.  
    20.     [SerializeField] private int numberOfFlies = 1;
    21.  
    22.     [SerializeField] private bool useECS = false;
    23.  
    24.  
    25.     //tymczasowo
    26.     [SerializeField] private GameObject targetGameObject;
    27.  
    28.     [SerializeField] private Avatar avatar;
    29.     [SerializeField] private RuntimeAnimatorController controller;
    30.  
    31.     private GameObject[] targetsToLandOn;
    32.  
    33.  
    34.     EntityManager manager;
    35.     Entity sourceEntity;
    36.  
    37.     private Vector3 flyPos;
    38.  
    39.  
    40.     // Start is called before the first frame update
    41.     void Awake()
    42.     {
    43.  
    44.      
    45.         if (useECS)
    46.         {
    47.  
    48.             var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, new BlobAssetStore());
    49.  
    50.             sourceEntity = GameObjectConversionUtility.ConvertGameObjectHierarchy(prefab, settings);
    51.  
    52.             manager = World.DefaultGameObjectInjectionWorld.EntityManager;
    53.  
    54.  
    55.             BlobAssetReference<Collider> sourceCollider = manager.GetComponentData<PhysicsCollider>(sourceEntity).Value;
    56.             Animator sourceAnimator = manager.GetComponentObject<Animator>(sourceEntity);
    57.  
    58.  
    59.             for (int i = 0; i < numberOfFlies; i++)
    60.             {
    61.                
    62.                 flyPos = new Vector3(UnityEngine.Random.Range(2, 10), UnityEngine.Random.Range(2, 10), UnityEngine.Random.Range(2, 10));
    63.  
    64.                 ChangeTarget();
    65.  
    66.                 Entity entityInstance = manager.Instantiate(sourceEntity);
    67.  
    68.                 manager.SetComponentData(entityInstance, new PhysicsCollider { Value = sourceCollider });
    69.  
    70.                 manager.SetComponentData(entityInstance, new Hovering { isHovering = true });
    71.  
    72.                 manager.SetComponentData(entityInstance, new Translation { Value = flyPos });
    73.                 manager.SetComponentData(entityInstance, new RotationSpeed { Value = UnityEngine.Random.Range(4f, 7f) });
    74.                 manager.SetComponentData(entityInstance, new MoveSpeed { Value = UnityEngine.Random.Range(4f, 7f) });
    75.  
    76.                 manager.SetComponentData(entityInstance, new Velocity { VelocityVector = transform.forward });
    77.                 manager.SetComponentData(entityInstance, new Target { TargetPosition = targetGameObject.transform.position });
    78.                 manager.SetComponentData(entityInstance, new TargetIndex { TargetEntityIndex = 0 });
    79.                 manager.SetComponentData(entityInstance, new Animation { State = 0 });
    80.  
    81.                
    82.                 Animator animator = sourceAnimator;
    83.  
    84.                 animator.runtimeAnimatorController = Resources.Load("fly") as RuntimeAnimatorController;
    85.  
    86.                 Debug.Log("aourceAnimator_" + animator);
    87.  
    88.                 manager.SetComponentData(entityInstance, new AnimatorComponent { AnimatorInstance = animator });
    89.  
    90.  
    91.             }
    92.  
    93.  
    94.         }
    95.  
    96.  
    97.     }
    And finally the system itself

    Code (CSharp):
    1. using Unity.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Entities.Hybrid;
    6. using Unity.Jobs;
    7.  
    8.  
    9. public class AnimationSystem : ComponentSystem
    10. {
    11.    
    12.  
    13.     protected override void OnUpdate()
    14.     {
    15.    
    16.        
    17.         Entities.ForEach((Entity entity, ref Animation animation,  AnimatorComponent animator) => {
    18.  
    19.  
    20.             animation.State = 0;
    21.  
    22.  
    23.             switch (animation.State)
    24.             {
    25.  
    26.                 case 0:
    27.                  
    28.                     animator.AnimatorInstance.SetBool("fly", true);
    29.                    
    30.  
    31.                     break;
    32.  
    33.             }
    34.          
    35.  
    36.         });
     
    Plaximo likes this.
  2. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    Anybody? There is virtually no tutorial on this, I've searched everywhere. The only one I've found is this one:



    but it looks outdated - and this guy hid his "using" part so I have no idea how he made ComponentGroup. I havn't found any documentation on ComponentGroups either... :( I assume some new API replaced that but I have no idea which one to use.
    My time for that project is running out..

    Help!
     
    Plaximo likes this.
  3. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    When I changed the code to

    animator.Play("fly");


    the warning states :
    Game object with animator is inactive

    Well, my model is instantiated correctly with all its hierarhy, so how is it disabled?
     
  4. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    "ComponentGroup was renamed to EntityQuery". At least that one is demystified ;)
    I will try to rewrite the code from this tutorial, and let you know the results.
     
    Plaximo likes this.
  5. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    Well i managed to eliminate the warning by adding this line to my spawn script :

    sourceAnimator = Instantiate(prefab.GetComponent<Animator>());


    manager.AddComponentObject(entityInstance, sourceAnimator);


    And I rewrote the code from tutorial above to this:


    Code (CSharp):
    1. var createEntities = entityQuery.ToEntityArray(Allocator.Persistent);
    2.         var animators = entityQuery.ToComponentArray<Animator>();
    3.  
    4.    
    5.    
    6.         if (createEntities.Length > 0)
    7.         {
    8.             for (int i = 0; i != createEntities.Length; i ++)
    9.             {
    10.  
    11.            
    12.            
    13.                 var animator = animators[i];
    14.          
    15.                 animator.Play("walk");
    16.            
    17.             }
    18.         }

    And in theory this works.... but... (there is always one)...

    When using "convert and Inject Game Object" the body of my model is separated from wings and legs (they dissapear) ad is thrown around by physics. Other system works on it but not strong enough for it to fly. Some Entities are not affected by this.

    And no animation whatsoever. (EDIT: Actually one fly out of 100 have animation! :), it is the first one created. )

    the option "convert and Destoy" gives null exeption.
     
  6. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    Well I finally managed to overcome this. The solution is crazy but works.

    Instead of istantiating Animator component I instantiated the fly Game Object, and pass it into array. Then I wrote a small system to sync the Game Object with entities. Here it is, as there is virtually no docs or tutorials on this, maybe somone will benefit from it:


    Code (CSharp):
    1. private void LateUpdate()
    2.     {
    3.         EntityQuery entities = manager.CreateEntityQuery(typeof(Translation), typeof (Rotation), typeof(Animation));
    4.  
    5.         var positions = entities.ToComponentDataArray<Translation>(Allocator.Persistent);
    6.         var rotations = entities.ToComponentDataArray<Rotation>(Allocator.Persistent);
    7.  
    8.        
    9.  
    10.         for (int i = 0; i < numberOfFlies; i++)
    11.         {
    12.  
    13.  
    14.  
    15.             flies[i].transform.position = positions[i].Value;
    16.             flies[i].transform.rotation = rotations[i].Value;
    17.         }
    18.  
    19.         positions.Dispose();
    20.         rotations.Dispose();
    21.     }

    So now my animation ComponentSystem works on GameObjects driven by entities.

    Next step was to disable the mesh on the prefab passed on to the ECS system ( but keeping all necesery components like colliders). No double meshes anymore :)

    I re enabled it again to instantiate the GO.

    It's ugly but works. I will be optimising it for sure later.

    I was expecting somone will be interested in this topic and will help me. But talking to myself helped too, so I hope anyone who will find it later on will benefit from my experience. :)
     
    eatbuckshot, alexchesser and Abbrew like this.
  7. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    568
    I’ve been reading your posts. Nicely done. Others will benefit surely. Great first posts. Wish I could have helped but I have not yet touched DOTS.
     
    Janosgolya likes this.
  8. alexchesser

    alexchesser

    Joined:
    Sep 15, 2017
    Posts:
    147
    @Janosgolya hey! thank you so much for posting all the parts of your journey here. I really felt your pain while you were posting your research and implementation journey and didn't have anyone to bounce ideas off of. By persisting in the research and posting the entire story piece by piece you've become the person you wish you could have found at that time for me.

    :)

    Genuinely, thank you.

    A month or so on, are you still happy with the solution? Have you made any improvements?
     
  9. Janosgolya

    Janosgolya

    Joined:
    Jul 19, 2012
    Posts:
    9
    Sorry for not posting the rest of the story - I was extremely busy, but actually I did manage to overcome most of the problems and finish the shot.

    [Warning! Disturbing image ahead - dead man eaten by maggots and flies (he is not really dead, jus an actor ;) ]


    The system worked but it isn't very performant. It's not a big deal as Unity Recorder is frame rate independent and I only needed 24 frames per second. But I manage to simulate 1000 flies above 30fps.... as long as they do not collide with mesh collider. It is like 10 times slower than any other collider in DOTS. Lucky it does not have to be real time.

    I will develop it further, as I want to use this system for my small 'space invaders/tower defence' mashup game. I will remove all animation on the prefab then, as spaceships does not need it, so it should have a huge boost in fps. I just have to optimize it more.
     
    alexchesser likes this.