Cannot Update Transform Position of Camera with Cinemachine Script

Hello,

I am having an issue with being unable to modify the camera’s transform when I attach a Cinemachine Brain script to it.

In image 1 (brain_unattached), I am able to modify the transform to coordinates 0, 0 and center my object of focus (the blue square). When I attach the brain in image 2 (brain_attached), the camera snaps to position -17, 9 and the blue square is now on the far right.

When I adjust the transform position in a script it also has no effect. Any help on this issue would be much appreciated thanks!


Hi @scott847 and welcome to the forum!

What you’re describing is expected behavior and pretty fundamental to what Cinemachine is and does. Once you’ve handed control to a Cinemachine Virtual Camera, you can’t control the Camera directly, nor should you want to. The whole idea of Cinemachine is that the virtual camera (or cameras) control the Camera relative to the settings you input.

2 Likes

@scott847 If you want to control the Camera’s position directly by script after it has a CM Brain, create a passive Virtual Camera (i.e. a CinemachineVirtualCamera with Do Nothing in both Aim and Body), make it high priority to make it drive the main camera, then have your script control its position, rotation, and FOV.

1 Like

Hello, sorry for resurrecting an old thread.
I’m looking for a way to modify a follow player virtual camera. Specifically, what I’m trying to do is when a player enter a trigger, I want to spin the character AND the virtual camera around 180 degrees so they will be heading back to where they came from. What I manage to do was only turn the character around while the camera stays in position, so that the player is facing back to the camera.

I found out that it’s impossible to change the virtual camera’s position around via script. I tried having a second virtual camera with Do Nothing (Body and Aim), switch over to that camera (via Priority) when I turn the character around, and adjust the transform.position of the follow camera, but when I switch back to the follow camera, it doesn’t move from the original position.

Do I miss anyting? How do you recommend I can do this, and do it efficiently? Thank you in advance!

Here are the things that I’ve tried so far:

  • Ticking the ‘inherit’ checkbox
  • Using ForceCameraPosition method
  • Disable virtual camera component > set position > enable virtual camera component
  • Using another virtual camera with Do Nothing (Body & Aim) as temporary camera> disable the follow cam > set position of the follow cam > enable follow cam
  • Same as above but instead of disabling - enabling, made the Do Nothing cam has higher - lower priority than the follow cam.

Can you show the inspector for you vcam? Open all the foldouts.
I want to see the Binding Mode in particular.

1 Like

Hello Gregoryl, thanks for your response. Here’s the inspector view of the vCam I’m trying to rotate with the character.

Hi @Gregoryl , any update? I still have no success rotating the cam with the character.

I tried using Transposer as the body component of Cinemachine vcam, still no success. Attached is the screencap of my settings. I also tried changing the binding to Simple Follow Wi th World Up when moving the position and rotation of the vcam, and then swap it back to Lock To Target after moving the vCam, but it still snaps back to it’s previous position.

Any clue to what to try next would be greatly appreciated. Thanks in advance!

9856077--1419369--CamRotateProblem3.png

Update: I tried removing m_follow > modify transform of the vcam > set m_follow to the CameraRoot target, still no luck, the vCam moved for a bit, but snaps back to original position once the m_follow is set.

Tried PreviousStateIsValid = false too, vCam still snaps back to the original position.

I’ve been struggling with this issue for two weeks now, so any feedback is appreciated.

Sorry for the delay. Somehow this thread fell off my radar.

The way to think about follow cams is that they assume their position as a function of the target’s position and rotation. So, with a Transposer component in Body having LockToTarget binding mode, it should be enough to reposition and rotate the target, and the camera will automatically follow. No need to move it explicitly. The same is true for 3rdPersonFollow component in Body. You might want to set its PreviousStateIsValid=false to cancel the damping, but that’s the most you should have to do.

The question is: why is that not happening in your case? Are you properly repositioning and rotating the target (PlayerCameraRoot), or are you doing it to some other object?

Can you show the hierarchy? I would like to see the player, the PlayerCameraRoot, the vcam, and the main camera. I would like to see their parent/child relationships.

