Merge commit 'e37b6af9844f74e2eeb46350b48e31177aae48bc' into dryforge/T3
This commit is contained in:
@@ -60,6 +60,79 @@ namespace MagnetismHeroes.Tests
|
||||
}
|
||||
private void Drag(Vector2 start, Vector2 end) { _app.Input.Begin(start, false); _app.Input.Move(end); _app.Input.End(end); }
|
||||
private void Tap(Vector2 point) { _app.Input.Begin(point, false); _app.Input.End(point); }
|
||||
private void QueueTouch(int id, UnityEngine.InputSystem.TouchPhase phase, Vector2 position) =>
|
||||
InputSystem.QueueStateEvent(_touchscreen, new TouchState { touchId = id, phase = phase, position = position });
|
||||
|
||||
[UnityTest] public IEnumerator SameUpdateShortTapRespondsBeforeCollectionAndCountsOnceAfterCollection()
|
||||
{
|
||||
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
|
||||
var point = BallPoint(); QueueTouch(101, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(101, UnityEngine.InputSystem.TouchPhase.Ended, point);
|
||||
yield return null; yield return null;
|
||||
Assert.That(_app.Instruction, Does.Contain("먼저")); Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty);
|
||||
Drag(point, point + new Vector2(70, 20)); yield return null; Physics.SyncTransforms(); point = BallPoint();
|
||||
QueueTouch(102, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(102, UnityEngine.InputSystem.TouchPhase.Ended, point);
|
||||
yield return null; yield return null;
|
||||
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.FreePlay)); Assert.That(_store.Saved.completedStageIds, Is.EqualTo(new[] { 1 }));
|
||||
Assert.That(_app.Voice.PendingCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[UnityTest] public IEnumerator SameUpdateFastDragMovesWithoutCountingAnAlreadyCollectedFriend()
|
||||
{
|
||||
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
|
||||
var point = BallPoint(); Drag(point, point + new Vector2(50, 15)); yield return null; Physics.SyncTransforms(); point = BallPoint();
|
||||
var original = _app.Session.Board.Balls.Single().position;
|
||||
QueueTouch(111, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(111, UnityEngine.InputSystem.TouchPhase.Moved, point + new Vector2(40, 15));
|
||||
QueueTouch(111, UnityEngine.InputSystem.TouchPhase.Ended, point + new Vector2(40, 15)); yield return null; yield return null;
|
||||
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.Puzzle)); Assert.That(_app.Session.Board.CountedCount, Is.Zero);
|
||||
Assert.That(_app.Session.Board.Balls.Single().position, Is.Not.EqualTo(original));
|
||||
}
|
||||
|
||||
[UnityTest] public IEnumerator SameUpdateCanceledAndOutsideDragsPreserveCommittedState()
|
||||
{
|
||||
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms(); _app.Session.SaveCurrent();
|
||||
var point = BallPoint(); var original = _app.Session.Board.Balls.Single().position; var saved = JsonUtility.ToJson(_store.Saved);
|
||||
foreach (var phase in new[] { UnityEngine.InputSystem.TouchPhase.Canceled, UnityEngine.InputSystem.TouchPhase.Ended })
|
||||
{
|
||||
QueueTouch(121, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(121, UnityEngine.InputSystem.TouchPhase.Moved, point + new Vector2(50, 10));
|
||||
QueueTouch(121, phase, phase == UnityEngine.InputSystem.TouchPhase.Ended ? new Vector2(-1, point.y) : point + new Vector2(50, 10));
|
||||
yield return null; yield return null;
|
||||
Assert.That(_app.Session.Board.Balls.Single().position, Is.EqualTo(original)); Assert.That(_app.Session.Board.CollectedBallIds, Is.Empty);
|
||||
Assert.That(JsonUtility.ToJson(_store.Saved), Is.EqualTo(saved));
|
||||
}
|
||||
}
|
||||
|
||||
[UnityTest] public IEnumerator SameUpdateTouchBeginningOnUiNeverCountsTheFriendAtRelease()
|
||||
{
|
||||
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
|
||||
var point = BallPoint(); Drag(point, point + new Vector2(50, 15)); yield return null; Physics.SyncTransforms(); point = BallPoint();
|
||||
var button = _app.transform.Find("Interface/SafeArea/Play/HomeButton").GetComponent<RectTransform>();
|
||||
Vector2 uiPoint = button.TransformPoint(button.rect.center);
|
||||
QueueTouch(131, UnityEngine.InputSystem.TouchPhase.Began, uiPoint); QueueTouch(131, UnityEngine.InputSystem.TouchPhase.Ended, point);
|
||||
yield return null; yield return null;
|
||||
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.Puzzle)); Assert.That(_app.Session.Board.CountedCount, Is.Zero);
|
||||
}
|
||||
|
||||
[UnityTest] public IEnumerator SameUpdateSequentialShortTapsCompleteOnlyOnce()
|
||||
{
|
||||
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
|
||||
var point = BallPoint(); Drag(point, point + new Vector2(50, 15)); yield return null; Physics.SyncTransforms(); point = BallPoint();
|
||||
QueueTouch(141, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(141, UnityEngine.InputSystem.TouchPhase.Ended, point);
|
||||
QueueTouch(142, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(142, UnityEngine.InputSystem.TouchPhase.Ended, point);
|
||||
yield return null; yield return null;
|
||||
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.FreePlay)); Assert.That(_store.Saved.completedStageIds, Is.EqualTo(new[] { 1 }));
|
||||
Assert.That(_app.Voice.PendingCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[UnityTest] public IEnumerator SameUpdateConcurrentShortPinchNeverCountsTheFriend()
|
||||
{
|
||||
_touchscreen = InputSystem.AddDevice<Touchscreen>(); _app.StartStage(1); yield return null; Physics.SyncTransforms();
|
||||
var point = BallPoint(); Drag(point, point + new Vector2(50, 15)); yield return null; Physics.SyncTransforms(); point = BallPoint();
|
||||
QueueTouch(151, UnityEngine.InputSystem.TouchPhase.Began, point); QueueTouch(152, UnityEngine.InputSystem.TouchPhase.Began, point + new Vector2(80, 0));
|
||||
QueueTouch(152, UnityEngine.InputSystem.TouchPhase.Moved, point + new Vector2(160, 0)); QueueTouch(151, UnityEngine.InputSystem.TouchPhase.Ended, point);
|
||||
QueueTouch(152, UnityEngine.InputSystem.TouchPhase.Ended, point + new Vector2(160, 0)); yield return null; yield return null;
|
||||
Assert.That(_app.Session.Screen, Is.EqualTo(SessionScreen.Puzzle)); Assert.That(_app.Session.Board.CountedCount, Is.Zero);
|
||||
Assert.That(_app.Session.Camera.distance, Is.LessThan(8));
|
||||
}
|
||||
private void DragTo(Vector3 destination)
|
||||
{
|
||||
Physics.SyncTransforms(); var start = BallPoint();
|
||||
|
||||
@@ -5,6 +5,7 @@ using MagnetismHeroes.UnityRuntime.Presentation;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.EnhancedTouch;
|
||||
|
||||
namespace MagnetismHeroes.UnityRuntime.Interaction
|
||||
{
|
||||
@@ -25,6 +26,17 @@ namespace MagnetismHeroes.UnityRuntime.Interaction
|
||||
private int _fingerId = -1;
|
||||
private bool _waitForAllTouchesReleased;
|
||||
private int _pinchFirstId = -1, _pinchSecondId = -1;
|
||||
private readonly struct TouchContact
|
||||
{
|
||||
public readonly int Id;
|
||||
public readonly UnityEngine.InputSystem.TouchPhase Phase;
|
||||
public readonly Vector2 Position;
|
||||
public TouchContact(int id, UnityEngine.InputSystem.TouchPhase phase, Vector2 position)
|
||||
{ Id = id; Phase = phase; Position = position; }
|
||||
public bool InProgress => Phase == UnityEngine.InputSystem.TouchPhase.Began || Phase == UnityEngine.InputSystem.TouchPhase.Moved || Phase == UnityEngine.InputSystem.TouchPhase.Stationary;
|
||||
}
|
||||
private readonly Queue<TouchContact> _touchEvents = new Queue<TouchContact>();
|
||||
private readonly Dictionary<int, TouchContact> _touches = new Dictionary<int, TouchContact>();
|
||||
public bool AcceptInput => _app.Session != null && _app.Session.Board != null;
|
||||
|
||||
public void Begin(Vector2 point, bool uiBlocked)
|
||||
@@ -102,77 +114,99 @@ namespace MagnetismHeroes.UnityRuntime.Interaction
|
||||
|
||||
private static bool IsInside(Vector2 point) => point.x >= 0 && point.x < Screen.width && point.y >= 0 && point.y < Screen.height;
|
||||
|
||||
private static UnityEngine.InputSystem.Controls.TouchControl FindTouch(Touchscreen screen, int id)
|
||||
private TouchContact? FindTouch(int id) => _touches.TryGetValue(id, out var touch) ? touch : (TouchContact?)null;
|
||||
|
||||
private void FinishSingleTouch()
|
||||
{
|
||||
foreach (var touch in screen.touches) if (touch.touchId.ReadValue() == id) return touch;
|
||||
return null;
|
||||
var touch = FindTouch(_fingerId);
|
||||
if (!touch.HasValue || touch.Value.Phase != UnityEngine.InputSystem.TouchPhase.Ended) { Cancel(); return; }
|
||||
var point = touch.Value.Position; Move(point); End(point);
|
||||
}
|
||||
|
||||
private void FinishSingleTouch(Touchscreen screen)
|
||||
private void RecordTouch(Finger finger)
|
||||
{
|
||||
var touch = FindTouch(screen, _fingerId);
|
||||
if (touch == null || touch.phase.ReadValue() != UnityEngine.InputSystem.TouchPhase.Ended) { Cancel(); return; }
|
||||
var point = touch.position.ReadValue(); Move(point); End(point);
|
||||
if (!AcceptInput || finger.screen != Touchscreen.current) return;
|
||||
// Copy each official history callback before later events overwrite the native touch slot.
|
||||
var touch = finger.lastTouch;
|
||||
_touchEvents.Enqueue(new TouchContact(touch.touchId, touch.phase, touch.screenPosition));
|
||||
}
|
||||
|
||||
private void ProcessTouches()
|
||||
{
|
||||
var active = new List<TouchContact>();
|
||||
foreach (var touch in _touches.Values) if (touch.InProgress) active.Add(touch);
|
||||
if (_fingerId != -1 && !active.Exists(touch => touch.Id == _fingerId))
|
||||
{
|
||||
// Finish the original pointer before any new pointer can inherit its preview.
|
||||
FinishSingleTouch(); _waitForAllTouchesReleased = active.Count > 0; return;
|
||||
}
|
||||
if (_gesture == Gesture.Pinch)
|
||||
{
|
||||
Vector2 release = _last; bool ended = false, canceled = false;
|
||||
foreach (int id in new[] { _pinchFirstId, _pinchSecondId })
|
||||
{
|
||||
var touch = FindTouch(id);
|
||||
if (!touch.HasValue) { canceled = true; continue; }
|
||||
var phase = touch.Value.Phase;
|
||||
if (phase == UnityEngine.InputSystem.TouchPhase.Canceled) canceled = true;
|
||||
if (phase == UnityEngine.InputSystem.TouchPhase.Ended)
|
||||
{ release = touch.Value.Position; ended = true; canceled |= !IsInside(release); }
|
||||
}
|
||||
if (canceled || ended)
|
||||
{ End(release, canceled); _waitForAllTouchesReleased = active.Count > 0; return; }
|
||||
}
|
||||
// A canceled/finished pinch cannot restart when a third finger joins a remaining finger.
|
||||
if (_waitForAllTouchesReleased && _gesture == Gesture.None && active.Count > 0) return;
|
||||
if (active.Count >= 2)
|
||||
{
|
||||
var a = active[0].Position; var b = active[1].Position;
|
||||
if (_gesture != Gesture.Pinch && _gesture != Gesture.Blocked)
|
||||
{
|
||||
BeginPinch(Vector2.Distance(a, b), IsUI(a) || IsUI(b));
|
||||
_pinchFirstId = active[0].Id; _pinchSecondId = active[1].Id;
|
||||
}
|
||||
else Pinch(Vector2.Distance(a, b));
|
||||
_waitForAllTouchesReleased = true;
|
||||
return;
|
||||
}
|
||||
if (_waitForAllTouchesReleased)
|
||||
{
|
||||
if (active.Count == 0) { _waitForAllTouchesReleased = false; _gesture = Gesture.None; _fingerId = -1; }
|
||||
return;
|
||||
}
|
||||
if (_gesture == Gesture.Pinch || (active.Count == 0 && _gesture == Gesture.Blocked))
|
||||
{ End(_last); _fingerId = -1; return; }
|
||||
if (active.Count == 1)
|
||||
{
|
||||
var touch = active[0]; var point = touch.Position;
|
||||
if (_fingerId == -1)
|
||||
{
|
||||
if (touch.Phase != UnityEngine.InputSystem.TouchPhase.Began) { _waitForAllTouchesReleased = true; return; }
|
||||
Begin(point, IsUI(point)); _fingerId = touch.Id;
|
||||
}
|
||||
else Move(point);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!AcceptInput) return;
|
||||
var touchscreen = Touchscreen.current;
|
||||
if (touchscreen != null)
|
||||
if (!AcceptInput) { _touchEvents.Clear(); _touches.Clear(); return; }
|
||||
bool hadTouches = _touchEvents.Count > 0, suppressRestOfFrame = false;
|
||||
while (_touchEvents.Count > 0)
|
||||
{
|
||||
var active = new List<UnityEngine.InputSystem.Controls.TouchControl>();
|
||||
foreach (var touch in touchscreen.touches) if (touch.press.isPressed) active.Add(touch);
|
||||
if (_fingerId != -1 && !active.Exists(touch => touch.touchId.ReadValue() == _fingerId))
|
||||
var touch = _touchEvents.Dequeue(); _touches[touch.Id] = touch;
|
||||
if (!suppressRestOfFrame)
|
||||
{
|
||||
// Finish the original pointer before any new pointer can inherit its preview.
|
||||
FinishSingleTouch(touchscreen); _waitForAllTouchesReleased = active.Count > 0; return;
|
||||
}
|
||||
if (_gesture == Gesture.Pinch)
|
||||
{
|
||||
Vector2 release = _last; bool ended = false, canceled = false;
|
||||
foreach (int id in new[] { _pinchFirstId, _pinchSecondId })
|
||||
{
|
||||
var touch = FindTouch(touchscreen, id);
|
||||
if (touch == null) { canceled = true; continue; }
|
||||
var phase = touch.phase.ReadValue();
|
||||
if (phase == UnityEngine.InputSystem.TouchPhase.Canceled) canceled = true;
|
||||
if (phase == UnityEngine.InputSystem.TouchPhase.Ended)
|
||||
{ release = touch.position.ReadValue(); ended = true; canceled |= !IsInside(release); }
|
||||
}
|
||||
if (canceled || ended)
|
||||
{ End(release, canceled); _waitForAllTouchesReleased = active.Count > 0; return; }
|
||||
}
|
||||
// A canceled/finished pinch cannot restart when a third finger joins a remaining finger.
|
||||
if (_waitForAllTouchesReleased && _gesture == Gesture.None && active.Count > 0) return;
|
||||
if (active.Count >= 2)
|
||||
{
|
||||
var a = active[0].position.ReadValue(); var b = active[1].position.ReadValue();
|
||||
if (_gesture != Gesture.Pinch && _gesture != Gesture.Blocked)
|
||||
{
|
||||
BeginPinch(Vector2.Distance(a, b), IsUI(a) || IsUI(b));
|
||||
_pinchFirstId = active[0].touchId.ReadValue(); _pinchSecondId = active[1].touchId.ReadValue();
|
||||
}
|
||||
else Pinch(Vector2.Distance(a, b));
|
||||
_waitForAllTouchesReleased = true;
|
||||
return;
|
||||
}
|
||||
if (_waitForAllTouchesReleased)
|
||||
{
|
||||
if (active.Count == 0) { _waitForAllTouchesReleased = false; _gesture = Gesture.None; _fingerId = -1; }
|
||||
return;
|
||||
}
|
||||
if (_gesture == Gesture.Pinch || (active.Count == 0 && _gesture == Gesture.Blocked))
|
||||
{ End(_last); _fingerId = -1; return; }
|
||||
if (active.Count == 1)
|
||||
{
|
||||
var touch = active[0]; var point = touch.position.ReadValue();
|
||||
if (_fingerId == -1)
|
||||
{ Begin(point, IsUI(point)); _fingerId = touch.touchId.ReadValue(); }
|
||||
else Move(point);
|
||||
return;
|
||||
bool ownsGesture = touch.Id == _fingerId || (_gesture == Gesture.Pinch && (touch.Id == _pinchFirstId || touch.Id == _pinchSecondId));
|
||||
bool canceled = touch.Phase == UnityEngine.InputSystem.TouchPhase.Canceled ||
|
||||
(touch.Phase == UnityEngine.InputSystem.TouchPhase.Ended && !IsInside(touch.Position));
|
||||
ProcessTouches(); suppressRestOfFrame = ownsGesture && canceled;
|
||||
}
|
||||
if (!touch.InProgress) _touches.Remove(touch.Id);
|
||||
}
|
||||
if (suppressRestOfFrame) _waitForAllTouchesReleased = _touches.Count > 0;
|
||||
if (hadTouches || _touches.Count > 0) return;
|
||||
var mouse = Mouse.current;
|
||||
if (mouse == null) return;
|
||||
var position = mouse.position.ReadValue();
|
||||
@@ -182,7 +216,20 @@ namespace MagnetismHeroes.UnityRuntime.Interaction
|
||||
if (mouse.scroll.ReadValue().y != 0 && !IsUI(position))
|
||||
{ _orbit.Zoom(Mathf.Exp(mouse.scroll.ReadValue().y * 0.001f)); _app.Session.CommitCamera(_orbit.Snapshot()); }
|
||||
}
|
||||
private void OnApplicationPause(bool paused) { if (paused) Cancel(); }
|
||||
private void OnDisable() => Cancel();
|
||||
private void OnEnable()
|
||||
{
|
||||
EnhancedTouchSupport.Enable();
|
||||
UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerDown += RecordTouch;
|
||||
UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerMove += RecordTouch;
|
||||
UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerUp += RecordTouch;
|
||||
}
|
||||
private void OnApplicationPause(bool paused) { if (paused) { Cancel(); _touchEvents.Clear(); _touches.Clear(); } }
|
||||
private void OnDisable()
|
||||
{
|
||||
UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerDown -= RecordTouch;
|
||||
UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerMove -= RecordTouch;
|
||||
UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerUp -= RecordTouch;
|
||||
EnhancedTouchSupport.Disable(); Cancel(); _touchEvents.Clear(); _touches.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user