Magic Interpolation?

I removed all damping on the Cinemachine and set the camera update to manual. the rotation is working fine however, it seems the rotation starts after the camera rotates and seems to be behind by a few frames. Any ideas on how I remove this “start delay”?

Rotation Code if it helps (Using ECS…please ignore the poor structure, I’m still debugging)

    Entities
            .WithName("CinemachineECSJob")
            .WithoutBurst()
            .WithAll<CinemachineECSData>()
    //        .WithReadOnly(world)
    //        .WithReadOnly(timeAhead)
    //        .WithReadOnly(systemData)
            .ForEach((Entity entity, CinemachineECSConverterComponent monoBehaviour, ref CinemachineECSData cinemachineECSData) =>
            {
                // update the GameObjest's position (this is the gameobject the cinemachine camera is following)
                monoBehaviour.transform.position = systemData.Position;
                // update the rotation of the Target Entity base on cinemachine's rotation
                // NOTE: this is coded with the CinemachineFreeLook Component in mind
                CinemachineFreeLook freeLook = monoBehaviour.CinemachineGO.GetComponent<CinemachineFreeLook>();
                CinemachineBrain brain = monoBehaviour.MainCamera.GetComponent<CinemachineBrain>();
                if (freeLook != null)
                {
                    Debug.Log(math.degrees(systemData.internalData.CurrentRotationAngle) + "::" + freeLook.m_XAxis.Value);
                    float3 up = Core.GameSettings.DetermineUpUsingGravity(Core.GameSettings.GameSettingsPhysicsStep.Gravity,systemData.controllerComponentData.Gravity);
                    systemData.internalData.CalculateRotation = false;
                    systemData.internalData.CurrentRotationAngle = math.radians(freeLook.m_XAxis.Value);
                    Rotation r = GetComponent<Rotation>(systemData.TargetEntity);
                    SetComponent(systemData.TargetEntity, new Rotation { Value = math.normalize(math.mul(r.Value,quaternion.AxisAngle(up,systemData.internalData.CurrentRotationAngle-systemData.internalData.LastRotationAngle))) });
                    systemData.internalData.LastRotationAngle = systemData.internalData.CurrentRotationAngle;
                }
                else Debug.LogWarning("CinemachineECSJob: Failed to get CinemachineFreeLook Component!");
                SetComponent(systemData.TargetEntity, systemData.internalData);
                brain.ManualUpdate();
            }).Run();

Are you trying to update the rotation of the follow target based on the rotation of the camera? If so, then you’re creating a feedback loop because the FreeLook’s Binding Mode is Lock To Target - which means that it takes its rotation from the follow target. Try setting the binding mode to World Space.

My bad i forgot to add that the CameraParent target that the Cinemachine camera is set to follow and rotate around is not being rotated through code. it is a child (using ECS’s PreviousParent) of the GameObject I am applying the rotate code to.

But now that you mention it, since it is a child of the thing I’m rotating the it’s LocalToWorld is being affected by the parent according to this

I’ll update the code to remove the rotation applied to the parent and let you know.

Just set your vcam’s Binding Mode to world space - much simpler. Lock to Target doesn’t make sense for this kind of controller.

so trying to negate the rotation but all that achieved was glitchiness every 10-20 frames so I changed some code and did some tests and think it might be a timing issue. I think the ECS Job that sets the rotation may be faster or slower than the CinematicCamera GameObect.

the System has 2 jobs in it but the one that set the rotation to the “Player entity” (not CameraParent) finishes in less than Time.DeltaTime.

the binding has been set to World Space but no change is noticed

I just realized this is not true and I’m manually updating the position of the CameraParent transform in the Job as well so the Parent issue doesn’t apply

The way you described your original problem makes it sound like a feedback loop.

Maybe you can post your object hierarchy so I can get a better understanding of what’s parented to what - cameras and targets included

6583867--747712--upload_2020-12-2_20-48-1.png
[Edit]: forgot to dropdown CameraParent in the image

Anything that says has “Manager” in it is an empty game object i use to organize.

The CameraParent is what Cinemachine Follows and looks at.

In the Job, the transform position of CameraParent is being set. The Rotation of ElectrodeDummy is being set based on the CinemachineFreeLook current rotation value.

    Entities
            .WithName("CinemachineECSJob")
            .WithoutBurst()
            .WithAll<CinemachineECSData>()
    //        .WithReadOnly(world)
    //        .WithReadOnly(timeAhead)
            .WithReadOnly(systemData)
            .ForEach((Entity entity, CinemachineECSConverterComponent monoBehaviour, ref CinemachineECSData cinemachineECSData) =>
            {
                CinemachineBrain brain = monoBehaviour.MainCamera.GetComponent<CinemachineBrain>();
                brain.ManualUpdate();
                // update the CameraParent's position (this is the gameobject the cinemachine camera is following)
                monoBehaviour.transform.position = systemData.Position;
                // update the rotation of the Target Entity base on cinemachine's rotation
                // NOTE: this is coded with the CinemachineFreeLook Component in mind
                CinemachineFreeLook freeLook = monoBehaviour.CinemachineGO.GetComponent<CinemachineFreeLook>();
                if (freeLook != null)
                {
                    //            Debug.Log(math.degrees(systemData.internalData.CurrentRotationAngle) + "::" + freeLook.m_XAxis.Value);
                    float3 up = Core.GameSettings.DetermineUpUsingGravity(Core.GameSettings.GameSettingsPhysicsStep.Gravity, systemData.controllerComponentData.Gravity);
                    systemData.internalData.CalculateRotation = false;
                    // quanternion is in radians so we have to convert
                    systemData.internalData.CurrentRotationAngle = math.radians(freeLook.m_XAxis.Value);

                    // difference of (current - previous) rotation (in radians)
                    float difference = systemData.internalData.CurrentRotationAngle - systemData.internalData.LastRotationAngle;

                    // get rotation of ElectrodeDummy
                    Rotation r = GetComponent<Rotation>(systemData.TargetEntity);
                    // add rotation difference to ElectrodeDummy Rotation
                    SetComponent(systemData.TargetEntity, new Rotation { Value = math.normalize(math.mul(r.Value, quaternion.AxisAngle(up, difference))) });
                    // set last rotation to current rotation for later calculation
                    systemData.internalData.LastRotationAngle = systemData.internalData.CurrentRotationAngle;

                }
                else Debug.LogWarning("CinemachineECSJob: Failed to get CinemachineFreeLook Component!");
                SetComponent(systemData.TargetEntity, systemData.internalData);
            }).Run();

