Feel free to fix/add documentation to the wiki

Integrating cocos2d and UIKitLayer

Firstly, I would like to say really very appreciate the whole cocos2d develop team. Your high performance package help me to finish an impossible mission. Our cocos2d package is pretty much easier to manage and coding, very little bugs. especially when we want to use some animation, it is really very perfect.

I want to use cocos2d lib as my root develop platform instead of UIView family. But I met a problem here is the UIKit can not be managed by CocosNode family, only can be managed manually.

But there still some simple view & compenent can be used the game development. especially UITextView, UIProgressbar and soso.

I am thinking how to merge these 2 family into 1? let UIView family easily managed by Director?

Here is my point need to verify by later testing.

  1. Director is created based on a View (_openGLView). please check the function in the Director.m
-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame: (CGRect)rect
  1. CocosNode just used for child node management and it is draw function need to realized in its sub classes.
  2. I have check most subclass of CocosNode, most of them just realize the draw function. and some transform.My conclusion is whatever we add sub node in the Scene, all of them are drawn on the _openGLView. so we could add subview on the _openGLView to realize UIView shown on in the Director control. What action need us to do is :
    1. get _openGLView pointer from Director
    2. Create a subclass named UIViewNode from CocosNode, add UIView pointer as member variable,
    3. Overwrite -(id)init function, in this function we can create this view (pView) with rect(0,0,1,1), with no color, add this subView to _openGLView in director.
    4. Overwrite -(void) dealloc release this view and its subviews & sub hild, it is key to avoid memory leak;
    5. Deprecate addChild function, only support add addSubview & addViewNode (_openGLView is always in the bottom of the view, if we add other type of Node, it can not be shown on top of this Node), put subview & subviewnode’s view pointer to current view’ subview.
    6. Overwrite setVisible(call subview show/hide function & subViewNode setVisible function)
    7. Overwrite below function, (Damn …..) removeChild removeChildByTag removeAllChildrenWithCleanup getChildByTag reorderChild transform

After that, I think it is ok to show UIView on the Cocos2D games. Below bug can not be solved, need to take care while coding.

  • UIKitLayer must be on the top of node, try not code like this; otherwise you will find the UIViewNode always display on the top.
[scene addChild : [SubLayer1 node] z:0];
[scene addChild : [SubUIViewNode node] z:0];
[scene addChild : [SubLabel node] z:0];
  • Suggest to use UIKitLayer’s addSubView function and put the subview on the UIKitLayer. and take care to set the view’s region, only can control manually ( I am consider to add extra function to control)
  • UIImageView can not be easily transformed, take care, it hurts me a lot in my previous project.
  • UIKitLayer just provide a simple family control to let our customer view display on the game, pls try to avoid using UIKitLayer→pView to realized some function, pls add subviews to realize.

Below is the final source code for UIKitLayer class.

 
@interface UIKitLayer : CocosNode {
        UIView * pView;
} 
 
-(void) addChild : (UIView *) subView;
-(void) setFrame : (CGRect) frame;
-(void) setBackgroundColor : (UIColor *)color;
@end 
 
@implementation UIKitLayer
-(id) init
{
        self = [super init];
        pView = [[UIView alloc]initWithFrame : CGRectMake(0,0,1,1)];
        [pView setBackgroundColor: [UIColor clearColor]];
        [pView setCenter: cpv(0,0)];
        [super setPosition: cpv(0,0)]; 
 
        [[[Director sharedDirector] openGLView]  addSubview:pView]; 
 
        NSLog(@"pView inserted");
        return self;
} 
 
-(void) dealloc
{ 
 
        if(pView)
        {
                NSLog(@"pView deallocing");
                [pView removeFromSuperview];
                [pView release];
        }
        [super dealloc];
} 
 
-(void) addChild : (UIView *) subView
{
        NSAssert(pView,@"subView must be valid");
        [pView addSubview: subView];
} 
 
-(id) addChild: (CocosNode*)node z:(int)z
{
        NSAssert(false,@"This function is not allowed!");
        return nil;
} 
 
-(id) addChild: (CocosNode*)node z:(int)z tag:(int)tag
{
        NSAssert(false,@"This function is not allowed!");
        return nil;
} 
 
-(id) addChild: (CocosNode*)node z:(int)z parallaxRatio:(cpVect)c
{
        NSAssert(false,@"This function is not allowed!");
        return nil;
} 
 
-(void) insertChild:(CocosNode*) child z:(int)z
{
        NSAssert(false,@"This function is not allowed!");
} 
 
-(void) reorderChild:(CocosNode*) child z:(int)z
{
        NSAssert(false,@"This function is not allowed!");
} 
 
-(void) setVisible : (bool) agv
{
        super.visible = agv;
        pView.hidden = !agv;
} 
 
-(void) setPosition : (CGPoint) point
{
        [super setPosition: point];
        [pView setCenter: point];
} 
 
-(void) setFrame : (CGRect) frame
{
        [pView setFrame: frame];
} 
 
-(void) setBackgroundColor : (UIColor *)color
{
        [pView setBackgroundColor: color];
} 
 
@end

There are below key point need to take care

1. If we want to use landscape mode, need manually revise our View Coordinate System by View Controller. In this situation, no need to call [Director setLandscape] ; I have created viewController.view based on revised Coordinate. you can get reference from Iphone Sample [Which Way is Up].

Below is the revised applicationDidFinishLaunching.

 
//auto load viewController & window in MainWindow.XIB
@interface CocosDetectiveViewController : UIViewController { 
 
} 
 