Thank you for the reply @Gregoryl , much appreciated. I’m attaching a drawing of what I’m trying to do.
9860574--1420509--sketch.jpg

  • (image 1) Theres a pathway (Brown), in the middle of which there’s a collider (Blue), the player (Red) will be walking toward the collider. The Player Follow camera (Pink) follows the player from behind.
  • (image 2) Once the player touches the collider, I want the player to rotate 180 degrees (this value will be taken from the collider’s rotation, i.e. if the collider is rotated 90 degrees, when the player touches it, the player will be rotated 90 degrees too). I want to rotate both the player and the Player Follow Cam, so that the Player Follow Cam ’ s position relative to the player is the same (behind the player).

So far what I’ve managed to do is rotate the player armature, and whatever I do, the Player Follow camera stays in its previous position (so every time the player armature touches the collider, the player armature is rotated 180 degrees, and since the Player Follow camera doesn’t rotate with the player armature, the player armature is now facing the camera).

What I’m aiming to do is such that the Player Follow camera gets rotated with the player armature and keeps its relative position to the player armature (behind the player).

I hope I explained that clearly enough but if you need more information, please let me know.

I’m attaching the player-camera hierarchy, camera inspector settings, collider inspector settings, and a diagram of what I’m trying to achieve.

Here’s the code on the Collider:

private void OnTriggerEnter(Collider other)
{
    Debug.Log("TRIGGERED!!");
    if(other.gameObject.layer == 3 && !isCoolingDown)
    {
        Debug.Log("WARP TRIGGERED!! " + other.gameObject.name);
        isTriggered = true;

        vCam = GameObject.Find("PlayerFollow Camera").gameObject;
        vCam2 = GameObject.Find("PlayerFollow Warp Camera").gameObject;
        vCamComp = vCam.GetComponent<CinemachineVirtualCamera>();
        vCam2Comp = vCam2.GetComponent<CinemachineVirtualCamera>();

        CinemachineTransposer vCamTranspose = vCamComp.GetCinemachineComponent<CinemachineTransposer>();

        camFollow = vCamComp.Follow;
        camLookAt = vCamComp.LookAt;

        mainCam = GameObject.Find("MainCamera").gameObject;

        // GET TRANSFORM OF THE PLAYER GAMEOBJECT -------------------------------------------------------
        Vector3 deltaTransform = transform.position - other.gameObject.transform.position;
        Vector3 originalPos = other.transform.position;

        // GET TRANSFORM OF THE PLAYER FOLLOW CAMERA -------------------------------------------------------
        Vector3 vCamPos = vCam.transform.localPosition;
        Quaternion vCamRot = vCam.transform.localRotation;

        // HANDOVER TO PLAYERFOLLOW WARP CAMERA -----------------------------------------------------------------------------
        Debug.Log("Handover to warp camera");
        vCam2Comp.Priority = vCamComp.Priority + 10;
        /*
         * Player Follow Warp Camera is a Do Nothing Vcam that is the child of the Player Armature object, basically a vcam
         * that just follow the player and inherit its transform
         */
    
        // DEACTIVATE PLAYER CONTROLLER COMPONENTS ----------------------------------------------------------------
        other.GetComponent<CharacterController>().enabled = false;
        other.GetComponent<ThirdPersonController>().enabled = false;
        other.GetComponent<PlayerInput>().enabled = false;

        //SET BINDING MODE TO SIMPLE FOLLOW, DEACTIVATE, AND FORCE CAMERA POSITION
        vCamTranspose.m_BindingMode = CinemachineTransposer.BindingMode.SimpleFollowWithWorldUp;
        vCamComp.m_LookAt = null;
        vCam.SetActive(false);
        vCamComp.ForceCameraPosition(Vector2.zero, Quaternion.identity);
    
        // MODIFY TRANSFORM OF THE PLAYER GAMEOBJECT -------------------------------------------------------
        other.transform.position = triggerTarget.position - deltaTransform;
        other.transform.rotation = other.transform.rotation * transform.rotation;

        Vector3 currentPos = other.transform.position;
        //Vector3 deltaPlayerPos = originalPos - currentPos;

        //START COROUTINE TO HANDOVER BACK TO PLAYER FOLLOW CAMERA
        StartCoroutine(HandoverBackCam(vCam, vCam2));

   
    }
}