Your image isn’t coming through, but the hierarchy is visible in a previous post (duh) so it’s ok. I’m having a little trouble understanding what you’re trying to do with the camera. Can you describe in words how you want the camera to behave and its relationship with the target?

I want the Cinemachine Camera to rotate around and look at the CameraParent object. This is working fine. However, when I move my mouse i notice there is a very slight rotation delay (a few frames, barely noticable) on the ElectrodeDummy GameObject (the rotation is set in code and the GameObject not attached to Cinemachine Camera or targeted by it).

Now that i think about this could be an ECS issue rather than a Cinemachine issue.

If you want i can make a short video on it and post it.

So ElectrodeDummy (which I understand is a child of the non-rotating CameraParent object) is supposed to rotate to match the main camera’s Y rotation? Is that correct?

ElectrodeDummy and CameraParent do not have a parent/child relationship. In the ECS Job i set the position of CameraParent to ElectrodeDummy manually.

// update the CameraParent's position (this is the gameobject the cinemachine camera is following)
                // systemData.Position is a float3 representing the position of ElectrodeDummy
                CameraParent.transform.position = systemData.Position;

and yes, ElectrodeDummy is supposed to match the main camera’s Y rotation.

So:

  • Camera is tracking position of CameraParent. Binding mode is worldspace.
  • User moves mouse to rotate camera around CamerraParent.
  • You have an ECS job to rotate ElectrodeDummy to match, and then also set position of ElectrodeDummy to match CameraParent
  • You are observing a lag of several frames in the orientation of ElectrodeDummy

You have a ForEach loop that’s updating the CM Brain. That should happen only once per frame. How many times does your loop execute each frame?

The ECS job moves the CameraParent to the ElectrodeDummy’s (Electrode Dummy is a general Entity that i plan to have many of so i have a different system handle them)

But It’s Fixed :smile:

I moved the brain.ManualUpdate() around to other functions and found that all i needed was the the ManualUpdate() to be called before the applied rotation and after the call to set the CameraParent’s Position.

Entities
            .WithName("CinemachineECSJob")
            .WithoutBurst()
            .WithAll<CinemachineECSData>()
            .WithReadOnly(systemData)
            .ForEach((Entity entity, CinemachineECSConverterComponent monoBehaviour, ref CinemachineECSData cinemachineECSData) =>
            {
                // update the CameraParent's position (this is the gameobject the cinemachine camera is following)
                // systemData.Position is a float3 representing the position of ElectrodeDummy
                monoBehaviour.transform.position = systemData.Position;
               
                // update HERE!
                CinemachineBrain brain = monoBehaviour.MainCamera.GetComponent<CinemachineBrain>();
                brain.ManualUpdate();
               
                // update the rotation of the Target Entity base on cinemachine's rotation
                // NOTE: this is coded with the CinemachineFreeLook Component in mind
                CinemachineFreeLook freeLook = monoBehaviour.CinemachineGO.GetComponent<CinemachineFreeLook>();
                if (freeLook != null)
                {
                    //            Debug.Log(math.degrees(systemData.internalData.CurrentRotationAngle) + "::" + freeLook.m_XAxis.Value);
                    float3 up = Core.GameSettings.DetermineUpUsingGravity(Core.GameSettings.GameSettingsPhysicsStep.Gravity, systemData.controllerComponentData.Gravity);
                    systemData.internalData.CalculateRotation = false;
                    // quanternion is in radians so we have to convert
                    systemData.internalData.CurrentRotationAngle = math.radians(freeLook.m_XAxis.Value);

                    // difference of (current - previous) rotation (in radians)
                    float difference = systemData.internalData.CurrentRotationAngle - systemData.internalData.LastRotationAngle;

                    // get rotation of ElectrodeDummy
                    Rotation r = GetComponent<Rotation>(systemData.TargetEntity);
                    // add rotation difference to ElectrodeDummy Rotation
                    SetComponent(systemData.TargetEntity, new Rotation { Value = math.normalize(math.mul(r.Value, quaternion.AxisAngle(up, difference))) });
                    // set last rotation to current rotation for later calculation
                    systemData.internalData.LastRotationAngle = systemData.internalData.CurrentRotationAngle;

                }
                else Debug.LogWarning("CinemachineECSJob: Failed to get CinemachineFreeLook Component!");
                SetComponent(systemData.TargetEntity, systemData.internalData);
            }).Run();

Thank you for your help everything looks perfect now! I can’t wait to furture explore into Cinemachine :smile: