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 How to get the final camera "resting" state?

Discussion in 'Cinemachine' started by JonasMummSeal, Nov 24, 2022.

  1. JonasMummSeal

    JonasMummSeal

    Joined:
    Mar 3, 2021
    Posts:
    4
    Hi, I'm using a CinemachineVirtualCamera with a FollowTarget, a LookAtTarget, a Transposer component and a Composer component. The Transposer and Composer both have damping enabled, so the camera smoothly follows it's targets.
    All the above works swimmingly, but now I'm in a situation where I need to predict the final position and rotation of the camera after it has moved towards the FollowTarget through damping. I suppose I could get myself references to the Transposer and Composer components, and use their offset parameters to reconstruct the camera position. But I'm wondering if there's a more general way to get a camera's resting position. I've tried calling
    CinemachineVirtualCamera.CalculateNewState
    with high delta-time value using reflection, but calling the method also modified the camera instance's state.

    Thanks in advance :)
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,267
    If I understand correctly, you want the position that the camera will end up at, once it has finished damping. Is that correct? If so, then you can call
    vcam.GetCinemachineComponent<CinemachineTransposer>().GetTargetCameraPosition()


    That will give you the target position, but there is no corresponding way to get the target rotation. Can I ask why you need this? Maybe there is an alternative method.
     
    JonasMummSeal likes this.
  3. JonasMummSeal

    JonasMummSeal

    Joined:
    Mar 3, 2021
    Posts:
    4
    Thanks for the response, that's what I was looking for. As to why I need it: In our game we have a (tilted) top down camera users can control freely by dragging with the mouse/touchscreen. So the controls are similar to real time strategy games and city builder games. When a player drags the camera using the mouse/touchscreen, the camera should move in a way so the "world-position" the cursor touches always remains unchanged, even as the screen position of the cursor changes. This method of moving the camera works rather well, but in some instances we ran into feedback loops caused by the circular relation between cursor-screen-position, cursor-world-position and camera-position/rotation: When the camera tilts due to damping effects, the same cursor-screen-position might point to a different cursor-world-position from one frame to the next, which would require camera adjustments. These camera adjustments though might introduce new damping effects, different camera tilt, a changing of the cursor-world-position and therefore additional camera adjustments. The vicious cycle of adjustments causing further adjustments to be necessary goes on and on from there, causing the camera to shake back and forth. In order to break this feedback loop caused by damping, I was thinking about calculating the cursor-world-position from the target state of the camera, rather than the live-state of the camera, which might be subject to damping effects.
    I was able to come up with a solution that fixes our issue at hand. It's nowhere complete for any combination of CineMachine-components, but it get's the job done in our case:

    Code (CSharp):
    1.         public static CameraState GetRestingState(this CinemachineVirtualCamera cam)
    2.         {
    3.             var bodyComponent = cam.GetCinemachineComponent(CinemachineCore.Stage.Body);
    4.             var pos = bodyComponent switch
    5.             {
    6.                 CinemachineTransposer transposer => transposer.GetTargetCameraPosition(cam.State.ReferenceUp),
    7.                 CinemachineHardLockToTarget lockToTarget => lockToTarget.FollowTargetPosition,
    8.                 _ => throw new ArgumentOutOfRangeException(nameof(bodyComponent), bodyComponent != null ? bodyComponent.GetType() : null, null)
    9.             };
    10.             var aimComponent = cam.GetCinemachineComponent(CinemachineCore.Stage.Aim);
    11.             var lookAt = aimComponent switch
    12.             {
    13.                 CinemachineComposer composer => (composer.LookAtTarget)
    14.                     ? composer.LookAtTargetPosition + composer.LookAtTargetRotation * composer.m_TrackedObjectOffset
    15.                     : cam.State.ReferenceLookAt,
    16.                 CinemachineHardLookAt hardLookAt => cam.State.ReferenceLookAt,
    17.                 _ => throw new ArgumentOutOfRangeException(nameof(aimComponent), aimComponent != null ? aimComponent.GetType() : null, null)
    18.             };
    19.             var rotation = Quaternion.LookRotation((lookAt - pos).normalized, cam.State.ReferenceUp);
    20.             var newState = cam.State;
    21.             newState.RawOrientation = rotation;
    22.             newState.RawPosition = pos;
    23.             newState.ReferenceLookAt = lookAt;
    24.             return newState;
    25.         }