Tiny Defense: Using UIScrollView in Cocos2d by Sash

So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch.

Most of my time I was working on a solution to implementing scrollviews in cocos2d. Turns out this isn’t as straight forward as I thought, but I found a tutorial over at getsetgames.com which gave me a good place to start. I have to warn you, the code in that tutorial is pretty messy. I’ve cleaned it up a lot below.

The Problem

Cocos 2d has no equivalent to UIScrollView. You can’t even use the regular UIScrollview, since it doesn’t fit into the framework. This leaves you with two possible solutions:

  1. Reverse engineer the UIScroll View
    This would take a long time to get right. Apple has put a lot of polish into making the touch interactions perfect. There are a ton of edge cases and the code is not available to copy. Ouch.
  2. Implement an invisible UIScrollView
    And use its contentOffset property to position your CCNode. Much better!

The later solution is much cleaner and has a straight forward implementation. Basically it works like this. We subclass UIScrollView, make sure it is invisible, put it on top of Cocos2d so that the user is interacting with it, then take the scroll position and apply it to the stuff in cocos2D each frame. There are a few challenges that we will have a look at, but check out the code I wrote first:

Code: CCScrollView.h

#import "cocos2d.h"

@interface CCScrollView : UIScrollView

+ (CCScrollView *) makeScrollViewWithWidth:(int)width Height:(int)height;

- (void)setWidth:(int)width Height:(int)height;
- (int) getOffset;
- (CGPoint) getOffsetAsPoint;

@end

Code: CCScrollView.m

#import "CCScrollView.h"

@implementation CCScrollView

