I am not very familiar with the built-in CharacterController, but I can tell you how I have implemented this for my own character controller (although, I should warn you it is almost definitely more convoluted than necessary). I made this class using UnityEngine; public class CopyMovement : MonoBehaviour { [SerializeField] public GameObject target; Transform target_transform; Transform common_ancestor; Quaternion previousAncestorRotation; Vector3 previousAncestorPosition; Matrix4x4 previousTargetWorldToLocal; Quaternion previousTargetRotation; void Start(){ if(target!=null) Initialize(); } public void Initialize(){ target_transform = target.transform; common_ancestor = Utils.FindCommonAncestor(transform, target_transform); if(common_ancestor!=null){ previousAncestorPosition= common_ancestor.position; previousAncestorRotation = common_ancestor.rotation; } previousTargetWorldToLocal = target_transform.worldToLocalMatrix; previousTargetRotation = target_transform.rotation; } void LateUpdate() { //if the objects do not share ancestors, it's fairly straightforward: calculate the position and rotation of the constrained object in the local space of the previous parent pose, and //move it to the same pose in the _current_ local space //if they are part of the same hierarchy, however, the transformations of the common ancestors get applied twice to the constrained object (once naturally, once because of this script) //so, we just apply the inverse of the movement of the common ancestors to correct it. Simple, right? if(target!=null){ Vector3 pointInPreviousLocalSpace = previousTargetWorldToLocal.MultiplyPoint(transform.position); Quaternion rotationInPreviousLocalSpace = Quaternion.Inverse(previousTargetRotation) * transform.rotation; //set up the whole common-ancestor thing Quaternion offsetAncestorRotation = Quaternion.identity; Matrix4x4 ancestorMatrix = Matrix4x4.identity; if(common_ancestor!=null){ offsetAncestorRotation = Quaternion.Inverse(previousAncestorRotation) * common_ancestor.rotation; ancestorMatrix = Matrix4x4.TRS( common_ancestor.worldToLocalMatrix.MultiplyVector(previousAncestorPosition-common_ancestor.position), Quaternion.Inverse(offsetAncestorRotation), Vector3.one ); } //the thing happens! Matrix4x4 theMatrix; if(common_ancestor!=null) theMatrix = common_ancestor.localToWorldMatrix* ancestorMatrix * common_ancestor.worldToLocalMatrix * target_transform.localToWorldMatrix; else theMatrix = target_transform.localToWorldMatrix; transform.SetPositionAndRotation( theMatrix.MultiplyPoint(pointInPreviousLocalSpace), Quaternion.Inverse(offsetAncestorRotation) * target_transform.rotation * rotationInPreviousLocalSpace); //store the current local space information for next time previousTargetWorldToLocal = target_transform.worldToLocalMatrix; previousTargetRotation = target_transform.rotation; if(common_ancestor!=null){ previousAncestorRotation = common_ancestor.rotation; previousAncestorPosition = common_ancestor.position; } } } } And added a component of this type to the character. Then, inside of the character controller, when checking the ground that it is standing on, I change the settings of the CopyMovement. The abridged version of that code is something like this: CopyMovement groundParentConstraint; bool checkGround(){ if(castRayDownwards()){ if(groundParentConstraint.target != hit.collider.gameObject){ groundParentConstraint.target = hit.collider.gameObject; groundParentConstraint.Initialize(); } return true; } else{ groundParentConstraint.target = null; return false; } } On top of this being a bit convoluted, it also does not work when the "parent" (the platform, in this case) has root motion animation, but do not ask me why, because I have no clue. But still, it's better than nothing.