/// <summary>
        /// Ticks the kinematic property block forward by a certain delta time.
        /// This will integrate all kinematic properties.
        /// </summary>
        static public Vector2 Integrate(ref KinematicState2D ioProperties, ref KinematicConfig2D inConfig, float inDeltaTime)
        {
            ApplyLimits(ref ioProperties, ref inConfig);
            Vector2 offset = IntegratePosition(ref ioProperties, ref inConfig, inDeltaTime);

            IntegrateVelocity(ref ioProperties, ref inConfig, inDeltaTime);
            ApplyLimits(ref ioProperties, ref inConfig);
            return(offset);
        }
        /// <summary>
        /// Integrates position with velocity, acceleration, and gravity.
        /// </summary>
        static public Vector2 IntegratePosition(ref KinematicState2D ioProperties, ref KinematicConfig2D inConfig, float inDeltaTime)
        {
            float t2 = inDeltaTime * inDeltaTime;
            float dx = (ioProperties.Velocity.x * inDeltaTime)
                       + (((inConfig.Acceleration.x + inConfig.Gravity.x * ioProperties.GravityMultiplier) * t2 * 0.5f));
            float dy = (ioProperties.Velocity.y * inDeltaTime)
                       + (((inConfig.Acceleration.y + inConfig.Gravity.y * ioProperties.GravityMultiplier) * t2 * 0.5f));

            return(new Vector2(dx, dy));
        }
        /// <summary>
        /// Applies speed limits to the given property block.
        /// </summary>
        static public void ApplyLimits(ref KinematicState2D ioProperties, ref KinematicConfig2D inConfig)
        {
            float speed2 = ioProperties.Velocity.sqrMagnitude;
            float high   = inConfig.MaxSpeed;
            float high2  = high * high;
            float low2   = MinSpeed * MinSpeed;

            if (speed2 < low2)
            {
                ioProperties.Velocity.x = ioProperties.Velocity.y = 0;
            }
            else if (high2 > 0 && speed2 > high2)
            {
                ioProperties.Velocity.Normalize();
                ioProperties.Velocity.x *= high;
                ioProperties.Velocity.y *= high;
            }
        }
        /// <summary>
        /// Integrates velocity, accounting for acceleration, gravity, friction, and drag.
        /// </summary>
        static public void IntegrateVelocity(ref KinematicState2D ioProperties, ref KinematicConfig2D inConfig, float inDeltaTime)
        {
            // acceleration
            float x = ioProperties.Velocity.x + (inConfig.Acceleration.x + inConfig.Gravity.x * ioProperties.GravityMultiplier) * inDeltaTime;
            float y = ioProperties.Velocity.y + (inConfig.Acceleration.y + inConfig.Gravity.y * ioProperties.GravityMultiplier) * inDeltaTime;

            // friction
            x = x < 0 ? Math.Min(x + inConfig.Friction.x * inDeltaTime, 0) : Math.Max(x - inConfig.Friction.x * inDeltaTime, 0);
            y = y < 0 ? Math.Min(y + inConfig.Friction.y * inDeltaTime, 0) : Math.Max(y - inConfig.Friction.y * inDeltaTime, 0);

            // drag
            float drag = inConfig.Drag > 0 ? (float)Math.Exp(-inConfig.Drag * inDeltaTime) : 1;

            x *= drag;
            y *= drag;

            // copy
            ioProperties.Velocity.x = x;
            ioProperties.Velocity.y = y;
        }