#region License /* FNA - XNA4 Reimplementation for Desktop Platforms * Copyright 2009-2016 Ethan Lee and the MonoGame Team * * Released under the Microsoft Public License. * See LICENSE for details. */ #endregion #region Using Statements using System; #endregion namespace Microsoft.Xna.Framework.Input { public struct GamePadThumbSticks { #region Public Properties public Vector2 Left { get { return left; } } public Vector2 Right { get { return right; } } #endregion #region Private Variables private Vector2 left; private Vector2 right; #endregion #region Public Constructor public GamePadThumbSticks(Vector2 leftPosition, Vector2 rightPosition) { left = leftPosition; right = rightPosition; ApplySquareClamp(); } #endregion #region Internal Constructor internal GamePadThumbSticks( Vector2 leftPosition, Vector2 rightPosition, GamePadDeadZone deadZoneMode ) { /* XNA applies dead zones before rounding/clamping values. * The public constructor does not allow this because the * dead zone must be known first. */ left = leftPosition; right = rightPosition; ApplyDeadZone(deadZoneMode); if (deadZoneMode == GamePadDeadZone.Circular) { ApplyCircularClamp(); } else { ApplySquareClamp(); } } #endregion #region Private Methods private void ApplyDeadZone(GamePadDeadZone dz) { // Based on the XInput constants const float leftThumbDeadZone = 0.24f; const float rightThumbDeadZone = 0.265f; switch (dz) { case GamePadDeadZone.None: break; case GamePadDeadZone.IndependentAxes: left.X = ExcludeAxisDeadZone(left.X, leftThumbDeadZone); left.Y = ExcludeAxisDeadZone(left.Y, leftThumbDeadZone); right.X = ExcludeAxisDeadZone(right.X, rightThumbDeadZone); right.Y = ExcludeAxisDeadZone(right.Y, rightThumbDeadZone); break; case GamePadDeadZone.Circular: left = ExcludeCircularDeadZone(left, leftThumbDeadZone); right = ExcludeCircularDeadZone(right, rightThumbDeadZone); break; } } private void ApplySquareClamp() { left.X = MathHelper.Clamp(left.X, -1.0f, 1.0f); left.Y = MathHelper.Clamp(left.Y, -1.0f, 1.0f); right.X = MathHelper.Clamp(right.X, -1.0f, 1.0f); right.Y = MathHelper.Clamp(right.Y, -1.0f, 1.0f); } private void ApplyCircularClamp() { if (left.LengthSquared() > 1.0f) { left.Normalize(); } if (right.LengthSquared() > 1.0f) { right.Normalize(); } } #endregion #region Private Static Methods private static float ExcludeAxisDeadZone(float value, float deadZone) { if (value < -deadZone) { value += deadZone; } else if (value > deadZone) { value -= deadZone; } else { return 0.0f; } return value / (1.0f - deadZone); } private static Vector2 ExcludeCircularDeadZone(Vector2 value, float deadZone) { float originalLength = value.Length(); if (originalLength <= deadZone) { return Vector2.Zero; } float newLength = (originalLength - deadZone) / (1.0f - deadZone); return value * (newLength / originalLength); } #endregion #region Public Static Operators and Override Methods /// /// Determines whether two specified instances of /// are equal. /// /// The first object to compare. /// The second object to compare. /// /// True if and are equal; /// otherwise, false. /// public static bool operator ==(GamePadThumbSticks left, GamePadThumbSticks right) { return (left.left == right.left) && (left.right == right.right); } /// /// Determines whether two specified instances of /// are not equal. /// /// The first object to compare. /// The second object to compare. /// /// True if and are not equal; /// otherwise, false. /// public static bool operator !=(GamePadThumbSticks left, GamePadThumbSticks right) { return !(left == right); } /// /// Returns a value indicating whether this instance is equal to a specified object. /// /// An object to compare to this instance. /// /// True if is a and has the /// same value as this instance; otherwise, false. /// public override bool Equals(object obj) { return (obj is GamePadThumbSticks) && (this == (GamePadThumbSticks) obj); } public override int GetHashCode() { return this.Left.GetHashCode() + 37 * this.Right.GetHashCode(); } #endregion } }