@end
@interface CocosDetectiveAppDelegate : NSObject  {
        CocosDetectiveViewController * viewController;
        UIWindow *window; 
 
} 
 
@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) CocosDetectiveViewController
*viewController;
@implementation CocosDetectiveAppDelegate
@synthesize window;
@synthesize viewController; 
 
- (void)applicationDidFinishLaunching:(UIApplication *)app{ 
 
        [app setStatusBarHidden:YES animated:NO];
        // Override point for customization after app launch
        [window addSubview: viewController.view ];
        // show FPS
        [[Director sharedDirector] setDisplayFPS:YES];
        // frames per second
        [[Director sharedDirector] setAnimationInterval:1.0/60];
        // attach cocos2d to a window, if we need play game under landscape mode,don't call attachInWindow 
 
        // and no need to call [Director setLandscape] function anymore
        [[Director sharedDirector] attachInView: viewController.view withFrame: CGRectMake(0,0,480,320)];
        [[Director sharedDirector] runWithScene: [MenuScene node]]; 
 
} 
 
-(void) dealloc{
        [viewController release];
        [window release];
        [super dealloc]; 
 
} 
 
// getting a call, pause the game
-(void) applicationWillResignActive:(UIApplication *)application
{
        [[Director sharedDirector] pause]; 
 
} 
 
// call got rejected
-(void) applicationDidBecomeActive:(UIApplication *)application
{
        [[Director sharedDirector] resume]; 
 
} 
 
// purge memroy
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)
application {
        [[TextureMgr sharedTextureMgr] removeAllTextures]; 
 
} 
 
// next delta time will be zero
-(void) applicationSignificantTimeChange:(UIApplication *)application
{
        [[Director sharedDirector] setNextDeltaTimeZero:YES]; 
 
} 
 
@end 
 
@implementation CocosDetectiveViewController 
 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)
interfaceOrientation { 
 
    //turn iphone to landscape mode
    return interfaceOrientation == UIInterfaceOrientationLandscapeRight; 
 
} 
 
@end

2. How to do implementation of UIKitLayer’s subclass.

// subView need  be manually released while dealloc to avoid memory leak. 
 
@interface AboutScene : Scene{
} 
 
@end 
 
@interface AboutLayer : UIKitLayer{
        UITextView *contentView;
} 
 
@end 
 
@implementation AboutScene 
 
- (id) init
{
    self = [super init];
    //create images
    Sprite * bg = [Sprite spriteWithFile:@"liuqianru.png"];
    [bg setPosition:cpv(100, 160)];
    [self addChild:bg z:0]; 
 
        //create button
        [MenuItemFont setFontSize:24];
    [MenuItemFont setFontName:@"Helvetica"];
    MenuItem *back  = [MenuItemFont itemFromString:@"Back" target:self selector:@selector(tapBack:)]; 
 
    Menu *menu = [Menu menuWithItems:back, nil];
    [menu alignItemsVertically];
    [self addChild:menu];
        [back setPosition: cpv(100,20)];
        [menu setPosition: cpv(0,0)]; 
 
        //create title
        Label * title = [Label labelWithString : @"About" fontName : @"Helvetica" fontSize : 20];
        [title setRGB: 0xFF : 0xFF :0x00];
        [self addChild : title];
        [title setPosition: cpv(240,300)];
        //create content
    [self addChild : [AboutLayer node]]; 
 
        return self; 
 
} 
 
-(void) tapBack : (id) sender
{
        [[Director sharedDirector]replaceScene:[MenuScene node]]; 
 
} 
 
@end 
 
@implementation AboutLayer
-(id) init
{
        self = [super init]; 
 
        //very important, need set same size or larger than subviews.
        [self setFrame: CGRectMake(0, 0, 280, 260)];
        [self setPosition: cpv(320,170)]; 
 
        UITextView *view =  [[UITextView alloc] initWithFrame:CGRectMake(0,0, 280, 260)]; 
 
        view.font = [UIFont systemFontOfSize:12.0]; 
 
        view.textColor = [UIColor whiteColor];
        view.backgroundColor = [UIColor clearColor];
        view.textAlignment = UITextAlignmentLeft;
        view.editable = FALSE;
        view.text = NSLocalizedStringFromTable(@"AboutContent",@"resource",nil);
        [self addChild: view];
        contentView = view;
        return self; 
 
} 
 
-(void) dealloc
{
        //need manual release subviews
        [contentView removeFromSuperview];
        [contentView release];
        [super dealloc];
} 
 
@end

3. Pls notice, UIKitLayer setFrame function is used to define the echo rect, all subview’s rect must be contained in the UIKeyLayer rect, we can use

[self setBackgroundColor:[UIColor greenColor]] to double check, otherwise UIKit tap/touch action can not be caught by UIKitLayer. 
 
@implementation AboutLayer
-(id) init
{
        self = [super init];
        //This action is very important, and also spent me very long time 
 
        //to find the root cause why system can not receive scroll action.
        [self setFrame: CGRectMake(0, 0, 280, 260)];
        UITextView *view =  [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 280, 260)];
}

There are still something remain I can not solve.

  • The default origin coordinate of UIKitLayer is left up in the landscape mode, which is slip with [Director class which is left down. Don't know how to solve this problem, anyway now just need to convert coordinate manually.
  • View Controller and lazy to implement (^_^) .
  • A demo sample project attached in our group, hope it can be helpfull (need manually import cocos2D path). [DemoUIKitLyaer]Any animation action can be simply realized for this Layer, only can do it manually, using View Transform, Really hate to use
  • Some member functions need to implement in UIKitLayer, will update later if necessary, and also need you help to give me some advice.