Search Unity

Is it possible to use Hybrid and Pure at the same time?

Discussion in 'Entity Component System' started by Reshima, Nov 1, 2018.

  1. Reshima

    Reshima

    Joined:
    Dec 10, 2012
    Posts:
    51
    So my goal is simple: I have a Player Entity (Pos, Rot, Speed, Player) and I'd like to have a Camera that follows the player entity. The player controls the player entity via a simple input system. I just want to make the camera follow the player entity position.

    I was able to do it using Hybrid approach but no luck with Pure, probably because of my lack of understanding.

    I searched the forums and I found these statements:

    1. Don't use Camera with ECS because I won't be instantiating hundreds of cameras... but I disagree because I'd like to use the same architecture everywhere in my game and squeeze perf benefits as much as possible.

    2. Camera is still a game object and because of that it is not yet supported in Pure ECS.

    So I thought: Well, I could make the Player entity part with Pure ECS and then do Hybrid ECS for the Camera system but I can't get it to work. When I try to access the Player entity position, I keep getting errors about transform pointers:

    Position must be an unsafe pointer to the Unity.Transforms.Position. Like this: Unity.Transforms.Position* Position;

    Before I go even further, I'd like to know if this is even possible. Any help is appreciated.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    Yes it works fine. I'd need to see some code to see what you're doing wrong.
     
    Antypodish likes this.
  3. Reshima

    Reshima

    Joined:
    Dec 10, 2012
    Posts:
    51
    The code is below. I'm sure I'm missing something:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Pure.Components;
    5. using Unity.Entities;
    6. using Unity.Mathematics;
    7. using Unity.Transforms;
    8. using UnityEngine;
    9. public class PlayerCameraSystem : ComponentSystem
    10. {
    11.     private struct EntityCamera
    12.     {
    13.         public readonly int Length;
    14.         public GameObjectArray GameObject;
    15.         public ComponentDataArray<PlayerCamera> PlayerCamera;
    16.         public ComponentDataArray<Position> Position;
    17.         public ComponentDataArray<Rotation> Rotation;
    18.     }
    19.     private struct EntityPlayer
    20.     {
    21.         public readonly int Length;
    22.         public GameObjectArray GameObject;
    23.         public ComponentDataArray<Player> Player;
    24.         public ComponentDataArray<Position> Position;
    25.     }
    26.     [Inject] private EntityCamera _entityCamera;
    27.     [Inject] private EntityPlayer _entityPlayer;
    28.     protected override void OnUpdate()
    29.     {
    30.         if (_entityCamera.Length == 0 || _entityPlayer.Length == 0)
    31.         {
    32.             Debug.Log("--- NOPE ---");
    33.             Debug.Log(_entityCamera.Length); // 1
    34.             Debug.Log(_entityPlayer.Length); // 0
    35.             return;
    36.         }
    37.         for (int i = 0; i < _entityPlayer.Length; ++i)
    38.         {
    39.             float3 currentPos = _entityCamera.Position[i].Value;
    40.             float3 desiredPos = _entityPlayer.Position[i].Value + new float3(0, 18f, -10f);
    41.             float3 smoothedPos = math.lerp(currentPos, desiredPos, 10f * Time.deltaTime);
    42.             Position cameraPos = _entityCamera.Position[i];
    43.             cameraPos.Value = smoothedPos;
    44.             quaternion rotation = quaternion.LookRotation(_entityPlayer.Position[i].Value - currentPos, math.up());
    45.             Rotation cameraRot = _entityCamera.Rotation[i];
    46.             cameraRot.Value = math.slerp(_entityCamera.Rotation[i].Value, rotation, Time.deltaTime * 0.25f);
    47.         }
    48.     }
    49. }
    50.  
    I keep getting a 0 length _entityPlayer. The EntityPlayer is the one I'm creating with pure ECS:

    Code (CSharp):
    1. Entity playerEntity = EntityManager.CreateEntity(PlayerArchetype);
    2.             EntityManager.SetComponentData(playerEntity, new Position() { Value = new float3(0f, 1f, 0f) });
    3.             EntityManager.SetComponentData(playerEntity, new Rotation() { Value = quaternion.identity });
    4.             EntityManager.SetComponentData(playerEntity, new PlayerInput() { Horizontal = 0, Vertical = 0, MousePosition = float2.zero });
    5.             EntityManager.SetComponentData(playerEntity, new MoveSpeed() { Value = 6 });
    6.             EntityManager.AddSharedComponentData(playerEntity, PlayerLook);
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    The pure entity has no game object attached yet you're requiring a GameObjectArray so you're never going to get a player match.
     
  5. Reshima

    Reshima

    Joined:
    Dec 10, 2012
    Posts:
    51
    Yes, thanks for pointing it out, it was obvious but I couldn't see it.

    I also realized I can't really modify position like that for my Camera, I need to do something like:

    Code (CSharp):
    1. _entityCamera.Position[i] = new Position { Value = smoothedPos }
    That updates the Position Component but it doesn't work for Hybrid components. As far as I understand, the game object still uses the old Transfom which controls the position of my camera, so I need to update it via transform, correct?

    So this is what worked for me:

    Code (CSharp):
    1. private struct EntityCamera
    2.     {
    3.         public readonly int Length;
    4.         public GameObjectArray GameObjects;
    5.         public ComponentDataArray<PlayerCamera> PlayerCameras;
    6.     }
    7.  
    8.     private struct EntityPlayer
    9.     {
    10.         public readonly int Length;
    11.         public ComponentDataArray<Player> Player;
    12.         public ComponentDataArray<Position> Position;
    13.     }
    14.  
    15.     [Inject] private EntityCamera _entityCamera;
    16.     [Inject] private EntityPlayer _entityPlayer;
    17.  
    18.     protected override void OnUpdate()
    19.     {
    20.         if (_entityCamera.Length == 0 || _entityPlayer.Length == 0) return;
    21.  
    22.         float deltaTime = Time.deltaTime;
    23.  
    24.         for (int i = 0; i < _entityPlayer.Length; ++i)
    25.         {
    26.             GameObject cameraGO = _entityCamera.GameObjects[i];
    27.  
    28.             float3 currentPos = cameraGO.transform.position;
    29.             float3 desiredPos = _entityPlayer.Position[i].Value + new float3(0, 18f, -10f);
    30.             float3 smoothedPos = math.lerp(currentPos, desiredPos, 10f * deltaTime);
    31.             cameraGO.transform.position = smoothedPos;
    32.  
    33.             Quaternion currentRot = cameraGO.transform.rotation;
    34.             Quaternion desiredRot = Quaternion.LookRotation(_entityPlayer.Position[i].Value - currentPos, math.up());
    35.             Quaternion smoothedRot = math.slerp(currentRot, desiredRot, deltaTime);
    36.             cameraGO.transform.rotation = smoothedRot;
    37.         }
    38.     }
    I posted my solution here in case anyone has any issues with this in the future.
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    You can just add the, CopyTransformToGameObject component, to the hybrid object and the gameobject transform will be updated automatically when you make changes to the position/rotation components.
     
  7. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    Have you run into cases where you want ECS to access more than just transform?
     
    Last edited: Nov 3, 2018
  8. Reshima

    Reshima

    Joined:
    Dec 10, 2012
    Posts:
    51
    This is good, but I found that it won't respect the NavMesh using this.
     
    laurentlavigne likes this.
  9. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    I don't actually use hybrid anymore [at least I use no GameObjectEntity] as I ended up converting my entire project to pure.

    The only components I still need access to are stuff like, Camera, Light etc. Originally I add these as shared components but now I just add them directly as objects to the entity just like GameObjectEntity does for transform etc (this requires a little reflection because the methods are internal.)

    I also wrote a couple of wrapper systems for third party tools such as a CineMachine.Hybrid system etc.

    If you're using navmesh you probably want to go the other way (CopyTransformFromGameObject) and drive your entity position/rotation from the navmesh.