private IEnumerator HandoverBackCam(GameObject vCam, GameObject vCam2)
{
    vCamComp.PreviousStateIsValid = false;
    CinemachineVirtualCamera followCamComp = vCam.GetComponent<CinemachineVirtualCamera>();
    CinemachineVirtualCamera warpCamComp = vCam2.GetComponent<CinemachineVirtualCamera>();
    CinemachineTransposer vCamTranspose = followCamComp.GetCinemachineComponent<CinemachineTransposer>();
 
    // REACTIVATE PLAYER FOLLOW CAMERA -----------------------------------------------------------------------

    while(vCam.transform.position != vCam2.transform.position)
    {
        yield return new WaitForEndOfFrame();
        followCamComp.ForceCameraPosition(vCam2.transform.position, vCam2.transform.rotation);
        vCamComp.PreviousStateIsValid = false;
    }
 
    vCam.SetActive(true);
    followCamComp.m_LookAt = playerArmature.gameObject.transform.Find("PlayerCameraRoot").transform;
    warpCamComp.Priority = followCamComp.Priority - 10;
    vCamTranspose.m_BindingMode = CinemachineTransposer.BindingMode.LockToTargetNoRoll;
    followCamComp.ForceCameraPosition(vCam2.transform.position, vCam2.transform.rotation);


    // ACTIVATE PLAYER CONTROLLER COMPONENTS ----------------------------------------------------------------
    playerArmature.GetComponent<CharacterController>().enabled = true;
    playerArmature.GetComponent<ThirdPersonController>().enabled = true;
    playerArmature.GetComponent<PlayerInput>().enabled = true;
    StopCoroutine(HandoverBackCam(vCam, vCam2));
    yield break;
}

Thank you very much for your assistance @Gregoryl .

9860574--1420500--Camera Inspector.png
9860574--1420503--Collider Inspector.png
9860574--1420506--Hierarchy.png

What is this PlayerCameraRoot object? I don’t see it in your hierarchy. Are there any custom scripts on it, or any scripts anywhere that are controlling its rotation?

Try this experiment, just as a test (not suggesting it as a permanent solution, I just want to see what happens):

  • Set the target of the follow vcam to be “Player Character”
  • In your OnTriggerEnter code, reposition and rotate the player character. Do nothing else.
  • What happens?

Hello @Gregoryl , thanks for the response!

  • Actually I’m using Unity’s third person starter asset as a starting point. The PlayerCameraRoot object is an empty GameObject that came with the starter asset, it’s a child of Player Armature GameObject set about the height of the shoulders of the player character ( I think it’s so that the camera doesn’t aim towards the feet of the character).
  • I tried this, still had the same effect. You can see a screen capture of this here:
    https://www.youtube.com/watch?v=LYIflUhCic4
    (The Player Follow Camera is selected, showing the XYZ axes).
  • As above.

Here’s the current code (used to the effect of test captured at the youtube link above).

public class WarpTriggerScriptTest2 : MonoBehaviour
{

    public GameObject playerArmature;
    public GameObject warpTarget;

    private void OnTriggerExit(Collider other)
    {
        if(other.gameObject == playerArmature)
        {
            Vector3 deltaPos = transform.position-other.transform.position;

            // ROTATE AND REPOSITION PLAYER ARMATURE
            other.transform.position = warpTarget.transform.position - deltaPos;
            other.transform.rotation = other.transform.rotation * transform.rotation;
          
        }
    }
}

Something is weird with your setup. I don’t know what object is entering your trigger, and I don’t know what object your camera is tracking. I asked but your answers were absent or vague. You didn’t say whether you did as I asked for (1), and you didn’t answer my question about scripts acting on PlayerCameraRoot.

I tried making a simple test, it works as expected. You can take a look at it. What is different about your setup?

9864300–1421310–FlipDirectionTest.unitypackage (5.57 KB)

Hi @Gregoryl , thanks for your response, and sorry if my explanation / answer was vague, I tried to be as clear as possible, I’ll try to explain better this time, it’s gonna be a bit long.