/*
* Returns a CCScrollView Object.
* You can treat this like a regular UIScrollView, but don't add subviews. Instead
* use the methods getOffset and getOffsetAsPoint to set the Y Position of the
* CCNode you want to scroll each frame
*/
+(CCScrollView *) makeScrollViewWithWidth:(int)width Height:(int)height
{
   CCScrollView * scrollView =
     [[CCScrollView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
   scrollView.contentSize = CGSizeMake(width, height);
   scrollView.showsHorizontalScrollIndicator = NO;
   scrollView.showsVerticalScrollIndicator = NO;
   [scrollView setUserInteractionEnabled:TRUE];
   [scrollView setScrollEnabled:TRUE];

   [[[CCDirector sharedDirector] openGLView] addSubview:scrollView];

   return scrollView;
}

/*
* Change the size of the scrollable area
*/
- (void)setWidth:(int)width Height:(int)height
{
   self.contentSize = CGSizeMake(width, height);
}

/*
* Returns the offset of the scrollview as a point
*/
- (CGPoint) getOffsetAsPoint
{
   CGPoint offset = [self contentOffset];
   offset = [[CCDirector sharedDirector] convertToGL: offset];
   offset.y *= -1;
   offset.x *= -1;

   return offset;
}

/*
* Returns the Y co-ordinate of the offset of the ScrollView
*/
- (int) getOffset
{
   CGPoint offset = [self getOffsetAsPoint];
   return offset.y;
}

- (void) dealloc
{
   [self removeFromSuperview];
   [super dealloc];
}

/*
* Override touch functions
* This allows Cocos2d to process touches
*/
-(void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
   if (!self.dragging)
      [[[CCDirector sharedDirector] openGLView] touchesBegan:touches withEvent:event];

   [super touchesBegan: touches withEvent: event];
}

-(void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
   if (!self.dragging)
      [[[CCDirector sharedDirector] openGLView] touchesEnded:touches withEvent:event];

   [super touchesEnded: touches withEvent: event];
}
@end

Code: Implementation.m

//
//  Implementation.m
//  TinyDefense
//
//  Created by Sasha MacKinnon on 21/09/11.
//  Copyright 2011 Bit Battalion. All rights reserved.
//

#import "Implementation.h"

@implementation Implementation

+ (CCScene *) scene
{
    CCScene * s = [CCScene node];
    Implementation * l = [Implementation node];
    [s addChild:l];

    return s;
}

- (id)init
{
    if (self = [super init])
    {
        gameNode = [[CCNode alloc] init];

        //add the background to the game
        background = [CCSprite spriteWithFile:@"background.png"];
        background.position = ccp(160, 480);
        [gameNode addChild:background];
        [self addChild: gameNode];

        //set up scrolling
        scrollView = [CCScrollView makeScrollViewWithWidth:320 Height:960];

        [self schedule:@selector(enterFrame:)];
    }

    return self;
}

- (void) enterFrame: (ccTime) dt
{
    gameNode.position = ccp(0, [scrollView getOffset]);
}
@end

Setting up the UIScrollView

We want an easy way to create scroll views without having to go through the hassle of manually allocing and setting properties every time w need a new one. The makeScrollViewWithWidth method does just this. It:

  • creates the scroll view,
  • sets its content size to the arguments
  • adds itself to the OpenGLView, so it appears on the screen
+(CCScrollView *) makeScrollViewWithWidth:(int)width Height:(int)height
{
    CCScrollView * scrollView =
       [[CCScrollView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
    scrollView.contentSize = CGSizeMake(width, height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    [scrollView setUserInteractionEnabled:TRUE];
    [scrollView setScrollEnabled:TRUE];

    [[[CCDirector sharedDirector] openGLView] addSubview:scrollView];

    return scrollView;
}

The contentOffset Property

Next we need is to be able to access the position of our ScrollView after the user has scrolled. Since CCScrollView is a subclass of UIScrollView, we could access the “contentOffset” property directly, but we need to do a few transformations on that before hand to make it work the way we want. I made getter methods to make life easier:

- (CGPoint) getOffsetAsPoint
{
    CGPoint offset = [self contentOffset];
    offset = [[CCDirector sharedDirector] convertToGL: offset];
    offset.y *= -1;
    offset.x *= -1;

    return offset;
}

- (int) getOffset
{
    CGPoint offset = [self getOffsetAsPoint];
    return offset.y;
}

Making sure touches are registered by cocos2D

Right now we can have elements scrolling on the screen, but we can’t touch any of them. This is because CCScrollView is sitting on top of cocos2d, so the touches stop being processed before they hit cocos2D. To solve this we override touchBegan and touchEnd, and pass the touches down to cocos2D

-(void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
    if (!self.dragging)
        [[[CCDirector sharedDirector] openGLView] touchesBegan:touches withEvent:event];
    [super touchesBegan: touches withEvent: event];
}

-(void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
    if (!self.dragging)
        [[[CCDirector sharedDirector] openGLView] touchesEnded:touches withEvent:event];
    [super touchesEnded: touches withEvent: event];
}

Fixing the content offset bug

But it STILL won’t work. Cocos2D, for some weird reason, isn’t prepared to deal with the touches we get from UIScrollView. It offsets the touches by a small margin when you scroll down far enough. I found a great solution online, which requires a quick change to CCNode.m

http://stackoverflow.com/questions/2457380/uiscrollview-and-cocos2d

Basically replace these functions in CCNode.m:

- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch {
    // cocos2d 1.0 rc bug when using with additional overlayed views (such as UIScrollView).
    // CGPoint point = [touch locationInView: [touch view]];
    CGPoint point = [touch locationInView: [[CCDirector sharedDirector] openGLView]];
    point = [[CCDirector sharedDirector] convertToGL: point];
    return [self convertToNodeSpace:point];
}

- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch {
    // cocos2d 1.0 rc bug when using with additional overlayed views (such as UIScrollView).
    // CGPoint point = [touch locationInView: [touch view]];
    CGPoint point = [touch locationInView: [[CCDirector sharedDirector] openGLView]];
    point = [[CCDirector sharedDirector] convertToGL: point];
    return [self convertToNodeSpaceAR:point];
}

and CCMenu.m

-(CCMenuItem *) itemForTouch: (UITouch *) touch {
    // cocos2d 1.0 rc bug when using with additional overlayed views (such as UIScrollView).
    // CGPoint touchLocation = [touch locationInView: [touch view]];
    CGPoint touchLocation = [touch locationInView: [[CCDirector sharedDirector] openGLView]];
    touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
    ...

And now it’s ready to go!! The code is here in full, so feel free to copy paste these classes.

Fixing it for iOS 5

After all this, it might still not work with iOS 5. Instead of explaining why, I’ll point you to a stack overflow answer that has the solution:

Animation in OpenGL ES view freezes when UIScrollView is dragged on the iPhone

Whats Next

I’ve done a ton of work since I made this video which I’ll post in a few days. Tonight, I’m travelling back to Australia! I’m hoping to get a bit of code written on the plane before my battery dies. Wish me luck!


Discussion

  1. Lemon linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  2. bangal ka jadu linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  3. driving tips linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  4. Master karate gi linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  5. dick linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  6. Vincent Simmons linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  7. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  8. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  9. couples games linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  10. Massager linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  11. adam and eve sex toys linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  12. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  13. strap on dildo linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  14. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  15. adammale linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  16. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  17. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  18. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  19. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  20. dildos linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  21. floor protection film linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  22. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  23. discount sex toy linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  24. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  25. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  26. magic wand massager linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  27. dildos linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  28. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  29. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  30. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  31. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  32. jee juh linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  33. Cash Money Records linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  34. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  35. Sex toys linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  36. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  37. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  38. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  39. bondage play linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  40. تعمیر یخچال linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  41. vibrating strapon linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  42. gay sex toy review linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  43. marble linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  44. Safe Seal Sealcoating linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  45. TV/Film Music linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  46. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  47. [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  48. resell rights ebooks linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  49. Peristaltic Pump linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

  50. military manuals linked here

    [... So as you know that I spent the weekend trying to get xcode, objective c and cocos2d to be my bitch. Most of my time I was working on a solution ...]

Leave a Comment