void OnDrawGizmosSelected()
        {
            if (!m_showGuides)
            {
                return;
            }

            float maxWalkSpeed = m_platformPhysics.SolveMaxSpeedWithAccAndDrag(WalkingAcc, WalkingDrag);

            maxWalkSpeed = Mathf.Min(maxWalkSpeed, MaxWalkingSpeed);

            if (m_jumpingGuideMode == eJumpingGuideMode.MovingDirection)
            {
                if (m_platformPhysics.HSpeed > 0.1f * maxWalkSpeed)
                {
                    m_simulatedMovingDir = 1f;
                }
                else if (m_platformPhysics.HSpeed < -0.1f * maxWalkSpeed)
                {
                    m_simulatedMovingDir = -1f;
                }
            }
            else if (m_jumpingGuideMode == eJumpingGuideMode.Right)
            {
                m_simulatedMovingDir = 1f;
            }
            else if (m_jumpingGuideMode == eJumpingGuideMode.Left)
            {
                m_simulatedMovingDir = -1f;
            }

            if (!Application.isPlaying || IsGrounded || IsClimbing)
            {
                m_jumpingGuideBasePos = transform.position;
            }

            if (Application.isPlaying)
            {
                if (IsGrounded || IsClimbing)
                {
                    m_simulatedStartingHSpeed = m_platformPhysics.HSpeed;
                }
            }
            else
            {
                m_simulatedStartingHSpeed = maxWalkSpeed;
            }

            Vector3 simulationPos   = m_jumpingGuideBasePos + m_jumpingGuideOffset;
            float   maxHDist        = 0f;
            float   minHoldJumpTime = Time.fixedDeltaTime;
            int     iters           = Mathf.CeilToInt(JumpingAccTime / minHoldJumpTime) + 1;
            float   jumpingAccTime  = 0;
            float   timeDt          = Time.fixedDeltaTime;

            PlatformCharacterPhysics physics = new PlatformCharacterPhysics(m_platformPhysics);

            for (int iter = 0; iter < iters; ++iter, jumpingAccTime += minHoldJumpTime)
            {
                physics.Position  = Vector3.zero;
                physics.HDrag     = WalkingDrag;
                physics.MaxHSpeed = MaxWalkingSpeed;
                physics.Velocity  = new Vector3(m_simulatedStartingHSpeed, JumpingSpeed);
                float time          = 0f;
                float maxWhileIters = 1000;
                do
                {
                    Vector3 prevPos = physics.Position;
                    physics.AddAcceleration(m_simulatedMovingDir * Vector2.right * AirborneAcc);
                    if (time < jumpingAccTime)
                    {
                        physics.Acceleration += transform.up * m_jumpingAcc;
                    }
                    time += timeDt;
                    float timeToGround = physics.SolveTimeToPosY(0f);
                    if (timeToGround >= timeDt)
                    {
                        physics.UpdatePhysics(timeDt);
                    }
                    else
                    {
                        physics.UpdatePhysics(timeToGround);
                        physics.Position = new Vector3(physics.Position.x, 0f); // for float imprecisions, pos.y will me almost but not equal to 0f. This will end the loop.
                    }

                    Gizmos.color = Color.Lerp(new Color(0f, 1f, 0f, 0.2f), new Color(0f, 1f, 0f, 0.5f), 1f - (iter + 1f) / iters);
                    Gizmos.DrawLine(prevPos + simulationPos, physics.Position + simulationPos);
                    // Draw max and min high line depending on
                    if (iter == 0 || iter == iters - 1)
                    {
                        Gizmos.color = new Color(0f, 0f, 0f, 0.2f);
                        Gizmos.DrawSphere(prevPos + simulationPos, 0.03f * GizmoUtils.GetGizmoSize(prevPos + simulationPos));
                    }
                }while (physics.Position.y > 0 && --maxWhileIters > 0);
                //Debug.Assert(maxIters > 0, "Infinite loop detected!");
                Gizmos.DrawSphere(physics.Position + simulationPos, 0.03f * GizmoUtils.GetGizmoSize(physics.Position + simulationPos));
                maxHDist = Mathf.Max(maxHDist, Mathf.Abs(physics.Position.x));
            }
            maxHDist *= m_simulatedMovingDir;
            float minJumpHeight = physics.SolveMaxJumpHeight(m_jumpingSpeed);
            float maxJumpHeight = physics.SolveMaxJumpHeight(m_jumpingSpeed, m_jumpingAcc, m_jumpingTime);

            Gizmos.color = new Color(1f, 1f, 0f, 1f);
            Gizmos.DrawLine(simulationPos + new Vector3(-maxHDist * .25f, minJumpHeight), simulationPos + new Vector3(maxHDist * .75f, minJumpHeight));
            Gizmos.color = new Color(1f, 1f, 0f, 1f);
            Gizmos.DrawLine(simulationPos + new Vector3(-maxHDist * .25f, maxJumpHeight), simulationPos + new Vector3(maxHDist * .75f, maxJumpHeight));
            Gizmos.color = Color.white;
        }
        protected virtual void Update()
        {
            if (m_deltaLadderJumpTimeThreshold > 0f)
            {
                m_deltaLadderJumpTimeThreshold -= Time.deltaTime;
            }
            DoClimbing();

            if (!m_isClimbing)
            {
                // Jumping Action
                if (GetActionState(eControllerActions.Jump))
                {
                    bool jumpActionDown = GetIfActionHasChanged(eControllerActions.Jump);
                    int  wallPushFactor = 0;
                    if (jumpActionDown && !IsGrounded)
                    {
                        wallPushFactor = m_smartCollider.SkinRightRayContacts.Contains(true) ? -1 : m_smartCollider.SkinLeftRayContacts.Contains(true) ? 1 : 0;
                        if (transform.localScale.x < 0)
                        {
                            wallPushFactor = -wallPushFactor;
                        }
                    }
                    //wall jump
                    if (jumpActionDown && !IsGrounded && (m_jumpingAdditionalParameters.wallJumps < 0 || m_jumpingAdditionalParameters.wallJumps > m_wallJumpsCounter) &&
                        m_smartCollider.SkinRightRayContacts.Contains(true) || m_smartCollider.SkinLeftRayContacts.Contains(true))    // isMovingAgainstAWall
                    {
                        ++m_wallJumpsCounter;
                        ++m_airJumpsCounter;
                        m_jumpingTimer           = JumpingAccTime;
                        m_platformPhysics.VSpeed = JumpingSpeed;
                        m_platformPhysics.HSpeed = wallPushFactor * m_jumpingAdditionalParameters.wallPushSpeed;
                    }
                    //air jump
                    else if (jumpActionDown && !IsGrounded && (m_jumpingAdditionalParameters.airJumps < 0 || m_jumpingAdditionalParameters.airJumps > m_airJumpsCounter))
                    {
                        ++m_airJumpsCounter;
                        m_jumpingTimer           = JumpingAccTime;
                        m_platformPhysics.VSpeed = JumpingSpeed;
                    }
                    // ground jump
                    else if (
                        IsGrounded &&
                        //GetIfActionHasChanged(eControllerActions.Jumping) && //NOTE: if not commented, player needs to release and press jump to jump
                        m_jumpingTimer == -1 //NOTE: if not commented, player jumps only once if holding jumping while in air
                        )
                    {
                        m_jumpingTimer           = JumpingAccTime;
                        m_platformPhysics.VSpeed = JumpingSpeed;
                        m_isGrounded             = false;
                    }
                }
                else if (!GetActionState(eControllerActions.Jump))
                {
                    m_jumpingTimer = -1f;
                }

                // Moving Action
                if (GetActionState(eControllerActions.Right))
                {
                    m_platformPhysics.AddAcceleration(Vector2.right * HorizontalMovingAcc * m_horSpeedScale);
                }
                if (GetActionState(eControllerActions.Left))
                {
                    m_platformPhysics.AddAcceleration(-Vector2.right * HorizontalMovingAcc * m_horSpeedScale);
                }

                // Platform Drop Down Action
                if (m_isGrounded && m_platformDropTimer <= 0f && GetActionState(eControllerActions.PlatformDropDown))
                {
                    //NOTE: for this to work, OneWayCollisionDown should be removed from LayerCollisions
                    m_smartCollider.LayerCollision      = m_smartCollider.LayerCollision & ~m_smartCollider.OneWayCollisionDown;
                    m_smartCollider.OneWayCollisionDown = 0;
                    m_platformDropTimer = PlatformDropTime;
                }

                // Jumping
                if (m_jumpingTimer > 0f)
                {
                    m_jumpingTimer -= Time.deltaTime;
                    m_platformPhysics.Acceleration += transform.up * m_jumpingAcc;
                }

                // Platform Drop Down
                if (m_platformDropTimer > 0f)
                {
                    m_platformDropTimer -= Time.deltaTime;
                    if (m_platformDropTimer <= 0f)
                    {   // Restore the One Way Down collision again after the time is over
                        m_smartCollider.OneWayCollisionDown = m_savedOneWayCollisionDown;
                    }
                }
                m_instantVelocity           = (transform.position - m_prevPos) / Time.deltaTime;
                m_prevPos                   = transform.position;
                m_platformPhysics.Drag      = new Vector2(m_walkingDrag, 0f);
                m_platformPhysics.MaxHSpeed = MaxWalkingSpeed * m_horSpeedScale;
                m_platformPhysics.Position  = transform.position;
                m_platformPhysics.UpdatePhysics(Time.deltaTime);
                m_groundDist = _CalculateGroundDist();
                bool wasGrounded = m_isGrounded;
                m_isGrounded = m_smartCollider.enabled && m_smartCollider.IsGrounded() && m_platformPhysics.DeltaDisp.y <= 0f;

                // NOTE: Unity 5.4 has a bug in TransformDirection being affected by scale (x is negated to flip the sprite).
                // So instead of calling TransformDirection I will rotate it directly.
                transform.position = transform.position + transform.rotation * (m_platformPhysics.Position - transform.position);

                //+++ Do slopes
                // If over a slope, move the character in the slope direction
                if (m_slopeAngle != 0f)
                {
                    if (Mathf.Abs(m_slopeAngle) <= Mathf.Round(m_maxSlope))
                    {
                        transform.position += transform.up * m_platformPhysics.DeltaDisp.x * Mathf.Tan(m_slopeAngle * Mathf.Deg2Rad);
                        m_isGrounded        = true;
                    }
                    else if (Mathf.Sign(m_slopeAngle) == Mathf.Sign(m_platformPhysics.DeltaDisp.x))
                    {
                        // Avoid climbing up the slope
                        transform.position       = m_platformPhysics.Position = new Vector3(m_prevPos.x, transform.position.y, transform.position.z);
                        m_platformPhysics.HSpeed = 0f;
                        m_isGrounded             = true;
                    }
                }
                // if not over a slope, check if there is a slope under the character to place it to the ground
                else if (!m_isGrounded && wasGrounded && m_platformPhysics.DeltaDisp.y <= 0f)
                {
                    m_groundDist = _CalculateGroundDist();
                    float magneticDist = Mathf.Abs(m_platformPhysics.DeltaDisp.x * Mathf.Tan((m_maxSlope) * Mathf.Deg2Rad));
                    if (m_groundDist <= magneticDist)
                    {
                        m_isGrounded        = true;
                        m_groundDist       += SmartRectCollider2D.k_SkinMinWidth;
                        transform.position += -transform.up * m_groundDist;
                    }
                }
                if (m_isGrounded)
                {
                    m_wallJumpsCounter = m_airJumpsCounter = 0;
                }
                m_slopeAngle = 0f;
                //---
            }

            //Update Actions
            m_actionChanged   = m_prevActionFlags ^ m_actionFlags;
            m_prevActionFlags = m_actionFlags;
        }