Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Hybrid Entities UI in ECS 1.0

Discussion in 'Entity Component System' started by OlympusGames, Jan 19, 2023.

  1. OlympusGames

    OlympusGames

    Joined:
    Feb 22, 2017
    Posts:
    7
    bb8_1 likes this.
  2. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    For anything not supported by entities, you need to keep a GameObject that handles the Monobehavior components and make a system to synch it's position with the entity.
    This can be done with class IComponentData.

    I explain that for the animation of characters in this video


    For all UI in world space I would recommend avoiding UI Toolkit as it doesn't support yet world space UI. So you should be able to apply the same principle as for animation with a UI canvas prefab.
     
    bb8_1 likes this.
  3. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    I'd just add that if you use CompanionLink which is what Unity adds when you bake those hybrid components, then Unity will keep the transform in sync for you. Then just add whatever other components you want to manipulate directly to Entity.
    Code (CSharp):
    1. EntityManager.AddComponentObject(playerHealthEntity, new CompanionLink{Companion = playerHealthGameObject});
    2. EntityManager.AddComponentObject(playerHealthEntity, playerHealthGameObject.GetComponent<SpriteRenderer>());
     
    bb8_1 likes this.
  4. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    I would be careful with that since it's used by unity for some built-in support of some of the GO features like particule system for instance.
    Since the companion link is a component it can only have one object companion. If you use it with another supported by unity GO feature you might run into conflicts.

    And the companion link is only a one way link from entities to GO. You don't have the other way around.
     
  5. AlexAdach

    AlexAdach

    Joined:
    Jan 25, 2023
    Posts:
    24
    is there a list somewhere where you can see what is and isn't supported by entities?

    I tried putting a textmeshpro canvas above my capsule in my character prefab. And it doesn't seem to render that when it instantiates it.
     
  6. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    There is a c sharp file in the package that list all the supported type. Can't get the name right now but I think I show it in the video.
     
  7. AlexAdach

    AlexAdach

    Joined:
    Jan 25, 2023
    Posts:
    24
    Wanted to thank you. The video was very helpful. Been giving you thumbs up on all to help with algorithm :).

    Two questions: For the time being, I'm just using an empty gameobject with a canvas and textmespro object inside as my "presentation component". I'm seeing the GO getting spawned inside the subscene, in edit mode I can see the canvas moving around with the entities, but I don't see any text.

    2 - When my entities are destroyed by my despawner system, the associated gameobject is destroyed properly as well. If I quit playmode through the editor, it does not. I've tried a few different approaches. Added an OnApplicationQuit callback to the EntityGameObject component, that didn't seem to work. Tried iterating through a EntityGameObject[] with Resource.FindObjectsOfTypeAll in my despawner system's on destroy. They all persist. Any ideas?
     
  8. AlexAdach

    AlexAdach

    Joined:
    Jan 25, 2023
    Posts:
    24
    For anyone struggling with this in the future. Here's how I got it to work. Wayn's approach was causing issues with TMPro since it doesn't seem to want to render if it's inside an entity subscene.

    I have a PresentationGOWrapper Monobehavior. I have this on a managed component. When I initially add this component to my character entities, I reach out to a singleton script in the Monobehavior side. That singleton instantiates a prefab, and assigns this wrapper component to it, and returns that wrapper component so that it can be easily references in ECS land.

    Just figured it out like 10 min ago. The goal I think is to do as many calculations as possible in burst compiled code in jobs. So I need to implement that. Doing the look rotation calculation so that the text faces the camera inside the monobehavior wrapper is terrible for performance.

    Code (CSharp):
    1.     public class CharacterPresentationManagerSingleton : MonoBehaviour
    2.     {
    3.         public GameObject CharacterPresentationPrefab;
    4.  
    5.         public static CharacterPresentationManagerSingleton Instance { get; private set; }
    6.         private void Awake()
    7.         {
    8.             // If there is an instance, and it's not me, delete myself.
    9.             if (Instance != null && Instance != this)
    10.             {
    11.                 Destroy(this);
    12.             }
    13.             else
    14.             {
    15.                 Instance = this;
    16.             }
    17.         }
    18.  
    19.         public CharacterPresentationGOWrapper NewHoverText(int entityIndex)
    20.         {
    21.             var newGO = GameObject.Instantiate(CharacterPresentationPrefab, new Vector3(0, 0, 0), Quaternion.identity);
    22.             //So all the new presentation objects children of this empty, for organization. Don't know if this is a bad idea or not.
    23.             newGO.transform.parent = this.transform;
    24.  
    25.             var wrapper = newGO.AddComponent<CharacterPresentationGOWrapper>();
    26.             wrapper.EntityIndex = entityIndex;
    27.             return wrapper;
    28.         }
    29.     }
    Code (CSharp):
    1. public class CharacterPresentationGOWrapper : MonoBehaviour
    2.     {
    3.         // Start is called before the first frame update
    4.         //public GameObject HoverText;
    5.         public int EntityIndex;
    6.         private Transform _rotateCameraChild;
    7.         private Transform _rotateCharacterChild;
    8.         private Transform _mainCameraTransform;
    9.         private Canvas _hoverCanvas;
    10.         private TextMeshProUGUI _hoverText;
    11.  
    12.         public void Awake()
    13.         {
    14.             _rotateCameraChild = transform.Find("RotateCamera");
    15.             _rotateCharacterChild = transform.Find("RotateCharacter");
    16.  
    17.             _mainCameraTransform = Camera.main.transform;
    18.             _hoverCanvas = GetComponentInChildren<Canvas>();
    19.             _hoverText = GetComponentInChildren<TextMeshProUGUI>();
    20.  
    21.             _hoverCanvas.transform.localPosition = new Vector3(0f,2f,0f);
    22.         }
    23.  
    24.         public void UpdateTransform(Vector3 position, Quaternion rotation)
    25.         {
    26.             transform.position = position;
    27.             _rotateCharacterChild.transform.rotation = rotation;
    28.  
    29.             _hoverText.text = position.ToString();
    30.             RotateToCamera();
    31.         }
    32.  
    33.         public void Dispose()
    34.         {
    35.             if (gameObject != null)
    36.                 Destroy(gameObject);
    37.         }
    38.  
    39.         private void RotateToCamera()
    40.         {
    41.             var directionToCamera = transform.position - _mainCameraTransform.position;
    42.             var rotationToCamera = Quaternion.LookRotation(directionToCamera, Vector3.up);
    43.             _rotateCameraChild.transform.rotation = rotationToCamera;
    44.         }
    45.  
    46.     }

    and where it gets added to the character entity

    Code (CSharp):
    1.             var ecbBOS = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged);
    2.  
    3.             //Instantiate Game Objects for characters that do not have one assigned yet.
    4.             foreach (var (character, entity) in SystemAPI.Query<CharacterTag>().WithEntityAccess())
    5.             {
    6.                 //Instantiates presentation Game objects and assigns them to the character entities.
    7.                 if (!SystemAPI.ManagedAPI.HasComponent<CharacterPresentationGOData>(entity))
    8.                 {
    9.                     var newHoverText = CharacterPresentationManagerSingleton.Instance.NewHoverText(entity.Index);
    10.                     ecbBOS.AddComponent(entity, new CharacterPresentationGOData() { PresentationGOWrapper = newHoverText });
    11.                 }
    12.             }
    and the component
    Code (CSharp):
    1. public class CharacterPresentationGOData : IComponentData, IDisposable
    2. {
    3.     public CharacterPresentationGOWrapper PresentationGOWrapper;
    4.  
    5.     public void Dispose()
    6.     {
    7.         if (PresentationGOWrapper != null)
    8.         PresentationGOWrapper.Dispose();
    9.     }
    10. }
     
    NosTek likes this.