From f3d44f0551218e7bf5e9780fc2de5426325ca832 Mon Sep 17 00:00:00 2001 From: Robert Carone Date: Mon, 1 Feb 2016 19:38:03 +0000 Subject: [PATCH] as pointed out by hobsynth at #53, when many touches were queued in the touch processor, they essentially would "overwrite" themselves causing touches to be lost in the game loop; this is now corrected by checking for duplicates and repeating processing until the queue is empty --- sparrow/src/Classes/SPTouchProcessor.h | 4 +- sparrow/src/Classes/SPTouchProcessor.m | 132 +++++++++++++------------ 2 files changed, 69 insertions(+), 67 deletions(-) diff --git a/sparrow/src/Classes/SPTouchProcessor.h b/sparrow/src/Classes/SPTouchProcessor.h index eba472d9..62f51a1f 100644 --- a/sparrow/src/Classes/SPTouchProcessor.h +++ b/sparrow/src/Classes/SPTouchProcessor.h @@ -69,8 +69,8 @@ NS_ASSUME_NONNULL_BEGIN /// Called internally by "advanceTime:". To calculate updated targets, the method will call /// "hitTestPoint:" on the "root" object. /// -/// @param touches A list of all touches that have changed just now. -- (void)processTouches:(NSMutableOrderedSet *)touches; +/// @param touches A set of all touches that have changed just now. +- (void)processTouches:(NSSet *)touches; /// Enqueues a new touch. - (void)enqueueTouch:(SPTouch *)touch; diff --git a/sparrow/src/Classes/SPTouchProcessor.m b/sparrow/src/Classes/SPTouchProcessor.m index c5d9561d..45f7e691 100644 --- a/sparrow/src/Classes/SPTouchProcessor.m +++ b/sparrow/src/Classes/SPTouchProcessor.m @@ -54,8 +54,9 @@ - (instancetype)initWithStage:(SPStage *)stage _queuedTouches = [[NSMutableArray alloc] init]; _lastTaps = [[NSMutableArray alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cancelCurrentTouches) - name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(cancelCurrentTouches) + name:UIApplicationWillResignActiveNotification object:nil]; } return self; @@ -83,37 +84,48 @@ - (void)dealloc - (void)advanceTime:(double)seconds { _elapsedTime += seconds; - + if (_lastTaps.count) { NSMutableArray *remainingTaps = [NSMutableArray array]; - + for (SPTouch *touch in _lastTaps) if (_elapsedTime - touch.timestamp <= _multitapTime) [remainingTaps addObject:touch]; - + SP_RELEASE_AND_RETAIN(_lastTaps, remainingTaps); } - + if (_queuedTouches.count) { for (SPTouch *touch in _currentTouches) if (touch.phase == SPTouchPhaseBegan || touch.phase == SPTouchPhaseMoved) touch.phase = SPTouchPhaseStationary; - - for (SPTouch *touch in _queuedTouches) - [_updatedTouches addObject:[self createOrUpdateTouch:touch]]; - - [self processTouches:_updatedTouches]; - [_updatedTouches removeAllObjects]; - - NSMutableOrderedSet *remainingTouches = [NSMutableOrderedSet orderedSet]; - for (SPTouch *touch in _currentTouches) - if (touch.phase != SPTouchPhaseEnded && touch.phase != SPTouchPhaseCancelled) - [remainingTouches addObject:touch]; - - SP_RELEASE_AND_RETAIN(_currentTouches, remainingTouches); - [_queuedTouches removeAllObjects]; + + while (_queuedTouches.count) + { + NSMutableArray *excessTouches = [NSMutableArray array]; + + for (SPTouch *touch in _queuedTouches) + { + if (![_updatedTouches containsObject:touch]) + { + [self addCurrentTouch:touch]; + [_updatedTouches addObject:touch]; + } + else + { + [excessTouches addObject:touch]; + } + } + + [self processTouches:_updatedTouches.set]; + [_updatedTouches removeAllObjects]; + + SP_RELEASE_AND_RETAIN(_queuedTouches, excessTouches); + } + + [self removeEndedTouches]; } } @@ -131,11 +143,8 @@ - (NSInteger)numCurrentTouches #pragma mark Process Touches -- (void)processTouches:(NSMutableOrderedSet *)touches +- (void)processTouches:(NSSet *)touches { - // the same touch event will be dispatched to all targets - SPTouchEvent *touchEvent = [[SPTouchEvent alloc] initWithType:SPEventTypeTouch touches:_currentTouches.set]; - // hit test our updated touches for (SPTouch *touch in touches) { @@ -145,6 +154,10 @@ - (void)processTouches:(NSMutableOrderedSet *)touches touch.target = [_root hitTestPoint:touchPosition forTouch:YES]; } } + + // the same touch event will be dispatched to all targets + SPTouchEvent *touchEvent = + [[SPTouchEvent alloc] initWithType:SPEventTypeTouch touches:_currentTouches.set]; // dispatch events for the rest of our updated touches for (SPTouch *touch in touches) @@ -155,55 +168,45 @@ - (void)processTouches:(NSMutableOrderedSet *)touches - (void)cancelCurrentTouches { - double now = CACurrentMediaTime(); - // remove touches that have already ended / were already canceled - [_currentTouches filterUsingPredicate: - [NSPredicate predicateWithBlock:^ BOOL (SPTouch *touch, NSDictionary *bindings) - { - return touch.phase != SPTouchPhaseEnded && touch.phase != SPTouchPhaseCancelled; - }]]; - + [self removeEndedTouches]; + + double now = CACurrentMediaTime(); for (SPTouch *touch in _currentTouches) { touch.phase = SPTouchPhaseCancelled; touch.timestamp = now; } + + SPTouchEvent *touchEvent = [[SPTouchEvent alloc] + initWithType:SPEventTypeTouch touches:_currentTouches.set]; for (SPTouch *touch in _currentTouches) - { - SPTouchEvent *touchEvent = [[SPTouchEvent alloc] initWithType:SPEventTypeTouch - touches:_currentTouches.set]; [touch.target dispatchEvent:touchEvent]; - [touchEvent release]; - } - + + [touchEvent release]; [_currentTouches removeAllObjects]; } #pragma mark Update Touches -- (SPTouch *)createOrUpdateTouch:(SPTouch *)touch +- (void)addCurrentTouch:(SPTouch *)touch { - SPTouch *currentTouch = [self currentTouchWithID:touch.touchID]; - if (!currentTouch) + NSUInteger index = [_currentTouches indexOfObject:touch]; + + if (index != NSNotFound) { - currentTouch = [SPTouch touchWithID:touch.touchID]; - [_currentTouches addObject:currentTouch]; + SPTouch *currentTouch = _currentTouches[index]; + touch.target = currentTouch.target; // save the target + [_currentTouches replaceObjectAtIndex:index withObject:touch]; + } + else + { + [_currentTouches addObject:touch]; } - currentTouch.globalX = touch.globalX; - currentTouch.globalY = touch.globalY; - currentTouch.previousGlobalX = touch.previousGlobalX; - currentTouch.previousGlobalY = touch.previousGlobalY; - currentTouch.phase = touch.phase; - currentTouch.forceFactor = touch.forceFactor; - currentTouch.timestamp = _elapsedTime; - - if (currentTouch.phase == SPTouchPhaseBegan) - [self updateTapCount:currentTouch]; - - return currentTouch; + if (touch.phase == SPTouchPhaseBegan) + [self updateTapCount:touch]; } - (void)updateTapCount:(SPTouch *)touch @@ -213,8 +216,8 @@ - (void)updateTapCount:(SPTouch *)touch for (SPTouch *tap in _lastTaps) { - float sqDist = powf(tap.globalX - touch.globalX, 2) + - powf(tap.globalY - touch.globalY, 2); + float sqDist = powf(tap.globalX - touch.globalX, 2) + + powf(tap.globalY - touch.globalY, 2); if (sqDist <= minSqDist) nearbyTap = tap; @@ -233,15 +236,14 @@ - (void)updateTapCount:(SPTouch *)touch [_lastTaps addObject:[[touch copy] autorelease]]; } -#pragma mark Current Touches - -- (SPTouch *)currentTouchWithID:(size_t)touchID +- (void)removeEndedTouches { - for (SPTouch *touch in _currentTouches) - if (touch.touchID == touchID) - return touch; - - return nil; + [_currentTouches filterUsingPredicate: + [NSPredicate predicateWithBlock:^ BOOL (SPTouch *touch, NSDictionary *bindings) + { + return touch.phase != SPTouchPhaseEnded && + touch.phase != SPTouchPhaseCancelled; + }]]; } @end