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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Replace an Entity with GameObject based on distance

Discussion in 'Entity Component System' started by thurd666, Apr 20, 2023.

  1. thurd666

    thurd666

    Joined:
    May 12, 2022
    Posts:
    7
    After a lot of time, effort and questions on this forum I finally managed to successfully create an asteroid (cubes right now) field that's really fast (on my PC I can get to 300k entities and I need around 1% of that), behaves like I want it to (player is surrounded by them and if he moves they are created and destroyed appropriately) and runs well on Quest 2 (tested 3k).

    But right now I'm facing a problem of actually interacting with those asteroids. By interacting I mean colliding the player, mining them (hopefully with mesh deformation) etc.
    My initial thought was to replace Entities with GameObjects as the player gets closer to them and since there won't be more than a couple at a time, so performance should not be a problem. But I have no idea how to instantiate the same prefab as a GameObject inside a System and if such operation won't be visible to the player?
    But as I read more about Collisions and Physics in ECS I started to wonder if maybe I should try to work on "upgrading" those Entities to handle everything? But it seems like I might have to work 10x as hard on this rather than just go with GameObject approach. Especially if performance won't necessitate this.
     
  2. MatanYamin

    MatanYamin

    Joined:
    Feb 2, 2022
    Posts:
    109
    What about trying to "present" an entity with an empty GameObject that has a collider on it with a script attached to it?
    That way, when the collider detects something, you can get the position & rotation of that Entity that belongs to it and place there a new GameObject and do whatever you want with it (while disabling the entity).
     
  3. thurd666

    thurd666

    Joined:
    May 12, 2022
    Posts:
    7
    Oh, I already solved the problem of "when" to make the switch. In my creation/destruction of entities I use distance so I know when a player gets closer to an Entity and I can do something based on that.

    But my problem is actually getting a GameObject to Instantiate from a System. I cannot get GameObjects inside a ComponentData (and it doesn't feel "right") because I get an error about non-nullable types.
    I wanted to create a GameObject when a player gets close to an Entity, make it exactly the same as Entity Prefab and hide/disable the Entity itself. If done under a frame I don't think it will be noticeable. But the case is I'd prefer doing it from a System because I'm already going through Entity distance data for destruction purposes and I'd rather do this GameObject to Entity switch inside the same loop (performance reasons).

    Code (CSharp):
    1. protected override void OnUpdate()
    2.     {
    3.         //.... Not relevant Code
    4.        
    5.         NativeArray<Entity> asteroidDestroyArray = asteroidEntityQuery.ToEntityArray(Allocator.Temp);
    6.  
    7.         foreach (Entity entity in asteroidDestroyArray)
    8.         {
    9.             LocalTransform entityLocalTransform = EntityManager.GetComponentData<LocalTransform>(entity);
    10.             if (verifyOutOfBounds(entityLocalTransform.Position))
    11.             {
    12.                 EntityManager.DestroyEntity(entity);
    13.             }
    14.             //  Test if I get closer then it dissapears - seems to be working
    15.             if (convertToGameObject(entityLocalTransform.Position))
    16.             {
    17.                 // Here I want to Instantiate a GameObject
    18.             }
    19.         }
    20.     }
     
  4. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    You can't add GameObjects to an IComponentData but you can add objects to the Entity via AddComponentObject and can then make the Entity track the GameObject's transform.
    I don't use entities graphics so not sure what components you need to remove there but if you just want the rigidbody motion of the GameObject, you could always keep the entities graphic representation and not add graphics to the GameObject.
    Code below instantiates from a single prefab but you'd ideally get it from an object pool or a singleton with a list of prefabs you can instantiate from.

    You can adapt code below, but it just demonstrates the three phases of animating a pure entity, then adding a GameObject to it, then the GameObject animates the entity.
    Code (CSharp):
    1. GameObject _prefab;
    2.  
    3. protected override void OnUpdate()
    4. {
    5.     // Update Entity LocalTransform
    6.     Entities
    7.     .ForEach( (Entity entity, ref LocalTransform localTransform) => {
    8.         localTransform = localTransform.RotateZ(math.radians(10) * SystemAPI.Time.DeltaTime);
    9.     })
    10.     .WithAll<Asteroid>()
    11.     .WithNone<UnityEngine.Transform>()
    12.     .Run();
    13.  
    14.  
    15.     // Instantiate and Add GameObject
    16.     Entities
    17.     .ForEach( (Entity entity, in LocalTransform localTransform) => {
    18.         // if instantiate condition
    19.         var gameObject = GameObject.Instantiate(_prefab, localTransform.Position, localTransform.Rotation);
    20.         EntityManager.AddComponentObject(entity, gameObject.transform);
    21.         EntityManager.AddComponent<CopyTransformFromGameObject>(entity);
    22.     })
    23.     .WithAll<Asteroid>()
    24.     .WithNone<UnityEngine.Transform>()
    25.     .WithStructuralChanges()
    26.     .WithoutBurst()
    27.     .Run();
    28.  
    29.  
    30.     // GameObject updates Entity LocalTransform
    31.     Entities
    32.     .ForEach( (ref LocalTransform localTransform, in UnityEngine.Transform unityTransform) => {
    33.         localTransform = LocalTransform.FromPositionRotation(unityTransform.position, unityTransform.rotation);
    34.     })
    35.     .WithAll<CopyTransformFromGameObject>()
    36.     .WithoutBurst()
    37.     .Run();
    38. }
     
    thurd666 likes this.