※キャプションと訳文は仕様です。

cocos2dとUIKitの統合

はじめに、私はcocos2dの開発チームの皆さんに本当に感謝を申し上げたい。あなた方の高いパフォーマンスを誇るパッケージは私には不可能なことを可能にさせました。私達のcocos2dで作成されたパッケージは管理とコーディングがとても簡単で、バグもほとんどありませんでした。特にいくつかのアニメーションを扱いたいときに非常に最適でした。

私はUIViewファミリーの代わりとなるメインの開発プラットフォームとしてcocos2dを使いたいのですが、ひとつ問題がありました。UIKitは手動によってのみ管理され、cocos2dファミリーでそれを管理することができなかったのです。

しかし、いくつかの簡単なビューやコンポーネントはゲーム開発に使用することが出来ます。特に、UITextViewやUIProgressbarなどがあります。

私はどのようにしてこれら2つのファミリーを統合させるのか、ディレクターによってUIViewファミリーを簡単に管理することができるのか考えています。

ここに後のテストで確認するために必要となるポイントをいくつかあげます:

  1. ディレクターはビュー(_openGLView)をベースにして作られています。Director.mのファンクションを確認してください。
     
    -(BOOL)initOpenGLViewWithView:(UIView *)view withFrame: (CGRect)rect
     
  2. CocosNodeは子要素のノードを管理するために用いられ、drawファンクションは実装されたサブクラスに必要になります。
  3. 私はCocosNodeのサブクラスのほとんどを確認してきましたが、それらのほぼ全てにdrawファンクションが実装されていました。またいくつかのトランスフォームでも同様に実装されていました。私の結論としては、シーンにサブノードを追加すると、それらの全てが_openGLView上で描画されると言うことです。そのためopenGLViewにサブビューを追加することでディレクターの制御でUIViewの表示を実現させることが出来ます。実現に必要な操作は次のとおりです:
    1. ディレクターから_openGLViewのポインタを取得する.
    2. CocosNodeからUIViewNodeで名付けたサブクラスを作成して、メンバ変数としてUIViewのポインタを追加する.
    3. -(id) initファンクションを上書きして、そのファンクションの中で色なし、rect(0,0,1,1)の方形のビュー(pView)を作成し、_openGLViewにサブビューとして追加する.
    4. -(void) deallocファンクションを上書きして、メモリリークを回避するために、このビューとサブビュー、サブヒルドを開放する.
    5. 非推奨なaddChildファンクションで、addSubviewとaddViewNodeのみをサポートして(_openGLViewは常にビューの下に存在し、他の種類のノードを追加する場合、このノードの上に表示することが出来ない)、現在のビューのサブビューに、サブビューとサブビューノードのビューのポインターを置く。
    6. setVisibleファンクションを上書きする(サブビューがshow/hideファンクションを呼び出して、subViewNodeがsetVisibleファンクションを呼び出す)
    7. 最低でも以下のファンクションを上書きする.(removeChild removeChildByTag removeAllChildrenWithCleanup getChildByTag reorderChild transform)

この後でなら、cocos2dのゲーム上でUIViewを表示しても問題ないだろうと思います。以下のバグは解決されないため、コーディング中に注意する必要があります。

  • UIKitLayerは必ずノートのトップに位置しなければならず、次のようなコードを試してはなりません。それ以外の場合では、UIViewNodeは常にトップに表示されます。
     
    [scene addChild : [SubLayer1 node] z:0];
    [scene addChild : [SubUIViewNode node] z:0];
    [scene addChild : [SubLabel node] z:0];
     
  • UIKitLayerにサブビューを追加するときは、UIKitLayerのaddSubViewファンクションの使用を推奨します。ビューの領域を設定する際には、手動でのみしか御できない点に気をつけてください。(私は制御用の昨日の追加を検討しています)
  • UIImageViewは簡単に変形することが出来ないので気をつけてください。私の以前のプロジェクトではこの欠点はとても痛いです…。
  • UIKitLayerは、ゲーム上で利用者がビューを表示するためのシンプルなファミリーコントロールを提供しています。UIKitLayerの使用を避け、実装されたファンクションのためにpViewを使用するように努めてください。

下記に記すのがUIKitLayerクラスのためのソースコードです。

 
@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
 

