Search Unity

Character Controller ignores transform.position

Discussion in 'Scripting' started by EnriqueGato, Jan 22, 2019.

  1. EnriqueGato

    EnriqueGato

    Joined:
    Sep 3, 2016
    Posts:
    81
    I had to re-import my project in a new, clean one and, after doing this, I'm having a weird behaviour from Character Controller component.

    If I do this:
    Code (CSharp):
    1. transform.position = newPosition;  // place the character in Checkpoint's position
    2. characterController.Move(velocity);  // move the character
    The character controller ignores the new position and moves the character from the previous one.

    Anyone knows why this happened? It worked right before re-importing the project.
     
    PotatoFire1000 likes this.
  2. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336
    The character controller has it's own internal definition of what position it has, and will set the transform's position to that every frame, so you can't move it with transform.position.

    For some reason, there's no .Warp method (like there's on NavMeshAgents that have the same issue). I seem to remember that you can do this, though:

    Code (csharp):
    1.  //probably extract this into a helper method.
    2. characterController.enabled = false;
    3. characterController.transform.position = pos;
    4. characterController.enabled = true;
    As I (believe) that the character controller checks it's starting position in OnEnable.
     
  3. MonkeyZero

    MonkeyZero

    Joined:
    Feb 20, 2013
    Posts:
    37
    This issue gets even worse. Even with the fix @Baste suggest, This really only fixes the position. Rotation, on the other hand, is still ignored completely even when wrapping it in the enabled switches.

    How do we fix.
    Code (CSharp):
    1. characterController.transform.rotation = rot;
     
    PotatoFire1000 likes this.
  4. MonkeyZero

    MonkeyZero

    Joined:
    Feb 20, 2013
    Posts:
    37
    I realized I got a bit harsh and was reposting to change the tone and did it again. One sec.

    Okay first, Yeah it used to work directly. For some reason, the character controller component was changed. It no longer respects this at all as of 2018.2 or so.

    I have a Spawn Marker system in mind that acts as a target for the player controller to use as a refrence when starting a level or traveling between two scenes. Think Fallout 3+ or the Elder Scrolls Oblivion/Skyrim.

    Here is my code exacly at this moment.

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class SpawnTarget : MonoBehaviour
    7. {
    8.     public string TargetSpawn = "SpawnName";
    9.     public string TargetScene = "SceneName";
    10.  
    11.     public void SetSpawnTarget(string _spawn)
    12.     {
    13.         TargetSpawn = _spawn;
    14.         Debug.Log("Player's SpawnTarget is now set to: " + TargetSpawn);
    15.     }
    16.  
    17.     public void SetSceneTarget(string _scene)
    18.     {
    19.         TargetScene = _scene;
    20.         Debug.Log("Player's SceneTarget is now set to: " + TargetScene);
    21.     }
    22. }
    This code sits on the Player Controller which is a modified version of the Standard Assets First Person Controller that removes CrossPlatformInput dependency.

    This character Controller is Presisitent by being a child of the Game Mananger Singleton I've created out of an Event Mananger.

    The second Part goes onto the Spawn marker itself.
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class SpawnMarker : MonoBehaviour
    7. {
    8.     public string MarkerAddress = "SpawnName";
    9.     public string PlayerTag = "Player";
    10.  
    11.     [SerializeField] private GameObject m_player;
    12.    
    13.     void Start()
    14.     {
    15.         m_player = GameObject.FindGameObjectWithTag(PlayerTag);
    16.  
    17.         if (m_player.GetComponent<SpawnTarget>().TargetSpawn == MarkerAddress)
    18.         {
    19.             m_player.GetComponent<CharacterController>().enabled = false;
    20.             m_player.GetComponent<Player.FirstPerson.FirstPersonController>().enabled = false;
    21.  
    22.             m_player.transform.position = this.transform.position;
    23.             m_player.transform.rotation = this.transform.rotation;
    24.  
    25.             m_player.GetComponent<CharacterController>().enabled = true;
    26.             m_player.GetComponent<Player.FirstPerson.FirstPersonController>().enabled = true;
    27.  
    28.             Debug.Log("The Player should not be at: " + this.transform.position + " position and rotated to: " + this.transform.rotation);
    29.             Debug.Log("If this is not the case then something has really gone wrong...");
    30.         }
    31.     }
    32. }
    This is a Scene Object that can be placed and rotated anywhere in the scene and when the scene gets loaded up and started, looks for the Player Controller by tag and is supposed to move it to itself and then rotate the player controller to match.

    The code works becuase it moves a cubes with the Spawn Targets script attached and tagged as player just fine. It even rotates it correctly not an issue. But when the CharacterController Capsule is attached, It looses all respect for the position and rotation. It's as if it never happened.

    The position can be fixed by turning off the CharacterController Component while the move is being made, but rotation is still ignored.

    Your game must be using an older version of unity becuase this is a fairly new issue.

    I've used this method before for a while with out issue untill a couble of version ago.
     
    PotatoFire1000 likes this.
  5. lukasz13

    lukasz13

    Joined:
    Jul 10, 2017
    Posts:
    7
    I had similar issue with a script that moves player to Scene's zero position. When CharacterController script was active I couldn't move player even in the editor play mode. I was able to solve it by change Edit>>Project Settings>>Physics>>Auto Sync Transform set to True. After that I was able to drag my player object in play mode.

    Here is the fix source and explanation: https://issuetracker.unity3d.com/is...-when-teleporting-with-transform-dot-position
     
  6. GamingFriendsCZ

    GamingFriendsCZ

    Joined:
    Mar 1, 2017
    Posts:
    2
    Ty sir
     
  7. ishwara_bhat

    ishwara_bhat

    Joined:
    Oct 18, 2018
    Posts:
    11
    Thanks Baste.
    I just added rotation. Also added bit more code on getting the rotation

    //Unity call of instantiation
    char = OnInstantiate(..)
    char.GetComponent<CharacterController>().enabled = false;
    char.transform.position = newPosition.position;
    char.transform.rotation = newPosition.rotation;
    char.GetComponent<CharacterController>().enabled = true;
     
  8. Vharz

    Vharz

    Joined:
    Jul 28, 2012
    Posts:
    25
    I solved it by doing a custom "Warp" method with characterController.Move(Vector3.zero) just before setting it's position/rotation.

    After the "warp" it just continues to pick up regular Move() input from Update().
     
  9. AFStudios

    AFStudios

    Joined:
    Jan 28, 2020
    Posts:
    1
    I just replaced it with a rigidbody component and referenced the rigid body and used transform.translate.

    (Runner game)
     
  10. gamerninjask

    gamerninjask

    Joined:
    Mar 5, 2020
    Posts:
    2
    Baste thanks it works!
     
  11. UnanchoredStudios

    UnanchoredStudios

    Joined:
    Jan 26, 2020
    Posts:
    6
    How?
     
  12. KevinSkocypec

    KevinSkocypec

    Joined:
    Jan 11, 2018
    Posts:
    2
    Thank you so much for this!! I had no clue this setting existed, or would be disabled. I am assuming it is like this to save memory, but for any games with a character in a vehicle this seems like a necessity.

    Anyways, thanks again!
     
  13. Captaingerbear

    Captaingerbear

    Joined:
    Mar 6, 2013
    Posts:
    57

    I was having this same issue and this fixed it for me. Thanks for the save!
     
  14. Reverie_Wisp

    Reverie_Wisp

    Joined:
    Oct 7, 2018
    Posts:
    2
    The enable/disable suggestion earlier in this code behaves the way it does because the act of enabling and disabling a collider (CharacterController's base class) seems to call Physics.SyncTransforms() or some equivalent under the hood. Using the info from the issue linked above, I wrote a Warp extension method for CharacterController so I didn't have to change the entire project's settings:

    Code (CSharp):
    1. public static class CharacterControllerExtensions
    2. {
    3.     public static void Warp(this CharacterController @this, Vector3 target)
    4.     {
    5.         @this.gameObject.transform.position = target;
    6.         Physics.SyncTransforms();
    7.     }
    8. }
    This code could be expanded to take a transform instead of a vector to take rotation into account too before syncing transforms.
     
    Last edited: Sep 20, 2021
    Baste likes this.
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336
    It might be worthwhile to check for performance differences. SyncTransforms syncs all transforms, while it might be that disabling and then enabling the character controller only syncs that one object.
     
    Reverie_Wisp likes this.
  16. Reverie_Wisp

    Reverie_Wisp

    Joined:
    Oct 7, 2018
    Posts:
    2
    TL;DR
    Profiled it, and the cost for either method seemed relatively low if not slightly in favor of using Physics.SyncTransforms(). However, if you have a much higher framerate than your physics step, it may make sense to use enable/disable if you're frequently trying to move a player controller.

    Long answer:
    I figured I'd give this a test - I grabbed unity's 3rd person demo scene, threw in an extra 2000 modest complexity lumpy meshes with mesh colliders and had them randomly rotate to discourage any caching or sneaky stuff. I then threw in 1200 of that same mesh, this time with rigidbodies to give unity something to calculate, and let them bounce off the rotating ones, player, and scene.

    Wrote two warp methods, one wrapping the transform update in enable/disable, one where I call Physics.SyncTransforms(), and configured the scene so I'd warp with one style when pressing 1, and another when pressing 2. Results as follows:


    Force Sync | Enable/Disable
    ---------------------------
    0.032ms | 0.048ms (enable/disable tested first)
    0.046ms | 0.045ms (enable/disable tested first)
    0.033ms | 0.052ms (enable/disable tested first)
    0.031ms | 0.051ms (physics sync tested first)
    0.037ms | 0.044ms (physics sync tested first)
    0.028ms | 0.052ms (physics sync tested first)


    (Rows of data were not collected on the same frame, but in close proximity: For example, I'd press 2 then 1 and pause the profiler. Objects were allowed to settle at the beginning of the frame for two seconds so I could even operate the profiler.)

    The difference appears to be negligible if not slightly in favor of Physics.SyncTransforms(), tho frame times were in the 30-40ms range. So does this mean Physics.SyncTransforms() is better? Not quite.

    I noticed that unity calls SyncColliderTransform every fixed update anyways, which in my setup was taking an extra 4ms or so depending on what lumps tumbled where. I forced both warp styles to run on frames without fixed update and in that situation, Physics.SyncTransforms() would cost its usual plus that extra 4ms. Enable/Disable didn't have that extra cost, so depending on your title or physics setup it may make sense to use one over the other.

    If there's a way to call Physics.SyncTransforms() but specify a specific transform, that's probably the best of both worlds, but at time of writing I've not found something for that.
     
    Last edited: Sep 20, 2021
    idwilson001 likes this.
  17. Lenwit

    Lenwit

    Joined:
    Sep 22, 2021
    Posts:
    1
    OMG UR A GENIUSSSS KSMDKSMD I WAS HAVING THE SAME PROBLEM AAAAA DUDE U SAVE MY LIFE <3 LOVE U
     
    lukasz13 and Biscuitlid like this.
  18. jafrank26

    jafrank26

    Joined:
    Jul 5, 2019
    Posts:
    1
    Thank you for this. I'm very lucky to have stumbled upon this thread. I've been debugging for days and I don't think I would have caught this on my own.