Okay… so my setup was based on Unity’s Third Person Starter Asset ( https://assetstore.unity.com/packages/essentials/starter-assets-thirdperson-updates-in-new-charactercontroller-pa-196526 ), I started with the NestedParentArmature_Unpack prefab, perhaps it would be clearer if you see the setup in that asset?

Here’s the hierarchy of NestedParentArmature_Unpack :
9865026--1421499--NestedParentArmature hierarchy.png

The reason I set the follow of the PlayerFollowCamera to the PlayerArmature and not the Player Character is because the Player Character is an empty GameObject container that doesn’t move, it’s just an empty parent for the PlayerArmature GameObject that does move around. I tried setting the follow to NestedParentArmature_Unpack –which is equivalent to the Player Character GameObject in my project as I only changed the name –, but the camera stayed in place while the character moves. Here’s a video of that:

https://www.youtube.com/watch?v=0PwOEKHl9RU

The PlayerCameraRoot is from the NestedParentArmature_Unpack prefab from the Third Person Starter Asset, it’s empty, it contains no script, it’s just there for the transform so that the PlayerFollowCamera vcam is aimed at the character’s shoulders instead of feet.

9865026--1421502--PlayerCameraRoot Inspector.png


Now here’s what I did since last time:

I made a new empty Unity project and I tried your package, and it ran perfectly, thank you.

After that, I tried with NestedParentArmature_Unpack prefab from Unity’s Third Person Starter Asset, and changed the follow to PlayerArmature , here’s the result:

https://www.youtube.com/watch?v=XBSZx0olmwE

As you can see in the video, the PlayerFollowCamera did move to behind the character whenever the character is flipped (which is what we want), but the camera now aimed to the feet of the character, and it lost its ability to orbit around the character with mouse movement. Thus the player can’t control the character anymore (which we don’t want).

So I set the follow back to PlayerCameraRoot gameobject, and the camera gained back the ability to orbit around the character, however, the behavior is exactly like the one in my setup yesterday, the camera stayed and didn’t move to the the back of the character.

https://www.youtube.com/watch?v=rtxdw4wN8o0

I looked into the setup of the PlayerArmature, and there’s one script/component that I think is causing this behavior: it’s the Third Person Controller Script (which also came with Unity’s Third Person Starter Asset ). This script requires Cinemachine Camera Target as an input and it’s set to PlayerCameraRoot by default.

9865026--1421508--ThirdPersonController inspector.png

Here’s what’s inside the script:

 using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif

/* Note: animations are called via the controller for both the character and capsule using animator null checks
*/

namespace StarterAssets
{
    [RequireComponent(typeof(CharacterController))]
#if ENABLE_INPUT_SYSTEM
    [RequireComponent(typeof(PlayerInput))]
#endif
    public class ThirdPersonController : MonoBehaviour
    {
        [Header("Player")]
        [Tooltip("Move speed of the character in m/s")]
        public float MoveSpeed = 2.0f;

        [Tooltip("Sprint speed of the character in m/s")]
        public float SprintSpeed = 5.335f;

        [Tooltip("How fast the character turns to face movement direction")]
        [Range(0.0f, 0.3f)]
        public float RotationSmoothTime = 0.12f;

        [Tooltip("Acceleration and deceleration")]
        public float SpeedChangeRate = 10.0f;

        public AudioClip LandingAudioClip;
        public AudioClip[] FootstepAudioClips;
        [Range(0, 1)] public float FootstepAudioVolume = 0.5f;

        [Space(10)]
        [Tooltip("The height the player can jump")]
        public float JumpHeight = 1.2f;

        [Tooltip("The character uses its own gravity value. The engine default is -9.81f")]
        public float Gravity = -15.0f;

        [Space(10)]
        [Tooltip("Time required to pass before being able to jump again. Set to 0f to instantly jump again")]
        public float JumpTimeout = 0.50f;

        [Tooltip("Time required to pass before entering the fall state. Useful for walking down stairs")]
        public float FallTimeout = 0.15f;

        [Header("Player Grounded")]
        [Tooltip("If the character is grounded or not. Not part of the CharacterController built in grounded check")]
        public bool Grounded = true;

        [Tooltip("Useful for rough ground")]
        public float GroundedOffset = -0.14f;

        [Tooltip("The radius of the grounded check. Should match the radius of the CharacterController")]
        public float GroundedRadius = 0.28f;

        [Tooltip("What layers the character uses as ground")]
        public LayerMask GroundLayers;

        [Header("Cinemachine")]
        [Tooltip("The follow target set in the Cinemachine Virtual Camera that the camera will follow")]
        public GameObject CinemachineCameraTarget;

        [Tooltip("How far in degrees can you move the camera up")]
        public float TopClamp = 70.0f;

        [Tooltip("How far in degrees can you move the camera down")]
        public float BottomClamp = -30.0f;

        [Tooltip("Additional degress to override the camera. Useful for fine tuning camera position when locked")]
        public float CameraAngleOverride = 0.0f;

        [Tooltip("For locking the camera position on all axis")]
        public bool LockCameraPosition = false;

        // cinemachine
        private float _cinemachineTargetYaw;
        private float _cinemachineTargetPitch;

        // player
        private float _speed;
        private float _animationBlend;
        private float _targetRotation = 0.0f;
        private float _rotationVelocity;
        private float _verticalVelocity;
        private float _terminalVelocity = 53.0f;

        // timeout deltatime
        private float _jumpTimeoutDelta;
        private float _fallTimeoutDelta;

        // animation IDs
        private int _animIDSpeed;
        private int _animIDGrounded;
        private int _animIDJump;
        private int _animIDFreeFall;
        private int _animIDMotionSpeed;

#if ENABLE_INPUT_SYSTEM
        private PlayerInput _playerInput;
#endif
        private Animator _animator;
        private CharacterController _controller;
        private StarterAssetsInputs _input;
        private GameObject _mainCamera;

        private const float _threshold = 0.01f;

        private bool _hasAnimator;

        private bool IsCurrentDeviceMouse
        {
            get
            {
#if ENABLE_INPUT_SYSTEM
                return _playerInput.currentControlScheme == "KeyboardMouse";
#else
                return false;
#endif
            }
        }


        private void Awake()
        {
            // get a reference to our main camera
            if (_mainCamera == null)
            {
                _mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
            }
        }

        private void Start()
        {
            _cinemachineTargetYaw = CinemachineCameraTarget.transform.rotation.eulerAngles.y;
  
            _hasAnimator = TryGetComponent(out _animator);
            _controller = GetComponent<CharacterController>();
            _input = GetComponent<StarterAssetsInputs>();
#if ENABLE_INPUT_SYSTEM
            _playerInput = GetComponent<PlayerInput>();
#else
            Debug.LogError( "Starter Assets package is missing dependencies. Please use Tools/Starter Assets/Reinstall Dependencies to fix it");
#endif

            AssignAnimationIDs();

            // reset our timeouts on start
            _jumpTimeoutDelta = JumpTimeout;
            _fallTimeoutDelta = FallTimeout;
        }

        private void Update()
        {
            _hasAnimator = TryGetComponent(out _animator);

            JumpAndGravity();
            GroundedCheck();
            Move();
        }

        private void LateUpdate()
        {
            CameraRotation();
        }

        private void AssignAnimationIDs()
        {
            _animIDSpeed = Animator.StringToHash("Speed");
            _animIDGrounded = Animator.StringToHash("Grounded");
            _animIDJump = Animator.StringToHash("Jump");
            _animIDFreeFall = Animator.StringToHash("FreeFall");
            _animIDMotionSpeed = Animator.StringToHash("MotionSpeed");
        }

        private void GroundedCheck()
        {
            // set sphere position, with offset
            Vector3 spherePosition = new Vector3(transform.position.x, transform.position.y - GroundedOffset,
                transform.position.z);
            Grounded = Physics.CheckSphere(spherePosition, GroundedRadius, GroundLayers,
                QueryTriggerInteraction.Ignore);

            // update animator if using character
            if (_hasAnimator)
            {
                _animator.SetBool(_animIDGrounded, Grounded);
            }
        }

        private void CameraRotation()
        {
            // if there is an input and camera position is not fixed
            if (_input.look.sqrMagnitude >= _threshold && !LockCameraPosition)
            {
                //Don't multiply mouse input by Time.deltaTime;
                float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;

                _cinemachineTargetYaw += _input.look.x * deltaTimeMultiplier;
                _cinemachineTargetPitch += _input.look.y * deltaTimeMultiplier;
            }

            // clamp our rotations so our values are limited 360 degrees
            _cinemachineTargetYaw = ClampAngle(_cinemachineTargetYaw, float.MinValue, float.MaxValue);
            _cinemachineTargetPitch = ClampAngle(_cinemachineTargetPitch, BottomClamp, TopClamp);

            // Cinemachine will follow this target
            CinemachineCameraTarget.transform.rotation = Quaternion.Euler(_cinemachineTargetPitch + CameraAngleOverride,
                _cinemachineTargetYaw, 0.0f);
        }

        private void Move()
        {
            // set target speed based on move speed, sprint speed and if sprint is pressed
            float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;

            // a simplistic acceleration and deceleration designed to be easy to remove, replace, or iterate upon

            // note: Vector2's == operator uses approximation so is not floating point error prone, and is cheaper than magnitude
            // if there is no input, set the target speed to 0
            if (_input.move == Vector2.zero) targetSpeed = 0.0f;

            // a reference to the players current horizontal velocity
            float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;

            float speedOffset = 0.1f;
            float inputMagnitude = _input.analogMovement ? _input.move.magnitude : 1f;

            // accelerate or decelerate to target speed
            if (currentHorizontalSpeed < targetSpeed - speedOffset ||
                currentHorizontalSpeed > targetSpeed + speedOffset)
            {
                // creates curved result rather than a linear one giving a more organic speed change
                // note T in Lerp is clamped, so we don't need to clamp our speed
                _speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude,
                    Time.deltaTime * SpeedChangeRate);

                // round speed to 3 decimal places
                _speed = Mathf.Round(_speed * 1000f) / 1000f;
            }
            else
            {
                _speed = targetSpeed;
            }

            _animationBlend = Mathf.Lerp(_animationBlend, targetSpeed, Time.deltaTime * SpeedChangeRate);
            if (_animationBlend < 0.01f) _animationBlend = 0f;

            // normalise input direction
            Vector3 inputDirection = new Vector3(_input.move.x, 0.0f, _input.move.y).normalized;

            // note: Vector2's != operator uses approximation so is not floating point error prone, and is cheaper than magnitude
            // if there is a move input rotate player when the player is moving
            if (_input.move != Vector2.zero)
            {
                _targetRotation = Mathf.Atan2(inputDirection.x, inputDirection.z) * Mathf.Rad2Deg +
                                  _mainCamera.transform.eulerAngles.y;
                float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, _targetRotation, ref _rotationVelocity,
                    RotationSmoothTime);

                // rotate to face input direction relative to camera position
                transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
            }


            Vector3 targetDirection = Quaternion.Euler(0.0f, _targetRotation, 0.0f) * Vector3.forward;

            // move the player
            _controller.Move(targetDirection.normalized * (_speed * Time.deltaTime) +
                             new Vector3(0.0f, _verticalVelocity, 0.0f) * Time.deltaTime);

            // update animator if using character
            if (_hasAnimator)
            {
                _animator.SetFloat(_animIDSpeed, _animationBlend);
                _animator.SetFloat(_animIDMotionSpeed, inputMagnitude);
            }
        }

        private void JumpAndGravity()
        {
            if (Grounded)
            {
                // reset the fall timeout timer
                _fallTimeoutDelta = FallTimeout;

                // update animator if using character
                if (_hasAnimator)
                {
                    _animator.SetBool(_animIDJump, false);
                    _animator.SetBool(_animIDFreeFall, false);
                }

                // stop our velocity dropping infinitely when grounded
                if (_verticalVelocity < 0.0f)
                {
                    _verticalVelocity = -2f;
                }

                // Jump
                if (_input.jump && _jumpTimeoutDelta <= 0.0f)
                {
                    // the square root of H * -2 * G = how much velocity needed to reach desired height
                    _verticalVelocity = Mathf.Sqrt(JumpHeight * -2f * Gravity);

                    // update animator if using character
                    if (_hasAnimator)
                    {
                        _animator.SetBool(_animIDJump, true);
                    }
                }

                // jump timeout
                if (_jumpTimeoutDelta >= 0.0f)
                {
                    _jumpTimeoutDelta -= Time.deltaTime;
                }
            }
            else
            {
                // reset the jump timeout timer
                _jumpTimeoutDelta = JumpTimeout;

                // fall timeout
                if (_fallTimeoutDelta >= 0.0f)
                {
                    _fallTimeoutDelta -= Time.deltaTime;
                }
                else
                {
                    // update animator if using character
                    if (_hasAnimator)
                    {
                        _animator.SetBool(_animIDFreeFall, true);
                    }
                }

                // if we are not grounded, do not jump
                _input.jump = false;
            }

            // apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
            if (_verticalVelocity < _terminalVelocity)
            {
                _verticalVelocity += Gravity * Time.deltaTime;
            }
        }

        private static float ClampAngle(float lfAngle, float lfMin, float lfMax)
        {
            if (lfAngle < -360f) lfAngle += 360f;
            if (lfAngle > 360f) lfAngle -= 360f;
            return Mathf.Clamp(lfAngle, lfMin, lfMax);
        }

        private void OnDrawGizmosSelected()
        {
            Color transparentGreen = new Color(0.0f, 1.0f, 0.0f, 0.35f);
            Color transparentRed = new Color(1.0f, 0.0f, 0.0f, 0.35f);

            if (Grounded) Gizmos.color = transparentGreen;
            else Gizmos.color = transparentRed;

            // when selected, draw a gizmo in the position of, and matching radius of, the grounded collider
            Gizmos.DrawSphere(
                new Vector3(transform.position.x, transform.position.y - GroundedOffset, transform.position.z),
                GroundedRadius);
        }

        private void OnFootstep(AnimationEvent animationEvent)
        {
            if (animationEvent.animatorClipInfo.weight > 0.5f)
            {
                if (FootstepAudioClips.Length > 0)
                {
                    var index = Random.Range(0, FootstepAudioClips.Length);
                    AudioSource.PlayClipAtPoint(FootstepAudioClips[index], transform.TransformPoint(_controller.center), FootstepAudioVolume);
                }
            }
        }

        private void OnLand(AnimationEvent animationEvent)
        {
            if (animationEvent.animatorClipInfo.weight > 0.5f)
            {
                AudioSource.PlayClipAtPoint(LandingAudioClip, transform.TransformPoint(_controller.center), FootstepAudioVolume);
            }
        }
    }
}

