public void _Update(float timeStep)
    {
        updateTimers(timeStep);

        updateState(timeStep);

        // read input from input driver
        Vector2 input = new Vector2(m_InputDriver.Horizontal, m_InputDriver.Vertical);

        int rawInputX = 0;

        if (input.x > 0.0f)
        {
            rawInputX = 1;
        }
        else if (input.x < 0.0f)
        {
            rawInputX = -1;
        }

        // check which side of character is collided
        int wallDirX = m_Motor.Collisions.Right ? 1 : -1;

        // check if want dashing
        if (m_InputDriver.Dash)
        {
            startDash(rawInputX);
        }

        // check if want climbing ladder
        if (IsInLadderArea())
        {
            if (IsInLadderTopArea())
            {
                if (input.y < 0.0f)
                {
                    enterLadderState();
                }
            }
            else
            {
                if (input.y > 0.0f)
                {
                    enterLadderState();
                }
            }
        }

        // dashing state
        if (IsState(MotorState.Dashing))
        {
            m_Velocity.x = m_DashState.DashDir * m_DashState.GetDashSpeed(); //getDashSpeed();

            if (!IsGrounded() && DashModule.UseGravity)
            {
                m_Velocity.y = 0;
            }

            if (DashModule.ChangeFacing)
            {
                FacingDirection = (int)Mathf.Sign(m_Velocity.x);
            }

            if (DashModule.UseCollision)
            {
                m_Motor.Move(m_Velocity * timeStep, false);
            }
            // teleport, if there is no obstacle on the target position -> teleport, or use collision to find the closest teleport position
            else
            {
                bool cannotTeleportTo = Physics2D.OverlapBox(
                    m_Motor.Collider2D.bounds.center + m_Velocity * timeStep,
                    m_Motor.Collider2D.bounds.size,
                    0.0f,
                    m_Motor.Raycaster.CollisionLayer);

                if (!cannotTeleportTo)
                {
                    m_Motor.transform.Translate(m_Velocity * timeStep);
                }
                else
                {
                    m_Motor.Move(m_Velocity * timeStep, false);
                }
            }
        }

        // on custom action
        else if (IsState(MotorState.CustomAction))
        {
            //m_Velocity.x = m_ActionState.GetActionVelocity();

            //if (!IsGrounded() && DashModule.UseGravity)
            //    m_Velocity.y = 0;
        }

        // on ladder state
        else if (IsState(MotorState.OnLadder))
        {
            m_Velocity = input * m_MovementSettings.OnLadderSpeed;

            // jump if jump input is true
            if (m_InputDriver.Jump)
            {
                startJump(false, rawInputX, wallDirX);
            }

            if (m_Velocity.x != 0.0f)
            {
                FacingDirection = (int)Mathf.Sign(m_Velocity.x);
            }

            //m_Motor.Move(m_Velocity * timeStep, false);

            // dont do collision detection
            if (m_OnLadderState.HasRestrictedArea)
            {
                // outside right, moving right disallowed
                if (m_Motor.transform.position.x > m_OnLadderState.RestrictedAreaTopRight.x)
                {
                    if (m_Velocity.x > 0.0f)
                    {
                        m_Velocity.x = 0.0f;
                    }
                }

                // outside left, moving left disallowed
                if (m_Motor.transform.position.x < m_OnLadderState.RestrictedAreaBottomLeft.x)
                {
                    if (m_Velocity.x < 0.0f)
                    {
                        m_Velocity.x = 0.0f;
                    }
                }

                // outside up, moving up disallowed
                if (m_Motor.transform.position.y > m_OnLadderState.RestrictedAreaTopRight.y)
                {
                    if (m_Velocity.y > 0.0f)
                    {
                        m_Velocity.y = 0.0f;
                    }
                }

                // outside down, moving down disallowed
                if (m_Motor.transform.position.y < m_OnLadderState.RestrictedAreaBottomLeft.y)
                {
                    if (m_Velocity.y < 0.0f)
                    {
                        m_Velocity.y = 0.0f;
                    }
                }
            }

            /*
             * var newPos = m_Motor.transform.position + m_Velocity * timeStep;
             *
             * if (m_OnLadderState.HasRestrictedArea)
             * {
             *
             * }
             */
            m_Motor.transform.position += m_Velocity * timeStep;

            if (m_OnLadderState.HasRestrictedArea)
            {
                Vector2 pos = m_Motor.transform.position;
                pos.x = Mathf.Clamp(pos.x, m_OnLadderState.RestrictedAreaBottomLeft.x, m_OnLadderState.RestrictedAreaTopRight.x);
                pos.y = Mathf.Clamp(pos.y, m_OnLadderState.RestrictedAreaBottomLeft.y, m_OnLadderState.RestrictedAreaTopRight.y);

                // restricted in x axis
                if (pos.x != m_Motor.transform.position.x)
                {
                    pos.x = Mathf.Lerp(m_Motor.transform.position.x, pos.x, 0.25f);
                }

                // restricted in y axis
                if (pos.y != m_Motor.transform.position.y)
                {
                    pos.y = Mathf.Lerp(m_Motor.transform.position.y, pos.y, 0.25f);
                }

                m_Motor.transform.position = pos;
            }
        }

        else // other state
        {
            // fall through one way platform
            if (m_InputDriver.HoldingJump && input.y < 0.0f)
            {
                m_Motor.FallThrough();
            }

            // setup velocity.x based on input
            float targetVecX = input.x * m_MovementSettings.Speed;

            // smooth x direction motion
            if (IsGrounded())
            {
                m_Velocity.x         = targetVecX;
                m_VelocityXSmoothing = targetVecX;
            }
            else
            {
                m_Velocity.x = Mathf.SmoothDamp(m_Velocity.x, targetVecX, ref m_VelocityXSmoothing, m_MovementSettings.AccelerationTimeAirborne);
            }

            /*
             * m_Velocity.x = Mathf.SmoothDamp(m_Velocity.x, targetVecX, ref m_VelocityXSmoothing,
             *  (m_Motor.Collisions.Below) ? m_MovementSettings.AccelerationTimeGrounded : m_MovementSettings.AccelerationTimeAirborne);
             */

            // check wall sliding and jumping
            bool wallSliding = false;

            if (!IsGrounded())
            {
                if (IsAgainstWall())
                {
                    changeState(MotorState.WallSliding);
                    wallSliding = true;

                    if (m_Velocity.y < -m_WallJumpSettings.WallSlidingSpeedMax)
                    {
                        m_Velocity.y = -m_WallJumpSettings.WallSlidingSpeedMax;
                    }

                    // check if still sticking to wall
                    if (m_WallJumpSettings.WallStickTimer > 0.0f)
                    {
                        m_VelocityXSmoothing = 0;
                        m_Velocity.x         = 0;

                        if (input.x != wallDirX && input.x != 0)
                        {
                            m_WallJumpSettings.WallStickTimer -= timeStep;
                        }
                        else
                        {
                            m_WallJumpSettings.WallStickTimer = m_WallJumpSettings.WallStickTime;
                        }
                    }
                    else
                    {
                        m_WallJumpSettings.WallStickTimer = m_WallJumpSettings.WallStickTime;
                    }
                }
            }

            // Reset gravity if collision happened in y axis
            if (m_Motor.Collisions.Above || m_Motor.Collisions.Below)
            {
                //Debug.Log("Reset Vec Y");
                m_Velocity.y = 0;
            }

            // jump if jump input is true
            if (m_InputDriver.Jump && input.y >= 0.0f)
            {
                startJump(wallSliding, rawInputX, wallDirX);
            }

            // variable jump height based on user input
            if (m_JumpSettings.HasVariableJumpHeight)
            {
                if (m_InputDriver.ReleaseJump && input.y >= 0.0f)
                {
                    if (m_Velocity.y > m_MinJumpSpeed)
                    {
                        m_Velocity.y = m_MinJumpSpeed;
                    }
                }
            }

            if (m_ApplyGravity)
            {
                m_Velocity.y += m_Gravity * timeStep;
            }

            if (wallSliding)
            {
                FacingDirection = (m_Motor.Collisions.Left) ? 1 : -1;
            }
            else
            {
                if (m_Velocity.x != 0.0f)
                {
                    FacingDirection = (int)Mathf.Sign(m_Velocity.x);
                }
            }

            m_Motor.Move(m_Velocity * timeStep, false);
        }

        // check ladder area
        if (IsInLadderArea())
        {
            if (m_OnLadderState.BottomArea.Contains(m_Motor.Collider2D.bounds.center))
            {
                m_OnLadderState.AreaZone = LadderZone.Bottom;
            }
            else if (m_OnLadderState.TopArea.Contains(m_Motor.Collider2D.bounds.center))
            {
                m_OnLadderState.AreaZone = LadderZone.Top;
            }
            else if (m_OnLadderState.Area.Contains(m_Motor.Collider2D.bounds.center))
            {
                m_OnLadderState.AreaZone = LadderZone.Middle;
            }
        }
    }