下記に示す注意点があります。

  1. もしもランドスケープモードを使用したい場合、ビューコントローラによってビューの座標システムを手動で修正する必要があります。このような状況においては、[Director setLandscape]をコールする必要はありません。私は座標を修正してビューを基にしたviewControllerを作成してきました。iPhoneサンプルから参照することができるでしょう。

    applicationDidFinishLaunchingファンクションを以下のように修正します。
     
    // 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];
            // アプリを起動した後にカスタマイズするオーバーライドポイント
            [window addSubview: viewController.view ];
            // FPSを表示する
            [[Director sharedDirector] setDisplayFPS:YES];
            // アニメーションのフレームパーセック
            [[Director sharedDirector] setAnimationInterval:1.0/60];
            // cocos2dをウインドウにアタッチする。
        // ランドスケープモードでゲームをプレイする場合、attachInWindowを呼び出してはならない 
            // また、[Director setLandscape] ファンクションも同様。
            [[Director sharedDirector] attachInView: viewController.view withFrame: CGRectMake(0,0,480,320)];
            [[Director sharedDirector] runWithScene: [MenuScene node]]; 
     
    } 
     
    -(void) dealloc{
            [viewController release];
            [window release];
            [super dealloc]; 
     
    } 
     
    // 呼び出されたらゲームを一時停止する
    -(void) applicationWillResignActive:(UIApplication *)application
    {
            [[Director sharedDirector] pause]; 
     
    } 
     
    // リジェクトされたら呼び出す
    -(void) applicationDidBecomeActive:(UIApplication *)application
    {
            [[Director sharedDirector] resume]; 
     
    } 
     
    // メモリを解放する
    - (void)applicationDidReceiveMemoryWarning:(UIApplication *)
    application {
            [[TextureMgr sharedTextureMgr] removeAllTextures]; 
     
    } 
     
    // 次のデルタタイムを0秒にする
    -(void) applicationSignificantTimeChange:(UIApplication *)application
    {
            [[Director sharedDirector] setNextDeltaTimeZero:YES]; 
     
    } 
     
    @end 
     
    @implementation CocosDetectiveViewController 
     
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)
    interfaceOrientation { 
     
        // iPhoneをランドスケープモードに戻す
        return interfaceOrientation == UIInterfaceOrientationLandscapeRight; 
     
    } 
     
    @end
     
  2. UIkitLayerのサブクラスを実装する方法
     
    // メモリリークを回避するためにdealloc中でサブビューを手動で解放する必要がある 
     
    @interface AboutScene : Scene{
    } 
     
    @end 
     
    @interface AboutLayer : UIKitLayer{
            UITextView *contentView;
    } 
     
    @end 
     
    @implementation AboutScene 
     
    - (id) init
    {
        self = [super init];
        // イメージを生成する
        Sprite * bg = [Sprite spriteWithFile:@"liuqianru.png"];
        [bg setPosition:cpv(100, 160)];
        [self addChild:bg z:0]; 
     
        // ボタンを生成する
        [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)]; 
     
        // タイトルを生成する
        Label * title = [Label labelWithString : @"About" fontName : @"Helvetica" fontSize : 20];
        [title setRGB: 0xFF : 0xFF :0x00];
        [self addChild : title];
        [title setPosition: cpv(240,300)];
     
        // コンテンツを生成する
        [self addChild : [AboutLayer node]]; 
     
        return self; 
     
    } 
     
    -(void) tapBack : (id) sender
    {
            [[Director sharedDirector]replaceScene:[MenuScene node]]; 
     
    } 
     
    @end 
     
    @implementation AboutLayer
    -(id) init
    {
            self = [super init]; 
     
            // 非常に重要、 サブビューと同サイズかそれ以上の大きさに設定する必要がある
            [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
    {
            // サブビューを手動で解放する
            [contentView removeFromSuperview];
            [contentView release];
            [super dealloc];
    } 
     
    @end
     
  3. UIKitLayerのsetFrameファンクションがecho rectを定義するのに使用されている点に注目してください。全てのサブビューのrectはUIKitLayerのrectに必ず含まれなければなりません。私達は2回チェックするために[self setBackgroundColor:[UIColor greenColor]]ファンクションを使用できますが、それ以外の場合UIKitLayerではタッチ/ タップアクションをキャッチすることは出来ません。
     
    @implementation AboutLayer
    -(id) init
    {
            self = [super init];
            // このアクションは非常に重要で、これを忘れていたために、
            // システムがスクロールアクションを返さない根本的な原因を見つけるのに私は長い時間を費やしたものだ
            [self setFrame: CGRectMake(0, 0, 280, 260)];
            UITextView *view =  [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 280, 260)];
    }
     

まだ私が解決できない問題もいくつかあります。

  • UIKitLayerの原点座標はデフォルトではランドスケープモードの左上になっていて、これはデフォルトで左下のディレクタークラスとは異なっています。この問題の解決方法を知る術はなく、今はとにかく手動で座標を変換する必要があるのです。
  • ビューコントローラを実装すると遅延が大きくなります
  • デモのサンプルプロジェクトを私達のグループにアタッチするととても便利です。(手動でcocos2dのパスをインポートする必要がある) DemoUIKitLyaerでは、このレイヤーによっていくつかのアニメーションアクションが簡単に実現されます。ビューの変形については手動でのみ行うことが出来ますが、本当に扱いづらいです…。
  • UIKitLyaerでいくつかのメンバファンクションを実装する必要があり、それらは必要に応じて更新されます。また、私のいくつかのアドバイスであなたを手助けする必要があります。

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-10-24 (日) 17:14:26 (2586d)