Search Unity

Problem with setting transform.position of a player (FPS Controller)

Discussion in 'Physics' started by wztk, Oct 31, 2020.

  1. wztk

    wztk

    Joined:
    Feb 25, 2020
    Posts:
    36
    Hello guys,

    I've been developing a 3D game that's using FPS Controller from Standard Assets and I've run into a weird issue. I'm trying to set a player's position on a button press and while sometimes it does work exactly as intended, other times the player would be transported about halfway through. It's really frustrating, because in this case the exact point does make a huge difference for me.
    The function I've written is really simple:
    Code (CSharp):
    1. void HandlePositionSwitch(int stage)
    2. {
    3.         playerObject.transform.position = positions[stage]; //positions is a list that holds Vector3 positions
    4. }
    I'm using Unity 2019.3.6f1 and the target platform for the project is Android. Player object is the same as FPS Controller prefab except it has an extra script (using Update, nothing related to position) on the parent and the settings have been tweaked a bit (screenshot attached), It's also worth noting that the issue occurs more frequently on Editor playthroughs than on any Android device, but it does happen on Android as well.
    So far I've tried disabling Character controller component before transform.position and enabling it afterwards, but it hasn't helped.
     

    Attached Files:

    Last edited: Nov 1, 2020
  2. Monique_Dumont

    Monique_Dumont

    Joined:
    Feb 18, 2020
    Posts:
    41
    Hello,

    I replicated your issue. I think that this is because of this line of code in the FPS Controller (l. 129):

    Code (CSharp):
    1. m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime);
    Try to comment it out and test if your position does change. If that's the case, maybe you will need to add a condition to this line, so that it only occurs when you want.

    Let me know if that helped.

    Monique
     
    DhanashreeVinherkar and wztk like this.
  3. wztk

    wztk

    Joined:
    Feb 25, 2020
    Posts:
    36
    Hi Monique,

    Thank you very much for your swift response.

    I can now confirm that line 129 in FPS Controller is definitely the one that's causing the issue to occur, as commenting it out removes the issue completely.

    I've also followed your suggestions to try and add a conditional case for this line and added a bool to the code that I would flip during the procedure, but unfortunately it hasn't fixed it completely, although it's way better with it than without it.

    I've figured that Move function is causing some changes to the player which makes the issue occur in the first place and that's why disabling CharacterController component doesn't help. I'm looking into possible solutions right now, I've tried resetting the CollisionFlags manually before and/or after reposition, but that only reduces the volume of issue occurring. I'll post again when I find something that works.

    Thanks once again, you're a lifesaver.

    EDIT: I've tried the solution suggested by Monique once more, this time I've adjusted Physics settings of the project to include 'Auto Sync Transforms'. The issue occurs extremely rarely, I'd say about 1 in 50 cases and the character is way closer to the given position. Also, by the time script executes fully, it thinks that character position is in fact the same as the given position. Perhaps WaitForEndOfFrame in between could help fix the issue.
     
    Last edited: Nov 2, 2020
  4. Monique_Dumont

    Monique_Dumont

    Joined:
    Feb 18, 2020
    Posts:
    41
    Hello,

    Try this solution :
    Code (CSharp):
    1. public class CharacterPosition : MonoBehaviour
    2. {
    3.     private CharacterController characterController;
    4.  
    5.     void Start()
    6.     {
    7.         characterController = GetComponent<CharacterController>();
    8.     }
    9.  
    10.     void HandlePositionSwitch(int stage)
    11.     {
    12.         characterController.enabled = false;
    13.         gameObject.transform.position = positions[stage]; //positions is a list that holds Vector3 positions
    14.         print(positions[stage]);
    15.         characterController.enabled = true;
    16.     }
    17. }
    What I do is get a reference of the character controller in the script (because that what the function Move() at l.129 originates from). Then I disable it, change the position, then re-enable it. It worked on my side.

    Hope that helps.

    Monique,
     
    anurrag116_unity and wztk like this.
  5. ashayemartins

    ashayemartins

    Joined:
    Sep 29, 2020
    Posts:
    5
  6. wztk

    wztk

    Joined:
    Feb 25, 2020
    Posts:
    36
    Hello,

    I've tried that already and it does work quite well, but on odd occasion it's a bit off.

    Examples: Expected - (0.76f, 0.98f, -15.59f) Actual - (0.6057093f, 0.98f, -16.10035f). Expected - (6.05f, 0.98f, -12.2f) Actual - (4.871642f, 0.98f, -12.87739f)
    Test case: I jump and while mid jump I set the positions.

    It happens no matter what solution I try to use. Perhaps it's because in Editor I get about 200 fps and something blocks it while it happens. It happens far less often and it's way closer on Android which runs at about 60 fps.
    I added a Debug.Log to the end of the script to see the results and even in cases like the ones mentioned above,
    Debug.Log(playerObject.transform.position == positions[stage]);
    always shows true.

    The best solution I've used this far goes as follows:

    First Person Controller:
    Code (CSharp):
    1. if (!teleporting)
    2.             {
    3.                 m_CollisionFlags = m_CharacterController.Move(m_MoveDir * Time.fixedDeltaTime);
    4.             }

    Player script:
    Code (CSharp):
    1.     IEnumerator RepositionRoutine(int stage)
    2.     {
    3.         playerController.teleporting = true;
    4.         playerObject.transform.position = positions[stage];
    5.         yield return new WaitForEndOfFrame();
    6.         playerController.teleporting = false;
    7.     }
    And then I wrap it in a void function to put on a button:
    Code (CSharp):
    1. void HandlePositionSwitch(int stage)
    2.     {
    3.         StartCoroutine(RepositionRoutine(stage));
    4.         Debug.Log(playerObject.transform.position == positions[stage]);
    5.     }
    I'm still gonna look for a better solution than that, but for now it's serviceable.
     
    Last edited: Nov 3, 2020
    Volchok likes this.
  7. wztk

    wztk

    Joined:
    Feb 25, 2020
    Posts:
    36
    I think I found the solution, so far I've tested it in Editor for about 15 minutes just jumping and changing positions and had no offset on any of the positions. Everything is pretty much the same as the post above, but I've adjusted the coroutine like so:
    Code (CSharp):
    1. IEnumerator RepositionRoutine(int stage)
    2. {
    3.         playerController.teleporting = true;
    4.         yield return new WaitForFixedUpdate();
    5.         playerObject.transform.position = positions[stage];
    6.         yield return new WaitForFixedUpdate();
    7.         playerController.teleporting = false;
    8. }
    It actually makes a lot of sense to me as well, since FixedUpdate was the thing that the whole bug was caused by, so technically taking it out of equation should fix it.

    I'll test it a bit more on Editor and then Android to confirm and post results in edit tommorow.

    EDIT: Had no issues with this solution, I believe this is it. Thanks once again, Monique. It's been bugging me for a month at least.
     
    Last edited: Nov 4, 2020
    dTMSr, Volchok and Monique_Dumont like this.