Search Unity

Character controller getting "stuck" when going through a door that should easily be "fit-into-able"

Discussion in 'Scripting' started by Deleted User, Jun 24, 2015.

  1. Deleted User

    Deleted User

    Guest

    Essentially my character controller is trying to go through a doorway that is 2 blocks wide and 3 blocks tall (voxel world, each block is 1 unity unit^3 exactly). It walks to the door, gets noticeably slowed down for a few frames, then continues through the door.

    It gets stuck if the height is 2 or over, or if the skin width is too high, variable with the height.


    The character controller settings are shown here:

    http://gyazo.com/bf7446ef8ead4f87bac534dedaddc150

    I have modified the standard assets script a bit, only the FirstPersonController script, I copied it into a new script to avoid losing changes, here is the script:

    Lines that I remember changing are shown in blue for your convenience :D edit: didnt show up, you can see them by looking for comments, telling what each change was for

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityStandardAssets.CrossPlatformInput;
    4. using UnityStandardAssets.Utility;
    5. using Random = UnityEngine.Random;
    6.  
    7. namespace UnityStandardAssets.Characters.FirstPerson
    8. {
    9.     [RequireComponent(typeof (CharacterController))]
    10.     [RequireComponent(typeof (AudioSource))]
    11.     public class PlayerControl : MonoBehaviour
    12.     {
    13.         [SerializeField] private bool m_IsWalking;
    14.         [SerializeField] private bool m_RawInput;
    15.         [SerializeField] private float m_WalkSpeed;
    16.         [SerializeField] private float m_RunSpeed;
    17.         [SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten;
    18.         [SerializeField] private float m_JumpSpeed;
    19.  
    20.         [SerializeField] private float m_GravityMultiplier;
    21.         [SerializeField] private MouseLook m_MouseLook;
    22.         [SerializeField] private bool m_UseFovKick;
    23.         [SerializeField] private FOVKick m_FovKick = new FOVKick();
    24.         [SerializeField] private bool m_UseHeadBob;
    25.         [SerializeField] private CurveControlledBob m_HeadBob = new CurveControlledBob();
    26.         [SerializeField] private LerpControlledBob m_JumpBob = new LerpControlledBob();
    27.         [SerializeField] private float m_StepInterval;
    28.         [SerializeField] private AudioClip[] m_FootstepSounds;    // an array of footstep sounds that will be randomly selected from.
    29.         [SerializeField] private AudioClip m_JumpSound;           // the sound played when character leaves the ground.
    30.         [SerializeField] private AudioClip m_LandSound;           // the sound played when character touches back on ground.
    31.      
    32.         private Camera m_Camera;
    33.         private bool m_Jump;
    34.         private float m_YRotation;
    35.         private Vector2 m_Input;
    36.         private Vector3 m_MoveDir = Vector3.zero;
    37.         private CharacterController m_CharacterController;
    38.         private CollisionFlags m_CollisionFlags;
    39.         private bool m_PreviouslyGrounded;
    40.         private Vector3 m_OriginalCameraPosition;
    41.         private float m_StepCycle;
    42.         private float m_NextStep;
    43.         private bool m_Jumping;
    44.         private AudioSource m_AudioSource;
    45.         private float horizontal;
    46.         private float vertical;
    47.      
    48.         // Use this for initialization
    49.         private void Start()
    50.         {
    51.             m_CharacterController = GetComponent<CharacterController>();
    52.             m_Camera = Camera.main;
    53.             m_OriginalCameraPosition = m_Camera.transform.localPosition;
    54.             m_FovKick.Setup(m_Camera);
    55.             m_HeadBob.Setup(m_Camera, m_StepInterval);
    56.             m_StepCycle = 0f;
    57.             m_NextStep = m_StepCycle/2f;
    58.             m_Jumping = false;
    59.             m_AudioSource = GetComponent<AudioSource>();
    60.             m_MouseLook.Init(transform , m_Camera.transform);
    61.          
    62.          Screen.lockCursor = true;
    63.        //Temporary, to reduce annoyance
    64.         }
    65.  
    66.         private void GetInput(out float speed)
    67.         {
    68.             // Read input
    69.             horizontal = (m_RawInput ? Input.GetAxisRaw("Horizontal") : Input.GetAxis("Horizontal"));
    70.             vertical = (m_RawInput ? Input.GetAxisRaw("Vertical") : Input.GetAxis("Vertical"));
    71.          
    72. //Changed to give the option to not continue walking after key was released ^^^
    73.          
    74.             m_IsWalking = !Input.GetKey(KeyCode.LeftShift);
    75.          
    76.             // set the desired speed to be walking or running
    77.             speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed;
    78.             m_Input = new Vector2(horizontal, vertical);
    79.          
    80.             // normalize input if it exceeds 1 in combined length:
    81.             if (m_Input.sqrMagnitude > 1)
    82.             {
    83.                 m_Input.Normalize();
    84.             }
    85.          
    86.         }
    87.  
    88.      
    89.         // Update is called once per frame
    90.         private void Update()
    91.         {
    92.  
    93.             //   RotateView();
    94.             // the jump state needs to read here to make sure it is not missed
    95.             if (!m_Jump && !m_Jumping)   //Changed because I did not want character to jump after hitting ground
    96.                                                              //if jump was pressed mid air
    97.             {
    98.                 m_Jump = Input.GetButton("Jump");
    99.             }
    100.          
    101.             if ( m_CharacterController.isGrounded)
    102.             {
    103.                 StartCoroutine(m_JumpBob.DoBobCycle());
    104.         //        PlayLandingSound();
    105.                 m_MoveDir.y = 0f;
    106.                 m_Jumping = false;
    107.             }
    108.             if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded)
    109.             {
    110.                 m_MoveDir.y = 0f;
    111.             }
    112.          
    113.             m_PreviouslyGrounded = m_CharacterController.isGrounded;
    114.          
    115.          
    116.          
    117.         }
    118.      
    119.      
    120.         private void PlayLandingSound()
    121.         {
    122.             m_AudioSource.clip = m_LandSound;
    123.             m_AudioSource.Play();
    124.             m_NextStep = m_StepCycle + .5f;
    125.         }
    126.      
    127.      
    128.         private void FixedUpdate()
    129.         {
    130.             float speed;
    131.             GetInput(out speed);
    132.          
    133.             // always move along the camera forward as it is the direction that it being aimed at
    134.             Vector3 desiredMove = transform.forward*m_Input.y + transform.right*m_Input.x;
    135.          
    136.             // get a normal for the surface that is being touched to move along it
    137.             RaycastHit hitInfo;
    138.             Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo,
    139.                                m_CharacterController.height/2f);
    140.           //desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized;
    141.            //Commented this line out to avoid having the player sticking to edges that he/she walks off of, along
    142.            //Side note, I don't fully understand what this does, but it seemed to fix my problem, is this fix a bad
    143.            //solution? Also I'll remove the Physics.SphereCast later if this is a good fix, because it will be unneeded
    144.            //correct?
    145.  
    146.  
    147.  
    148.             m_MoveDir.x = desiredMove.x*speed;
    149.             m_MoveDir.z = desiredMove.z*speed;
    150.          
    151.          
    152.             if (m_CharacterController.isGrounded)
    153.             {
    154.  
    155.                 if (m_Jump)
    156.                 {
    157.                     m_MoveDir.y = m_JumpSpeed;
    158.         //            PlayJumpSound();
    159.                     m_Jump = false;
    160.                     m_Jumping = true;
    161.                 }
    162.             }
    163.             else
    164.             {
    165.                 m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime;
    166.             }
    167.             m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime);
    168.  
    169.             if ( (m_CollisionFlags  & CollisionFlags.Above) != 0 ){
    170.                 if(m_MoveDir.y > 0){
    171.                     m_MoveDir.y = 0;
    172.                 }
    173.             //    m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime;
    174.             }[/COLOR]
    175.  
    176.             //This is my best guess for why it could be going wrong, is it shoving me into the floor somehow?
    177.             //This edit was to make it so I don't stick to the ceiling momentarily when I hit my head on it.
    178.  
    179.  
    180.             ProgressStepCycle(speed);
    181.             UpdateCameraPosition(speed);
    182.             RotateView();
    183.         }
    184.      
    185.      
    186.      
    187.      
    188.         private void ProgressStepCycle(float speed)
    189.         {
    190.             if (m_CharacterController.velocity.sqrMagnitude > 0 && (m_Input.x != 0 || m_Input.y != 0))
    191.             {
    192.                 m_StepCycle += (m_CharacterController.velocity.magnitude + (speed*(m_IsWalking ? 1f : m_RunstepLenghten)))*
    193.                     Time.fixedDeltaTime;
    194.             }
    195.          
    196.             if (!(m_StepCycle > m_NextStep))
    197.             {
    198.                 return;
    199.             }
    200.          
    201.             m_NextStep = m_StepCycle + m_StepInterval;
    202.          
    203.         //    PlayFootStepAudio();
    204.         }
    205.      
    206.      
    207.         private void PlayJumpSound()
    208.         {
    209.             m_AudioSource.clip = m_JumpSound;
    210.             m_AudioSource.Play();
    211.         }
    212.      
    213.      
    214.      
    215.         private void PlayFootStepAudio()
    216.         {
    217.             if (!m_CharacterController.isGrounded)
    218.             {
    219.                 return;
    220.             }
    221.             // pick & play a random footstep sound from the array,
    222.             // excluding sound at index 0
    223.             int n = Random.Range(1, m_FootstepSounds.Length);
    224.             m_AudioSource.clip = m_FootstepSounds[n];
    225.             m_AudioSource.PlayOneShot(m_AudioSource.clip);
    226.             // move picked sound to index 0 so it's not picked next time
    227.             m_FootstepSounds[n] = m_FootstepSounds[0];
    228.             m_FootstepSounds[0] = m_AudioSource.clip;
    229.         }
    230.      
    231.      
    232.         private void UpdateCameraPosition(float speed)
    233.         {
    234.             Vector3 newCameraPosition;
    235.             if (!m_UseHeadBob)
    236.             {
    237.                 return;
    238.             }
    239.             if (m_CharacterController.velocity.magnitude > 0 && m_CharacterController.isGrounded)
    240.             {
    241.                 m_Camera.transform.localPosition =
    242.                     m_HeadBob.DoHeadBob(m_CharacterController.velocity.magnitude +
    243.                                         (speed*(m_IsWalking ? 1f : m_RunstepLenghten)));
    244.                 newCameraPosition = m_Camera.transform.localPosition;
    245.                 newCameraPosition.y = m_Camera.transform.localPosition.y - m_JumpBob.Offset();
    246.             }
    247.             else
    248.             {
    249.                 newCameraPosition = m_Camera.transform.localPosition;
    250.                 newCameraPosition.y = m_OriginalCameraPosition.y - m_JumpBob.Offset();
    251.             }
    252.             m_Camera.transform.localPosition = newCameraPosition;
    253.         }
    254.      
    255.      
    256.  
    257.      
    258.      
    259.         private void RotateView()
    260.         {
    261.             m_MouseLook.LookRotation (transform, m_Camera.transform);
    262.         }
    263.      
    264.      
    265.         private void OnControllerColliderHit(ControllerColliderHit hit)
    266.         {
    267.             Rigidbody body = hit.collider.attachedRigidbody;
    268.             //dont move the rigidbody if the character is on top of it
    269.             if (m_CollisionFlags == CollisionFlags.Below)
    270.             {
    271.                 return;
    272.             }
    273.          
    274.             if (body == null || body.isKinematic)
    275.             {
    276.                 return;
    277.             }
    278.             body.AddForceAtPosition(m_CharacterController.velocity*0.1f, hit.point, ForceMode.Impulse);
    279.         }
    280.      
    281.  
    282.      
    283.      
    284.      
    285.     }
    286.  
    287. }
    288.  

    Bonus question: character teleports upwards the step offset value sometimes when walking into a wall sideways. I assume this may be because the slope is set to 90, but I set that to 90 so that the character wouldn't get stuck when angling against a wall trying to step up to the side.
    Shown here: http://gyazo.com/4154c9a3b8fe67bdc847be90afed2675
    If the slope is set to anything less than 90 the character will not walk up the block to the left, and be stuck until you look farther left.
     
  2. Deleted User

    Deleted User

    Guest

  3. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Your CharacterController values seem OK so I think you're going to have to setup some debugging tests to really figure out the problem.

    First thing I would try is write a script that simply moves your character forward (characterController.Move(transform.forward * 10 * Time.deltaTime);), disable your FPS controller script, line them up in front of a door and see if they walk through without an issue. If so, you at least know that it's somewhere in the FPS controller script.

    Do the same test for the "bonus question" issue as well.

    That will at least give you a starting point to begin debugging. Report your findings.
     
  4. Deleted User

    Deleted User

    Guest

    Video of getting caught on door: https://dl.dropboxusercontent.com/u/85261353/Snagging on door.mp4
    Video of teleporting upwards: http://gyazo.com/ce9a9088cbe7175e151b944189e0d2c6

    Both of these videos were done using the debug thing you suggested.

    Code (CSharp):
    1.  
    2. Vector3 desiredMove = transform.forward* /*10 * Time.deltaTime */ m_Input.y + transform.right* 20 * Time.deltaTime; //   m_Input.x;
    3.  

    edit:
    After further experimentation, I have noticed that when I put my step offset to 2, my character can only go through the door if I jump.

    It seems I can fit through the door un-slowed-down if my step offset and my height added together are less than the door height
     
    Last edited by a moderator: Jun 24, 2015
  5. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Nice music :eek:

    It's not the clearest in a gif, but I'm able to duplicate your issue:


    If I delete the ceiling, it appears to work no problem. I'll start playing with values and see if I can come up with anything.
     
  6. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Setting the Step Offset to 0.75 from 1.2 seems to resolve the issue while still allowing you to walk on blocks:


    My guess is it calculates the step offset from the middle of the capsule, not the bottom, so it's hitting the ceiling of the door frame for a moment (1.1 height + 1.2 step offset > 2 block height?). Just a guess though.
     
  7. Deleted User

    Deleted User

    Guest

    Thats very interesting, then why is it that you cannot set the step offset to anything above your players height?
     
  8. Le-Pampelmuse

    Le-Pampelmuse

    Joined:
    Jan 2, 2015
    Posts:
    26
    This is still an unadressed problem in 2017 with Unity 5.6.

    FPSController (which I used for the first time out of lazyness, I usually test with my real characters anyway)
    has a height of 1.7 and a radius of 0.2, the doorway is 2 high and 1 wide, it couldn't go through!

    Decreasing the Step Offset from Standard 0.3 to 1.5 fixed it. This is a student-level error from Unity.
    I have given up hoping Unity getting a decent team, they simply don't care about us free developers, they just push their agendas and wait for the next paycheck from licensing.
     
  9. Tsilliev

    Tsilliev

    Joined:
    Jan 21, 2014
    Posts:
    35
    My solution based on the idea in this thread:
    https://forum.unity.com/threads/character-controller-step-offset-and-moving-through-gaps.202086/

    1) Create a box collider that is a child of the character and make its scale 0.1.
    2) Put the child a little above and in front of the head of the character
    3) Attach this script to the child:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class StepChanger : MonoBehaviour {
    6.  
    7.     GameObject pawn; // parent - character
    8.     CharacterController controller;
    9.  
    10.     // Use this for initialization
    11.     void Start () {
    12.     pawn = this.gameObject.transform.parent.gameObject;
    13.     controller = pawn.GetComponent<CharacterController>();
    14.     controller.height = 1.8f;
    15.     controller.radius = 0.4f;
    16.     }
    17.  
    18.     // Update is called once per frame
    19.     void Update () {
    20.      
    21.     }
    22.  
    23.     void OnTriggerEnter(Collider other)
    24.     {
    25.        print("colliding with: " + other.gameObject.name);
    26.         if (other.gameObject.name.Contains("Block")) //the name of the object that is the ceilling
    27.         {
    28.         print("we hit ceiling dropping step to 0.1");
    29.         controller.stepOffset = 0.1f;
    30.            
    31.         }
    32.     }
    33.  
    34.      void OnTriggerExit(Collider other)
    35.     {
    36.        print("collision exit");
    37.         if (other.gameObject.name.Contains("Block")) //the name of the object that is the ceilling
    38.         {
    39.         print("collision exit, return step offset to 1.1f");
    40.         controller.stepOffset = 1.1f;
    41.            
    42.         }
    43.     }
    44. }
    45.