Dieses Dokuwiki verwendet ein von Anymorphic Webdesign erstelltes Thema.

Integrating cocos2d and UIKitLayer

Firstly, I would like to say how I really appreciate the whole cocos2d develop team. Your high performance package helped me finish an impossible mission. Our cocos2d package is pretty easy to manage and code with very little bugs. Specially when we want to use some animations, it is really perfect.

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

But there's still some simple view & component that can be used for game development. Specially UITextView, UIProgressbar and so on.

I am thinking about how to merge these 2 families into 1, and let UIView family be easily managed by CCDirector.

Here is my point (I need to verify it by testing it later):

  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 is used just for child node management and its draw function need to be called in its subclasses.
  1. I have checked most subclasses of CocosNode, most of them just call the draw function and perform some kind of transformation. My conclusion is: whatever we add to a subnode in the Scene, are drawn on the _openGLView. So we could add a subview on the _openGLView to have UIView shown in the CCDirector control. What we'd have to do is:
    1. get a _openGLView pointer from CCDirector
    2. Create a subclass named UIViewNode from CocosNode, add the UIView pointer as a member variable
    3. Override -(id)init method. In this method we can create this view (pView) with rect(0,0,1,1), with no color and add this subView to _openGLView in CCDirector.
    4. Override -(void)dealloc to release this view and its subviews and subchildren. This is key to avoid memory leaks;
    5. Deprecate addChild method and only support addSubview & addViewNode (_openGLView is always in the bottom of the view, if we add another type of Node, it cannot be shown on top of this Node). Put subview & subviewnode’s view pointer to current view’s subview.
    6. Override setVisible (call subview show/hide function & subViewNode setVisible function)
    7. Override the following methods (damn…): removeChild, removeChildByTag, removeAllChildrenWithCleanup, getChildByTag, reorderChild, transform

After that, I think it is ok to show 'UIView' on Cocos2D games. I couldn't solve the following bug (take care while coding):

  • 'UIKitLayer' must be on the top of a CCNnode. Try not to code like this (otherwise you will find the 'UIViewNode' always displayed on top):
[scene addChild : [SubLayer1 node] z:0];
[scene addChild : [SubUIViewNode node] z:0];
[scene addChild : [SubLabel node] z:0];
  • I suggest to use UIKitLayer’s addSubView function and put the subview on the UIKitLayer. And take care to set the view’s region, as you can only control it manually (I am considering adding an extra function to control that)
  • UIImageView cannot be easily transformed. Beware, that gave me a lot of headache in my previous project.
  • UIKitLayer provides just a simple set of controls to let our custom view display on the game. Please try to avoid using UIKitLayer→pView to perform some function. Add some subviews instead.

Below is the final source code for the '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 some key points that we need to be aware of.

1. If we want to use landscape mode, we need to customize our View Coordinate System from the View Controller. In this situation, no need to call [Director setLandscape]. I have created a viewController view based on a custom coordinate. You can get a reference from the iPhone Sample “Which Way is Up”.

Below is a customized 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.statusBarHidden = YES;
        // 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 implement a 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. Please notice that the UIKitLayer's setFrame method is used to define the echo rect. All subview’s rects must be contained in the UIKeyLayer rect.

[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 somethings I couldn't solve.

  • The default origin coordinate of 'UIKitLayer' is upper left in landscape mode, while in the CCDirector class it is bottom left. Don't know how to solve this. Anyway we just need to convert it manually.
  • I'm too lazy to implemento View Controller (^_^).
  • A sample project is attached to our group (DemoUIKitLayer), hope it can be helpful (you need to manually import cocos2D path). Any animation action can be simply performed in this Layer, but you can only do it manually, using View Transform. Really hate to use it…
  • Some member functions need to be implemented in UIKitLayer. Will update it later if necessary, and I also could use your help and some advices.
tips/cocos2d_and_uikitlayer.txt · Last modified: 2010/12/07 19:37 by mgaldieri
Trace: tiled_maps index best_practices 0_99_0 start migrating_to_0_9 cocos2d_and_uikitlayer
Dieses Dokuwiki verwendet ein von Anymorphic Webdesign erstelltes Thema.
CC Attribution-Noncommercial-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0