Created
February 19, 2018 11:00
-
-
Save robc/c02597dcd56085bbf5a7c179101c02ea to your computer and use it in GitHub Desktop.
Touch Input Code from PocketDogfights - this was integrated into a wider input framework, which handles taking a number of calls from this and translating it into player input.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class InputController : MonoBehaviour | |
{ | |
public System.Action<bool> OnSetDirectionIndicatorVisibility; | |
public System.Action<float> OnSetDirectionIndicatorHeading; | |
public System.Action OnMenuMoveUp; | |
public System.Action OnMenuMoveDown; | |
public System.Action OnMenuMoveLeft; | |
public System.Action OnMenuMoveRight; | |
public System.Action OnMenuSelect; | |
public System.Action OnMenuBack; | |
public System.Action OnPlayerPressedPause; | |
private InputControllerMode inputMode; | |
private bool shouldShowDirectionIndicator; | |
private bool includePrimaryFireForBurst; | |
private float realTimeAtEndOfPreviousFrame; | |
private List<ITouchMouseInput> touchMouseHandlers; | |
private List<IGamepadGameplayAction> gameplayControlHandlers; | |
private List<IGamepadMenuAction> menuControlHandlers; | |
void Awake() | |
{ | |
gameplayControlHandlers = new List<IGamepadGameplayAction>(); | |
menuControlHandlers = new List<IGamepadMenuAction>(); | |
touchMouseHandlers = new List<ITouchMouseInput>(); | |
shouldShowDirectionIndicator = false; | |
inputMode = InputControllerMode.HandleMenuInputs; | |
#if (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_METRO) && !UNITY_EDITOR | |
touchMouseHandlers.Add(new TouchInputController()); | |
shouldShowDirectionIndicator = true; | |
#elif (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8) && UNITY_EDITOR | |
touchMouseHandlers.Add(new MouseTouchInputController()); | |
shouldShowDirectionIndicator = true; | |
#endif | |
#if UNITY_METRO || UNITY_WEBPLAYER || UNITY_STANDALONE | |
touchMouseHandlers.Add(new MouseInputController()); | |
#endif | |
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_METRO || UNITY_ANDROID | |
KeyboardMapper keyboardMapper = new KeyboardMapper("Keyboard"); | |
gameplayControlHandlers.Add(keyboardMapper); | |
menuControlHandlers.Add(keyboardMapper); | |
#endif | |
} | |
void Start() | |
{ | |
if (ControllerMappingService.Instance.SelectedMapper != null) | |
{ | |
gameplayControlHandlers.Add(ControllerMappingService.Instance.SelectedMapper); | |
menuControlHandlers.Add(ControllerMappingService.Instance.SelectedMapper); | |
} | |
ControllerMappingService.Instance.OnSelectedMapperDidChange += OnSelectedMapperDidChange; | |
realTimeAtEndOfPreviousFrame = Time.realtimeSinceStartup; | |
} | |
public void ConfigurePlayer(GameObject player) | |
{ | |
PlayerRotation = player.GetComponent<SimplePlaneRotation>(); | |
PlayerWeapons = player.GetComponent<PlayerGunner>(); | |
} | |
void Update() | |
{ | |
float realDeltaTime = Time.realtimeSinceStartup - realTimeAtEndOfPreviousFrame; | |
ControllerMappingService.Instance.CheckIfSelectedMapperIsAvailable(); | |
#if UNITY_IPHONE | |
iCadeBinding.updateState(); | |
#endif | |
if (inputMode == InputControllerMode.HandlePlayerInputs) | |
{ | |
includePrimaryFireForBurst = false; | |
bool hasDoneMovement = false; | |
bool isFiringPrimaryFire = false; | |
bool hasFiredSecondaryBurst = false; | |
for (int index = 0; index < touchMouseHandlers.Count; index++) | |
{ | |
touchMouseHandlers[index].Update(realDeltaTime); | |
if (touchMouseHandlers[index].IsMovementActive() && !hasDoneMovement) | |
{ | |
float newHeading = touchMouseHandlers[index].DestinationHeading(); | |
PlayerRotation.TurnPlaneToNewHeading(newHeading); | |
hasDoneMovement = true; | |
if (shouldShowDirectionIndicator) | |
{ | |
if (OnSetDirectionIndicatorVisibility != null) | |
OnSetDirectionIndicatorVisibility(true); | |
if (OnSetDirectionIndicatorHeading != null) | |
OnSetDirectionIndicatorHeading(newHeading); | |
} | |
} | |
else if (shouldShowDirectionIndicator) | |
{ | |
if (OnSetDirectionIndicatorVisibility != null) | |
OnSetDirectionIndicatorVisibility(false); | |
} | |
isFiringPrimaryFire |= touchMouseHandlers[index].IsPrimaryFireActive(); | |
hasFiredSecondaryBurst |= touchMouseHandlers[index].HasFiredSecondaryBurst(); | |
if (hasFiredSecondaryBurst) includePrimaryFireForBurst = true; | |
} | |
for (int index = 0; index < gameplayControlHandlers.Count; index++) | |
{ | |
if (!hasDoneMovement) | |
{ | |
float horizontalInput = 0f, verticalInput = 0f; | |
if (gameplayControlHandlers[index].IsSupportingDirectionalControls() && | |
PreferencesController.Instance.ControlType == GamepadControlType.Directional) | |
{ | |
verticalInput = gameplayControlHandlers[index].VerticalAxis(); | |
horizontalInput = gameplayControlHandlers[index].HorizontalAxis(); | |
if (!Mathf.Approximately(verticalInput, 0f) || !Mathf.Approximately(horizontalInput, 0f)) | |
PlayerRotation.TurnPlaneToNewHeading(MathUtilities.NormaliseAngle(Mathf.Atan2(horizontalInput, verticalInput) * Mathf.Rad2Deg)); | |
} | |
else | |
{ | |
horizontalInput = gameplayControlHandlers[index].HorizontalAxis(); | |
PlayerRotation.TurnYawByYawDelta(horizontalInput, Time.smoothDeltaTime, !Mathf.Approximately(horizontalInput, 0f)); | |
} | |
hasDoneMovement = !(Mathf.Approximately(horizontalInput, 0f) && Mathf.Approximately(verticalInput, 0f)); | |
} | |
if (gameplayControlHandlers[index].HasPressedPause() && OnPlayerPressedPause != null) | |
{ | |
OnPlayerPressedPause(); | |
return; | |
} | |
isFiringPrimaryFire |= gameplayControlHandlers[index].IsPrimaryFireActive(); | |
hasFiredSecondaryBurst |= gameplayControlHandlers[index].HasFiredSecondaryBurst(); | |
} | |
if (isFiringPrimaryFire) PlayerWeapons.StartPrimaryAutoFire(); | |
else PlayerWeapons.StopPrimaryAutoFire(); | |
/** | |
* Handles secondary fire. If we're on touch, then we fire a single burst | |
* (which falls back to primary if no ammo's available). For controllers, | |
* we use a secondary auto fire - which properly factors in the reload delays. | |
*/ | |
if (includePrimaryFireForBurst && hasFiredSecondaryBurst) | |
PlayerWeapons.FireSecondaryBurstIfAvailable(true); | |
else if (!includePrimaryFireForBurst) | |
{ | |
if (hasFiredSecondaryBurst) PlayerWeapons.StartSecondaryAutoFire(); | |
else PlayerWeapons.StopSecondaryAutoFire(); | |
} | |
} | |
else if (inputMode == InputControllerMode.HandleMenuInputs) | |
{ | |
for (int index = 0; index < menuControlHandlers.Count; index++) | |
{ | |
menuControlHandlers[index].UpdateMenuState(realDeltaTime); | |
if (menuControlHandlers[index].HasPressedMenuUp() && OnMenuMoveUp != null) | |
OnMenuMoveUp(); | |
else if (menuControlHandlers[index].HasPressedMenuDown() && OnMenuMoveDown != null) | |
OnMenuMoveDown(); | |
else if (menuControlHandlers[index].HasPressedMenuLeft() && OnMenuMoveLeft != null) | |
OnMenuMoveLeft(); | |
else if (menuControlHandlers[index].HasPressedMenuRight() && OnMenuMoveRight != null) | |
OnMenuMoveRight(); | |
if (menuControlHandlers[index].HasPressedMenuSelect() && OnMenuSelect != null) | |
OnMenuSelect(); | |
else if (menuControlHandlers[index].HasPressedMenuBack() && OnMenuBack != null) | |
OnMenuBack(); | |
else if (menuControlHandlers[index].HasPressedMenuBack() && OnPlayerPressedPause != null) | |
OnPlayerPressedPause(); | |
} | |
} | |
realTimeAtEndOfPreviousFrame = Time.realtimeSinceStartup; | |
} | |
public InputControllerMode InputMode | |
{ | |
get { return inputMode; } | |
set | |
{ | |
inputMode = value; | |
if (value == InputControllerMode.HandleMenuInputs) | |
{ | |
for (int index = 0; index < touchMouseHandlers.Count; index++) | |
touchMouseHandlers[index].ClearAllInputs(); | |
if (PlayerWeapons != null) PlayerWeapons.StopPrimaryAutoFire(); | |
if (PlayerRotation != null) PlayerRotation.enabled = false; | |
if (shouldShowDirectionIndicator && OnSetDirectionIndicatorVisibility != null) | |
OnSetDirectionIndicatorVisibility(false); | |
} | |
} | |
} | |
public SimplePlaneRotation PlayerRotation | |
{ | |
get; set; | |
} | |
public PlayerGunner PlayerWeapons | |
{ | |
get; set; | |
} | |
private void OnSelectedMapperDidChange(AbstractControllerMapper removedMapper, AbstractControllerMapper newMapper) | |
{ | |
if (removedMapper != null) | |
{ | |
if (gameplayControlHandlers.Contains(removedMapper)) gameplayControlHandlers.Remove(removedMapper); | |
if (menuControlHandlers.Contains(removedMapper)) menuControlHandlers.Remove(removedMapper); | |
} | |
if (newMapper != null) | |
{ | |
gameplayControlHandlers.Add(newMapper); | |
menuControlHandlers.Add(newMapper); | |
} | |
} | |
} | |
public enum InputControllerMode | |
{ | |
HandlePlayerInputs, | |
HandleMenuInputs | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class TouchInputController : ITouchMouseInput | |
{ | |
private const float AUTOFIRE_THRESHOLD = 0.166666667f; | |
private TouchDetails moveTouch; | |
private List<TouchDetails> activeFireTouches; | |
private List<TouchDetails> fireTouchesEndedThisFrame; | |
private List<Vector3> positionsOfAllEndedTouches; | |
private float computedHeading; | |
private bool isOutsideRange; | |
private float sqrDeadzone; | |
public TouchInputController() | |
{ | |
activeFireTouches = new List<TouchDetails>(); | |
positionsOfAllEndedTouches = new List<Vector3>(); | |
fireTouchesEndedThisFrame = new List<TouchDetails>(); | |
sqrDeadzone = Deadzone * Deadzone; | |
} | |
private float Deadzone | |
{ | |
get | |
{ | |
if (Screen.dpi < 250.0f) | |
return 15.0f; | |
else if (Screen.dpi < 300.0f) | |
return 20.0f; | |
else | |
return 30.0f; | |
} | |
} | |
private TouchDetails FireTouchForFingerId(int fingerId) | |
{ | |
for (int index = 0; index < activeFireTouches.Count; index++) | |
{ | |
if (activeFireTouches[index].FingerId == fingerId) | |
return activeFireTouches[index]; | |
} | |
return null; | |
} | |
private void UpdateMovementTouch(Touch touch, float delta) | |
{ | |
if (moveTouch == null || moveTouch.FingerId != touch.fingerId) return; | |
moveTouch.Update(touch, delta); | |
Vector2 destinationVector = moveTouch.Current - moveTouch.Origin; | |
if (!isOutsideRange) isOutsideRange = destinationVector.sqrMagnitude > sqrDeadzone; | |
computedHeading = MathUtilities.NormaliseAngle(Mathf.Atan2(destinationVector.x, destinationVector.y) * Mathf.Rad2Deg); | |
} | |
#region ITouchMouseInput methods | |
public void Update(float delta) | |
{ | |
fireTouchesEndedThisFrame.Clear(); | |
positionsOfAllEndedTouches.Clear(); | |
int numberOfTouches = Input.touchCount; | |
for (int index = 0; index < numberOfTouches; index++) | |
{ | |
Touch touch = Input.GetTouch(index); | |
if (moveTouch == null && touch.phase == TouchPhase.Began) | |
{ | |
moveTouch = new TouchDetails(touch.fingerId, touch.position); | |
isOutsideRange = false; | |
} | |
else if (moveTouch != null && moveTouch.FingerId == touch.fingerId) | |
{ | |
UpdateMovementTouch(touch, delta); | |
if (touch.phase == TouchPhase.Canceled || touch.phase == TouchPhase.Ended) | |
{ | |
positionsOfAllEndedTouches.Add(new Vector3(moveTouch.Current.x, moveTouch.Current.y, 0)); | |
/** | |
* Checks the ending position of the movement touch to see if we should | |
* treat it as a firing action. This will be the case if the touch never | |
* left the deadzone area, and ended prior to the AUTOFIRE_THRESHOLD period. | |
*/ | |
Vector2 destinationVector = moveTouch.Current - moveTouch.Origin; | |
if (destinationVector.sqrMagnitude < sqrDeadzone && | |
moveTouch.Duration < AUTOFIRE_THRESHOLD && | |
!isOutsideRange) | |
{ | |
fireTouchesEndedThisFrame.Add(moveTouch); | |
} | |
moveTouch = null; | |
} | |
} | |
else | |
{ | |
TouchDetails firingTouch = FireTouchForFingerId(touch.fingerId); | |
if (firingTouch != null) | |
{ | |
firingTouch.Update(touch, delta); | |
if (touch.phase == TouchPhase.Canceled || touch.phase == TouchPhase.Ended) | |
{ | |
activeFireTouches.Remove(firingTouch); | |
fireTouchesEndedThisFrame.Add(firingTouch); | |
positionsOfAllEndedTouches.Add(new Vector3(firingTouch.Current.x, firingTouch.Current.y, 0)); | |
} | |
} | |
else | |
{ | |
firingTouch = new TouchDetails(touch.fingerId, touch.position); | |
activeFireTouches.Add(firingTouch); | |
} | |
} | |
} | |
} | |
public void ClearAllInputs() | |
{ | |
if (moveTouch != null) | |
moveTouch = null; | |
activeFireTouches.Clear(); | |
} | |
public bool IsMovementActive() | |
{ | |
return (moveTouch != null && isOutsideRange); | |
} | |
public float DestinationHeading() | |
{ | |
return computedHeading; | |
} | |
public bool IsPrimaryFireActive() | |
{ | |
bool moveTouchActive = (moveTouch != null && moveTouch.Duration > AUTOFIRE_THRESHOLD); | |
float maxFireTouchDuration = 0; | |
for (int index = 0; index < activeFireTouches.Count; index++) | |
maxFireTouchDuration = Mathf.Max(maxFireTouchDuration, activeFireTouches[index].Duration); | |
return (moveTouchActive || maxFireTouchDuration > AUTOFIRE_THRESHOLD); | |
} | |
public bool HasFiredSecondaryBurst() | |
{ | |
float maxFireTouchDuration = 0f; | |
for (int index = 0; index < fireTouchesEndedThisFrame.Count; index++) | |
maxFireTouchDuration = Mathf.Max(maxFireTouchDuration, fireTouchesEndedThisFrame[index].Duration); | |
return (maxFireTouchDuration > 0f && maxFireTouchDuration < AUTOFIRE_THRESHOLD); | |
} | |
public List<Vector3> InputsEndedInLastFrame() | |
{ | |
return positionsOfAllEndedTouches; | |
} | |
#endregion | |
} | |
#region TouchDetail Class - implementation of the details we want to track for each touch | |
public class TouchDetails | |
{ | |
private int fingerId; | |
private Vector2 origin; | |
private Vector2 current; | |
private float duration; | |
public TouchDetails(int fingerId, Vector2 origin) | |
{ | |
this.fingerId = fingerId; | |
this.origin = origin; | |
this.current = origin; | |
this.duration = 0f; | |
} | |
public void Update(Touch touch, float delta) | |
{ | |
// Watchdog. We shouldn't be processing this touch with this object | |
// if the finger Id's don't match! | |
if (touch.fingerId != this.fingerId) return; | |
this.duration += delta; | |
this.current = touch.position; | |
} | |
public int FingerId | |
{ | |
get { return fingerId; } | |
} | |
public Vector2 Origin | |
{ | |
get { return origin; } | |
} | |
public Vector2 Current | |
{ | |
get { return current; } | |
} | |
public float Duration | |
{ | |
get { return duration; } | |
} | |
} | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment