Character controller won't rotate - Is it bad quaternion math or something else?

I’m trying to take an extant Character Controller asset and make it compatible with Fusion Networking - All I’ve done to make this work is modify the character controller script to accommodate fusion’s network character controller rather than the default Unity character controller. Just passing inputs and adjusting little else, and the position and animation of the character now works with networking. But not rotation.

For whatever reason, the character controller will no longer turn in place despite the script logic being the same. Except, strangely, the script’s logic for translating and rotating the character on moving platforms - Which utilizes quaternion math to determine the rotation and position to negate - works perfectly. If I manually set the player character to have an active platform ‘beneath them’ the quaternion math translates to the network character creator. But if they’re not on a platform the logic doesn’t pass, even though in both cases the “_moveDirection” vector is passed to the same character controller.

I’ve narrowed the problem down to this block of code; If the player’s on a platform and not grounded, it accounts for the position of the platform. If they’re not, it passes _moveDirection to the .move function of the character of the network character controller and suddenly that doesn’t mean anything.

        //    //platforms
        if (!CurrentActivePlatform || !CurrentActivePlatform.CompareTag("Platform")) return;
        if (CurrentActivePlatform)
            var newGlobalPlatformPoint = CurrentActivePlatform.TransformPoint(_activeLocalPlatformPoint);
            _moveDirection = newGlobalPlatformPoint - _activeGlobalPlatformPoint;
            if (_moveDirection.magnitude > 0.01f)

            if (!CurrentActivePlatform) return;

            //         // Support moving platform rotation
            var newGlobalPlatformRotation = CurrentActivePlatform.rotation * _activeLocalPlatformRotation;
            var rotationDiff = newGlobalPlatformRotation * Quaternion.Inverse(_activeGlobalPlatformRotation);
            // Prevent rotation of the local up vector

            rotationDiff = Quaternion.FromToRotation(rotationDiff * Vector3.up, Vector3.up) * rotationDiff;
           _characterTransform.rotation = rotationDiff * _characterTransform.rotation;
            _characterTransform.eulerAngles = new Vector3(0, _characterTransform.eulerAngles.y, 0);

            if (!(_moveDirection.magnitude > 0.01f)) return;
            _moveDirection = Vector3.Lerp(_moveDirection,, Time.deltaTime);

        if (CanControl)
            //        //this activate or deactivate jet pack Object and effect.
            JetPackObject.SetActive(Jetpack && _flyJetPack && JetPackFuel > 0 && !HoldingObject);

            if (_activeFall)
                _slowFall = true;
                _activeFall = false;

            if (HaveSlowFall && !_isGrounded && _slowFall)
                _slowFall = false;
// camera = forward
        _forward = _cameraTransform.TransformDirection(Vector3.forward);
         _forward.y = 0f;
          _forward = _forward.normalized;
         _right = new Vector3(_forward.z, 0.0f, -_forward.x);

        _move = (_horizontal * _right + _vertical * _forward);
        _direction = (_horizontal * _right + _vertical * _forward);

    //    _move = (inputData.ninjaDirection.x * _right + inputData.ninjaDirection.y * _forward);
     //   _controller.transform.forward = _move;

        //    // //if no is correct grounded then slide.
        if (!_isCorrectGrounded && _isGrounded)
           _move.x += (1f - _hitNormal.y) * _hitNormal.x * (1f - SlideFriction);
           _move.z += (1f - _hitNormal.y) * _hitNormal.z * (1f - SlideFriction);

        //    // //move the player if no is active the slow fall(this avoid change the speed for the fall)
      if (!_slowFall && _controller.enabled)
            _controller.Move(Time.deltaTime * RunningSpeed * _move);

        //    // //Check if is correct grounded.
        _isCorrectGrounded = (Vector3.Angle(Vector3.up, _hitNormal) <= SlopeLimit);

I’ve pored over this code and it makes sense. I must be missing something - Is it just an issue of return; being called in the platform logic before the translation is passed…? should I be changing this vector math to account for quaternions instead…? I dread this being the case as I barely understand quaternions. I’m sort of new to C# so I could be missing something more fundamental.

This isn’t a very specific question and I apologize for that, but I’m at the end of my rope. I don’t understand what’s not working here, when it works perfectly with a default character controller. Any help would be greatly appreciated.

Here is a video of the issue in action, perhaps that will help. Thanks for reading, either way! Wish me luck!


now I don’t know what any of this stuff is but I think rotating the transform point based on the platforms rotation should rotate it with the platform but you can also try momentarily parenting the object to the platform. hope this helps :slight_smile: