Search Unity

Third person camera inside JobSystem

Discussion in 'Entity Component System' started by syjgin, Jun 12, 2019.

  1. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    Maybe this was described many times, but currently can not find proper algorithm: I want to rotate camera on mouse move around both target's right and top axis, but currently rotation begins around forward axis too.
    What I want to do:

    I will pass target's Transform to Job later, for now I just want to correcly rotate without roll, with yaw and pitch only.
    How it's done now:
    Code (CSharp):
    1. using Unity.Entities;
    2. using UnityEngine;
    3. using Unity.Transforms;
    4. using Unity.Jobs;
    5. using Unity.Burst;
    6. using Unity.Collections;
    7. using Unity.Mathematics;
    8.  
    9. public class CameraUpdateSystem : JobComponentSystem {
    10.     const string X_MOVEMENT = "Mouse X";
    11.     const string Y_MOVEMENT = "Mouse Y";
    12.  
    13.     const float COEF = 1f;
    14.     const float MIN_ANGLE = -45;
    15.     const float MAX_ANGLE = 45;
    16.     [BurstCompile]
    17.     [RequireComponentTag(typeof(CameraComponent))]
    18.     struct UpdateTranslation : IJobForEach<Translation, Rotation> {
    19.         public float2 MouseMoving;
    20.         public void Execute(ref Translation translation, ref Rotation rotation) {
    21.             Quaternion currentRotation = new Quaternion();
    22.             currentRotation.w = rotation.Value.value.w;
    23.             currentRotation.x = rotation.Value.value.x;
    24.             currentRotation.y = rotation.Value.value.y;
    25.             currentRotation.z = rotation.Value.value.z;
    26.             float xAngle = math.clamp(currentRotation.eulerAngles.x, MIN_ANGLE, MAX_ANGLE);
    27.             float yAngle = currentRotation.eulerAngles.y;
    28.             rotation.Value = rotation.Value *
    29.             Quaternion.AngleAxis(-MouseMoving.y * COEF, Quaternion.Euler(0, -yAngle, 0) * Vector3.right) *
    30.             Quaternion.AngleAxis(-MouseMoving.x * COEF, Quaternion.Euler(-xAngle, 0, 0) * Vector3.up);
    31.         }
    32.     }
    33.  
    34.     protected override JobHandle OnUpdate(Unity.Jobs.JobHandle inputDeps) {
    35.         float2 mouseMovement = new float2();
    36.         if(Input.GetMouseButton(1)) {
    37.             mouseMovement[0] = Input.GetAxis(X_MOVEMENT);
    38.             mouseMovement[1] = Input.GetAxis(Y_MOVEMENT);
    39.         }
    40.         var job = new UpdateTranslation() {
    41.             MouseMoving = mouseMovement
    42.             };
    43.         return job.Schedule(this, inputDeps);
    44.     }
    45. }
    CameraComponent here is linked to target point game object with camera as child in coordinates (0,0,-10)
    How can I fix rotation?
     
  2. syjgin

    syjgin

    Joined:
    Feb 13, 2015
    Posts:
    136
    UPD: discovered that roll's cause is incorrect clamping, but I can not handle min & max angle correcly. It is possible to debug something, that happens inside job, or I have to convert it to ComponentSystem?
     
  3. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    Try this:

    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Mathematics;
    6. using Unity.Transforms;
    7. using static Unity.Mathematics.math;
    8. using quaternion = Unity.Mathematics.quaternion;
    9. using Time = UnityEngine.Time;
    10. using UnityEngine;
    11.  
    12. public struct RotateCameraSpeed : IComponentData
    13. {
    14.     public float Value;
    15. }
    16.  
    17. public struct RotateCameraPitchYaw : IComponentData
    18. {
    19.     public float2 Value;
    20. }
    21.  
    22. public class RotateCameraSystem : JobComponentSystem
    23. {
    24.     const string X_MOVEMENT = "Mouse X";
    25.     const string Y_MOVEMENT = "Mouse Y";
    26.  
    27.     const float MIN_ANGLE = -PI / 2;
    28.     const float MAX_ANGLE = PI / 2;
    29.  
    30.     [BurstCompile]
    31.     private struct RotateJob : IJobForEach<Rotation, RotateCameraPitchYaw, RotateCameraSpeed>
    32.     {
    33.         public float DeltaTime;
    34.         public float2 MouseMovement;
    35.  
    36.         public void Execute(ref Rotation rotation, ref RotateCameraPitchYaw pitchYaw, [ReadOnly] ref RotateCameraSpeed speed)
    37.         {
    38.             pitchYaw.Value += MouseMovement.yx * speed.Value * DeltaTime;
    39.             pitchYaw.Value.x = clamp(pitchYaw.Value.x, MIN_ANGLE, MAX_ANGLE);
    40.             rotation.Value = mul(quaternion.RotateY(pitchYaw.Value.y), quaternion.RotateX(pitchYaw.Value.x));
    41.         }
    42.     }
    43.  
    44.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    45.     {
    46.         float2 mouseMovement = new float2();
    47.         if (Input.GetMouseButton(1))
    48.         {
    49.             mouseMovement[0] = Input.GetAxis(X_MOVEMENT);
    50.             mouseMovement[1] = Input.GetAxis(Y_MOVEMENT);
    51.         }
    52.         return new RotateJob { DeltaTime = Time.deltaTime, MouseMovement = mouseMovement }.Schedule(this, inputDeps);
    53.     }
    54. }
     
  4. LazyGameDevZA

    LazyGameDevZA

    Joined:
    Nov 10, 2016
    Posts:
    143
    Why not rather have Cinemachine take care of it? I'm sure having a GameObject that's synced with an Entity would suffice to ensure that Cinemachine can track it?
     
  5. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Dots based cinemachine is being worked on. Aiming for October timeframe for preview release.
     
    psuong, RaL, eizenhorn and 1 other person like this.
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    This is a complete example of an orbit camera in DOTS. Feature-wise it's an equivalent of what you can find in this demo, except I haven't implemented obstruction checks yet (but with Unity.Physics you can do that in jobs without problems)


    note: "ConstrainEulerAngle" is not super well named. It just loops back the value of an angle between -180 and 180 degrees if it exceeds those limits

    Code (CSharp):
    1. [BurstCompile]
    2.     public struct ControlCameraJob : IJobForEachWithEntity<OrbitCamera, PlayerCameraInputs>
    3.     {
    4.         public float dt;
    5.  
    6.         public ComponentDataFromEntity<Translation> TranslationFromEntity;
    7.         public ComponentDataFromEntity<Rotation> RotationFromEntity;
    8.  
    9.         public void Execute(Entity camEntity, int index, ref OrbitCamera orbitCam, ref PlayerCameraInputs camInput)
    10.         {
    11.             if (TranslationFromEntity.Exists(orbitCam.AnchorEntity) && RotationFromEntity.Exists(orbitCam.AnchorEntity))
    12.             {
    13.                 Translation camTranslation = TranslationFromEntity[camEntity];
    14.                 Rotation camRotation = RotationFromEntity[camEntity];
    15.                 Translation anchorTranslation = TranslationFromEntity[orbitCam.AnchorEntity];
    16.                 Rotation anchorRotation = RotationFromEntity[orbitCam.AnchorEntity];
    17.  
    18.                 // Find desired values from input
    19.                 orbitCam.DesiredHorizontalAngle = orbitCam.DesiredHorizontalAngle + (camInput.Look.x * orbitCam.RotationSpeed * dt);
    20.                 CharacterUtilities.ConstrainEulerAngle(ref orbitCam.DesiredHorizontalAngle);
    21.                 orbitCam.DesiredVerticalAngle = math.clamp(orbitCam.DesiredVerticalAngle + (camInput.Look.y * orbitCam.RotationSpeed * dt), orbitCam.MinVerticalAngle, orbitCam.MaxVerticalAngle);
    22.                 CharacterUtilities.ConstrainEulerAngle(ref orbitCam.DesiredVerticalAngle);
    23.                 orbitCam.DesiredDistance = math.clamp(orbitCam.DesiredDistance + (-camInput.Zoom * orbitCam.DistanceSpeed * dt), orbitCam.MinDistance, orbitCam.MaxDistance);
    24.  
    25.                 // Smooth rotation
    26.                 float rotationInterpolant = CharacterUtilities.GetSharpnessInterpolant(orbitCam.RotationSharpness, dt);
    27.                 orbitCam.HorizontalAngle = math.lerp(orbitCam.HorizontalAngle, orbitCam.DesiredHorizontalAngle, rotationInterpolant);
    28.                 orbitCam.VerticalAngle = math.lerp(orbitCam.VerticalAngle, orbitCam.DesiredVerticalAngle, rotationInterpolant);
    29.                 camRotation.Value = math.mul(quaternion.EulerXYZ(math.radians(orbitCam.VerticalAngle), math.radians(orbitCam.HorizontalAngle), 0f), anchorRotation.Value);
    30.  
    31.                 // Smooth distance
    32.                 float distanceInterpolant = CharacterUtilities.GetSharpnessInterpolant(orbitCam.DistanceSharpness, dt);
    33.                 orbitCam.Distance = math.lerp(orbitCam.Distance, orbitCam.DesiredDistance, distanceInterpolant);
    34.                 float3 cameraFwd = math.mul(camRotation.Value, CharacterConsts.Forward);
    35.                 camTranslation.Value = anchorTranslation.Value + (-cameraFwd * orbitCam.Distance);
    36.  
    37.                 // Write back values
    38.                 TranslationFromEntity[camEntity] = camTranslation;
    39.                 RotationFromEntity[camEntity] = camRotation;
    40.             }
    41.         }
    42.     }
    To be honest I don't think there's actually a point to handling this in a job, because 99.999% of all games will never have more than 1 or 4 (splitscreen) actively-controlled camera at a time. But I wanted to try it just as an exercise anyway


    ....but with all of this said, I don't understand what makes your particular issue DOTS-related. Isn't it just a math problem?
     
    Last edited: Jun 13, 2019
    pal_trefall and florianhanke like this.