Created
December 21, 2015 04:46
-
-
Save n-yoda/b3343c090e1f51dc8a02 to your computer and use it in GitHub Desktop.
Unityでダブルタップやトリプルタップを直接取れないプラットフォームでダブルタップを扱う何か。
This file contains 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.Generic; | |
using System.Linq; | |
/// <summary> | |
/// Touchのラッパー的なクラス。 | |
/// </summary> | |
public struct WrappedTouch | |
{ | |
public Touch original { get; private set; } | |
public int tapCount { get; private set; } | |
public Vector2 deltaPosition { get; private set; } | |
public float deltaTime { get; private set; } | |
public int fingerId { get; private set; } | |
public TouchPhase phase { get; private set; } | |
public Vector2 position { get; private set; } | |
public WrappedTouch(Touch touch, int tapCount = 0) | |
{ | |
this.tapCount = tapCount; | |
original = touch; | |
deltaPosition = original.deltaPosition; | |
deltaTime = original.deltaTime; | |
fingerId = original.fingerId; | |
phase = original.phase; | |
position = original.position; | |
} | |
public WrappedTouch(WrappedTouch wt, int tapCount = 0) | |
{ | |
this.tapCount = tapCount; | |
original = wt.original; | |
deltaPosition = wt.deltaPosition; | |
deltaTime = wt.deltaTime; | |
fingerId = wt.fingerId; | |
phase = wt.phase; | |
position = wt.position; | |
} | |
public WrappedTouch(Vector2 dp, float dt, int id, TouchPhase ph, Vector2 p) | |
{ | |
deltaPosition = dp; | |
deltaTime = dt; | |
fingerId = id; | |
phase = ph; | |
position = p; | |
} | |
} | |
/// <summary> | |
/// 複数回タップを考慮したInput。マウスもTouchに含める。 | |
/// ExecutionOrderをマイナスにすること。 | |
/// </summary> | |
public class WrappedInput : MonoBehaviour | |
{ | |
const int mouseFingerId = int.MinValue; | |
Vector2 lastMouse; | |
float lastTime; | |
[SerializeField] float multiTapIntervalMax = 0.3f; | |
[SerializeField] float multiTapInchMax = 0.2f; | |
[SerializeField] int multiTapCountMax = 3; | |
[SerializeField] bool simulateTouchWithMouse = true; | |
WrappedTouch[] m_touches = new WrappedTouch[0]; | |
List<WrappedTouch> tempTouches = new List<WrappedTouch>(); | |
List<QueuedTouch> touchQueue = new List<QueuedTouch>(); | |
List<QueuedTouch> freeTouches = new List<QueuedTouch>(); | |
#if DEBUG_WRAPPED_INPUT | |
[SerializeField] List<string> queue; | |
[SerializeField] List<string> free; | |
#endif | |
class QueuedTouch | |
{ | |
public int count; | |
public float time; | |
public WrappedTouch touch; | |
public bool free; | |
public bool remove; | |
public QueuedTouch(WrappedTouch touch) | |
{ | |
this.time = Time.realtimeSinceStartup; | |
this.touch = touch; | |
this.count = 1; | |
} | |
public void ApplyNext(WrappedTouch touch) | |
{ | |
this.time = Time.realtimeSinceStartup; | |
this.touch = touch; | |
this.count ++; | |
} | |
} | |
/// <summary> | |
/// シングルトン | |
/// </summary> | |
static WrappedInput instance; | |
public static WrappedInput Instance | |
{ | |
get | |
{ | |
if (!instance) | |
{ | |
instance = FindObjectOfType<WrappedInput>(); | |
if (!instance) | |
{ | |
var go = new GameObject("WrappedInput"); | |
instance = go.AddComponent<WrappedInput>(); | |
} | |
} | |
return instance; | |
} | |
} | |
void Awake() | |
{ | |
instance = this; | |
Input.simulateMouseWithTouches = !simulateTouchWithMouse; | |
} | |
public static WrappedTouch[] touches | |
{ | |
get | |
{ | |
return Instance.m_touches; | |
} | |
} | |
void Update() | |
{ | |
var multiTapDistMax = multiTapInchMax * Screen.dpi; | |
tempTouches.Clear(); | |
var touches = Input.touches; | |
// マウスをタップに加える | |
WrappedTouch mouse = new WrappedTouch(); | |
int loop = touches.Length; | |
if (!Input.simulateMouseWithTouches) | |
{ | |
loop++; | |
var dt = Time.realtimeSinceStartup - lastTime; | |
var dp = (Vector2)Input.mousePosition - lastMouse; | |
if (Input.GetMouseButtonDown(0)) | |
mouse = new WrappedTouch(dp, dt, mouseFingerId, TouchPhase.Began, Input.mousePosition); | |
else if (Input.GetMouseButtonUp(0)) | |
mouse = new WrappedTouch(dp, dt, mouseFingerId, TouchPhase.Ended, Input.mousePosition); | |
else if (Input.GetMouseButton(0)) | |
mouse = new WrappedTouch(dp, dt, mouseFingerId, TouchPhase.Moved, Input.mousePosition); | |
else | |
loop--; | |
lastMouse = Input.mousePosition; | |
lastTime = Time.realtimeSinceStartup; | |
} | |
for (int i = 0; i < loop; i ++) | |
{ | |
var currentTouch = i == touches.Length ? mouse : new WrappedTouch(touches[i]); | |
// 既に数える必要が無いタップ | |
var iFree = freeTouches.FindIndex(x => x.touch.fingerId == currentTouch.fingerId); | |
if (iFree >= 0) { | |
if (currentTouch.phase == TouchPhase.Canceled | |
|| currentTouch.phase == TouchPhase.Ended) { | |
freeTouches[iFree].remove = true; | |
} | |
tempTouches.Add(new WrappedTouch(currentTouch, freeTouches[iFree].count)); | |
continue; | |
} | |
// キューに同じIDのタッチがある場合(これはおまけ) | |
var qSame = touchQueue.FindIndex(x => x.touch.phase == TouchPhase.Began && x.touch.fingerId == currentTouch.fingerId); | |
if (qSame >= 0) | |
{ | |
// キューにあるけど、範囲外 | |
if (Vector2.Distance(touchQueue[qSame].touch.position, currentTouch.position) > multiTapDistMax | |
|| Time.realtimeSinceStartup - touchQueue[qSame].time > multiTapIntervalMax) | |
{ | |
// 現在のカウントで流す | |
for (int j = 0; j < touchQueue.Count; j ++) | |
{ | |
if (touchQueue[j].touch.fingerId == currentTouch.fingerId) | |
{ | |
tempTouches.Add(new WrappedTouch(touchQueue[j].touch, touchQueue[qSame].count)); | |
touchQueue[j].remove = j != qSame; | |
} | |
} | |
touchQueue[qSame].free = true; | |
touchQueue.Add(new QueuedTouch(currentTouch)); | |
} | |
// キューにあってタップカウントが増える | |
else if (currentTouch.phase == TouchPhase.Began) | |
{ | |
// カウント0で流す | |
for (int j = 0; j < touchQueue.Count; j ++) | |
{ | |
if (touchQueue[j].touch.fingerId == currentTouch.fingerId) | |
{ | |
tempTouches.Add(new WrappedTouch(touchQueue[j].touch, 0)); | |
touchQueue[j].remove = j != qSame; | |
} | |
} | |
touchQueue[qSame].ApplyNext(currentTouch); | |
} | |
// キューにあるのでキューに詰める | |
else | |
{ | |
touchQueue.Add(new QueuedTouch(currentTouch)); | |
} | |
continue; | |
} | |
// キューに近い位置のタップがある場合 | |
var qNear = touchQueue.FindIndex( | |
x => x.touch.phase == TouchPhase.Began | |
&& Vector2.Distance(x.touch.position, currentTouch.position) < multiTapDistMax | |
&& Time.realtimeSinceStartup - x.time <= multiTapIntervalMax); | |
if (qNear >= 0) | |
{ | |
// カウント0で流す | |
for (int j = 0; j < touchQueue.Count; j ++) | |
{ | |
if (touchQueue[j].touch.fingerId == touchQueue[qNear].touch.fingerId) | |
{ | |
tempTouches.Add(new WrappedTouch(touchQueue[j].touch, 0)); | |
touchQueue[j].remove = j != qNear; | |
} | |
} | |
touchQueue[qNear].ApplyNext(currentTouch); | |
} | |
else if (currentTouch.phase == TouchPhase.Began) | |
{ | |
touchQueue.Add(new QueuedTouch(currentTouch)); | |
} | |
// 開始が記録されてないタッチが検出...されることはないはず | |
else | |
{ | |
Debug.Log("Unexpected touch: " + currentTouch); | |
} | |
} | |
// 時間切れタップ判定 | |
for (int i = 0; i < touchQueue.Count; i ++) | |
{ | |
if (!touchQueue[i].free && !touchQueue[i].remove && touchQueue[i].touch.phase == TouchPhase.Began | |
&& Time.realtimeSinceStartup - touchQueue[i].time > multiTapIntervalMax) | |
{ | |
var ended = touchQueue.FindIndex(x => x.touch.fingerId == touchQueue[i].touch.fingerId | |
&& (x.touch.phase == TouchPhase.Ended || x.touch.phase == TouchPhase.Canceled)) >= 0; | |
for (int j = 0; j < touchQueue.Count; j ++) | |
{ | |
if (touchQueue[j].touch.fingerId == touchQueue[i].touch.fingerId) | |
{ | |
tempTouches.Add(new WrappedTouch(touchQueue[j].touch, touchQueue[i].count)); | |
touchQueue[j].free = i == j && !ended; | |
touchQueue[j].remove = i != j || ended; | |
} | |
} | |
} | |
} | |
// removeフラッグで消去、freeフラッグでfreeTouchesに移動 | |
freeTouches.RemoveAll(x => x.remove); | |
for (int i = 0; i < touchQueue.Count; i ++) | |
{ | |
if (touchQueue[i].free) { | |
freeTouches.Add(touchQueue[i]); | |
} | |
} | |
touchQueue.RemoveAll(x => x.remove || x.free); | |
for (int i = 0; i < freeTouches.Count; i ++) | |
{ | |
freeTouches[i].remove = false; | |
} | |
// コピー | |
System.Array.Resize(ref m_touches, tempTouches.Count); | |
tempTouches.CopyTo(m_touches); | |
#if DEBUG_WRAPPED_INPUT | |
free = freeTouches.Select(x => x.touch.fingerId + ":" + x.touch.tapCount).ToList(); | |
queue = touchQueue.Select(x => x.touch.fingerId + ":" + x.touch.tapCount).ToList(); | |
#endif | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment