Game Development Community

dev|Pro Game Development Curriculum

iTorque2D Code Snippet: iOS gesture recognizers

by Paul Jan · 02/17/2012 (1:17 pm) · 1 comments

This code is for iTorque 1.5, and will change with 1.5.1.
*edit: (2-24-12) fixed bugs with onPan events (note: the %windowPos needs to be converted with t2dSceneWindow::getWorldPoint if you plan to interact with sceneObjects )

Add to the beginning of TGBAppDelegate.mm
#define _USE_PAN_GESTURES
#define _USE_SWIPE_GESTURES
#define _USE_PINCH_GESTURES

#if defined (RETINA_DISPLAY)
extern bool iPhone4;
extern void ConvertToIPhone4 (CGPoint *p, bool portrait);
#endif
You probably won't want to use all the gestures. So for now we use defines to pick and choose.

Add to applicationDidFinishLaunching,
When 1.5.1 comes out, this should be moved over to ViewController viewDidLoad, and the first line changed to something like UIView *view = (UIView *) self.view;.
UIView * view = [UIApplication sharedApplication].keyWindow;

#if defined(_USE_SWIPE_GESTURES)
    UISwipeGestureRecognizer *recognizer;
    
    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipes:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
    recognizer.numberOfTouchesRequired = 1;
    [ view addGestureRecognizer:recognizer];
    [recognizer release];
    
    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipes:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionUp)];
    recognizer.numberOfTouchesRequired = 1;
    [ view addGestureRecognizer:recognizer];
    [recognizer release];
    
    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipes:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionDown)];
    recognizer.numberOfTouchesRequired = 1;
    [ view addGestureRecognizer:recognizer];
    [recognizer release];
    
    recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipes:)];
    [recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
    recognizer.numberOfTouchesRequired = 1;
    [ view addGestureRecognizer:recognizer];
    [recognizer release];    
#endif // _USE_SWIPE_GESTURES    
    
#if defined (_USE_PINCH_GESTURES)
    UIPinchGestureRecognizer *pinchRecognizer;
    view.userInteractionEnabled = YES;
    
    pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinches:)];
    [ view addGestureRecognizer:pinchRecognizer];
    [ pinchRecognizer release];
    
#endif // _USE_PINCH_GESTURES
    
#if defined (_USE_PAN_GESTURES)
    UIPanGestureRecognizer *panRecognizer;
    view.userInteractionEnabled = YES;
    
    panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [ view addGestureRecognizer:panRecognizer];
    [ panRecognizer release];
    
#endif // _USE_PAN_GESTURES

And now add the recognizers to the end.
#if defined(_USE_SWIPE_GESTURES)

- (void) handleSwipes:(UISwipeGestureRecognizer*)paramSender{
    
    NSUInteger touches = paramSender.numberOfTouches;
    
    if (paramSender.direction & UISwipeGestureRecognizerDirectionDown){
        Con::executef(1, "onDownSwipe");
    }
    if (paramSender.direction & UISwipeGestureRecognizerDirectionLeft){
        Con::executef(1, "onLeftSwipe");
    }
    if (paramSender.direction & UISwipeGestureRecognizerDirectionRight){
        Con::executef(1, "onRightSwipe");
    }
    if (paramSender.direction & UISwipeGestureRecognizerDirectionUp){
        Con::executef(1, "onUpSwipe");
    }
}
#endif // _USE_SWIPE_GESTURES

#if defined(_USE_PINCH_GESTURES)
- (void) handlePinches:(UIPinchGestureRecognizer*)paramSender{
    
    NSUInteger touches = paramSender.numberOfTouches;    
    
    if (paramSender.state == UIGestureRecognizerStateEnded){
        clearPendingMultitouchEvents( ); 
        Con::executef(3, "onPinchEnded", Con::getFloatArg(paramSender.scale), Con::getFloatArg(paramSender.velocity));
    } else if (paramSender.state == UIGestureRecognizerStateBegan) {
        clearPendingMultitouchEvents( ); 
        Con::executef(3, "onPinchStarted", Con::getFloatArg(paramSender.scale), Con::getFloatArg(paramSender.velocity));
    } else if (paramSender.state == UIGestureRecognizerStateChanged) {
        clearPendingMultitouchEvents( ); 
        Con::executef(3, "onPinchChanged", Con::getFloatArg(paramSender.scale), Con::getFloatArg(paramSender.velocity));
    }
}
#endif // _USE_PINCH_GESTURES

#if defined(_USE_PAN_GESTURES)
- (void) handlePan:(UIPanGestureRecognizer*)paramSender{
    
    NSUInteger touches = paramSender.numberOfTouches;    
    
    CGPoint velocity = [paramSender velocityInView:paramSender.view];
    CGPoint translation = [paramSender translationInView:paramSender.view];
    CGPoint touchPoint1 = [paramSender locationInView:paramSender.view];
    
#if defined (RETINA_DISPLAY)
    if(iPhone4)
    {
        ConvertToIPhone4(&touchPoint1, platState.portrait);
    }
#endif

    // Argument Buffers.
    char argBuffer[3][32];
    
    dSprintf(argBuffer[0], 32, "%f %f", velocity.x, velocity.y);
    
	dSprintf(argBuffer[1], 32, "%f %f", translation.x, translation.y);
    
    dSprintf(argBuffer[2], 32, "%f %f", touchPoint1.x, touchPoint1.y);
        
    if (paramSender.state == UIGestureRecognizerStateEnded){
        clearPendingMultitouchEvents( ); //EFM
        Con::executef(4, "onPanEnded", argBuffer[0], argBuffer[1], argBuffer[2]);
    } else if (paramSender.state == UIGestureRecognizerStateBegan) {
        clearPendingMultitouchEvents( ); //EFM
        Con::executef(4, "onPanStarted", argBuffer[0], argBuffer[1], argBuffer[2]);
    } else if (paramSender.state == UIGestureRecognizerStateChanged) {
        clearPendingMultitouchEvents( ); //EFM
        Con::executef(4, "onPanChanged", argBuffer[0], argBuffer[1], argBuffer[2]);
    }
}
#endif // _USE_PAN_GESTURES

The pan and pinch recognizers allow for better and simpler camera controls than using onTouch events, for example:
// we make a t2dSceneObject called cameraMount and mount the camera onto it. 
// You'll want to use worldlimits and other physics settings to constrain the range of movement of the camera.

function cameraMount::onLevelLoaded(%this)
{
    %this.setUsesPhysics(true);
    sceneWindow2D.mount(%this, 0, 0, 0, true);
}

function onPanStarted(%velocity, %translation, %windowPos)
{
      sceneWindow2D.startZoom = sceneWindow2D.getCurrentCameraZoom();
      cameraMount.setLinearVelocity(t2dVectorScale(%velocity, sceneWindow2D.startZoom));
}

function onPanChanged(%velocity, %translation, %windowPos)
{
      cameraMount.setLinearVelocity(t2dVectorScale(%velocity, sceneWindow2D.startZoom));
}

function onPanEnded(%velocity, %translation, %windowPos)
{
      cameraMount.setLinearVelocity(t2dVectorScale(%velocity, sceneWindow2D.startZoom));
}

function onPinchStarted(%scale, %velocity)
{
      sceneWindow2D.startZoom = sceneWindow2D.getCurrentCameraZoom();
      sceneWindow2D.setCurrentCameraZoom(sceneWindow2D.startZoom * %scale);
}

function onPinchChanged(%scale, %velocity)
{
      sceneWindow2D.setCurrentCameraZoom(sceneWindow2D.startZoom * %scale);
}

function onPinchEnded(%scale, %velocity)
{
      sceneWindow2D.setCurrentCameraZoom(sceneWindow2D.startZoom * %scale);
}