Any recommendation what I should try to do next?

I hope this is clear enough, but if not, please let me know what else you need so I can send it here. Once again, thank you for your assistance.

UPDATE: I think we got it, thank you for your assistance @Gregoryl :), it turns out the Third Person Controller script is the culprit. I modified your code a little bit, and it works flawlessly. Here’s the video:

For anyone who’s having similar problem and is using Unity’s Third Person Starter Asset , you need to change the value of _cinemachineTargetYaw in the Third Person Controller component, so here’s our solution:

  • Modify these variable declarations in ThirdPersonController.cs from private to public:
// cinemachine
public float _cinemachineTargetYaw;
public float _cinemachineTargetPitch;
  • Attach this code to the collider that is going to flip your character, and drag and drop your PlayerArmature and PlayerFollowCamera to appropriate fields in the inspector:
    public ThirdPersonController thirdPersonC;
    public CinemachineVirtualCamera pFollowCam;
    private void OnTriggerEnter(Collider other)
    {
        // Turn player around 180 degrees when entering the trigger
        other.transform.rotation = Quaternion.AngleAxis(180, Vector3.up) * other.transform.rotation;

        // Turn the camera around by modifying _cinemachineTargetYaw in Third Person Controller Script
        thirdPersonC.enabled = false;
        thirdPersonC._cinemachineTargetYaw = thirdPersonC._cinemachineTargetYaw + 180f;
        pFollowCam.PreviousStateIsValid = false;
        thirdPersonC.enabled = true;

    }
1 Like

Glad you solved it. I confess to not being a fan of those starter assets.

Consider getting rid of the StarterAssets controller and using something else.

Here is a test that uses a simple character controller (the one that comes with the CM3 samples). In my view it has better separation between CM cameras and the controller. It’s much more forgiving and versatile and allows you to reposition and rotate the player without any fuss.

9866109–1421745–FlipTestWithSimpleController.unitypackage (182 KB)

1 Like

Whoa, thank you @Gregoryl , I’ll try it out and learn from it.

This was actually the second time I got a headache from the Third Person Starter Asset, the first one was with Timeline, it doesn’t play well with it either, I had to practically remove the components from the character to be able to animate it in timeline properly.

Btw will you guys be at Gamescom this year? We’re planning to go an set up a booth there. Maybe we can meet up or something.