手把手教你用cocos2d开发iphone游戏-译文2.4 深入学习HelloWorld 续

来源:互联网 发布:配电箱尺寸计算软件 编辑:程序博客网 时间:2024/05/30 13:43

step 2.AppDelegate

 

每个 iOS 程序都有一个 AppDelegate 类用于实现 UIApplicationDelegate 协议 (protocol)。这 个命名规则对每个新项目都是一样的:项目名加上 AppDelegate。但是我发现,在cocos2d1.0.0版本之后,凡是新创建的项目都自动省略了HelloWorld或项目的名称,因此,在我们的 HelloWorld 项目中叫做 AppDelegate。当然,如果你是用xcode的标准模板来创建项目,应该还是HelloWorldAppDelegate。

这一点让人恼火,好在不影响实际的项目运行。

 

AppDelegate 通过在某些时间点从 iOS 接收信息来跟踪程序的状态变化。例如, 你可以用它确认是否有打进来的电话,或者可用的系统内存已经不够用。程序 开始运行后收到的第一个信息是 applicationDidFinishLaunching,在这个方 法中,你可以放置启动代码,cocos2d 就是在这里初始化的。

如果你想学习更多 AppDelegate 相关的方法,怎样使用它们和 iPhone SDK 在什 么时候发送信息,你可以参考苹果官方文档中的 UIApplicationDelegate 协议:http://developer.apple.com/iphone/library/documentation/uikit/reference/UIApplicationDelegate_Protocol

在Xcode中,打开AppDelegate.m文件(译注:有的教程用HelloWorldAppDelegate.m,有的用CCHelloWorldAppDelegate.m,我相信只是版本不同导致文件名不同的问题,里面的内容应该没有神马不同)。

 

注:既然我们现在讲的是程序启动,我也顺便讲一下程序关闭。你会注意到AppDelegate 中 dealloc 方法存在一个奇怪的地方:这个方法不会被调用!任何在 AppDelegate 的 dealloc 方法中设置的断点都不起作用!这是正常的。当iOS 关闭一个程序时,它只是简单的把内存清空,以加快关闭的速度。这也是为什么 AppDelegate 的 dealloc 方法中的任何代码都不会被运行。你并不需要手动调用 dealloc 方法以“解决这个问题”。如果你确实需要在程序关闭之前在 AppDelegate 中运行代码,你可以在 applicationWillTerminate 方法中运行11

代码。如果你的目标 iOS 是 4 或者更高的版本,你应该使用applicationDidEnterBackground。

 

现在让我们进入这个类的代码细节:

 

切换到applicationDidFinishLaunching这个方法。先说一下在xcode4中切换方法的技巧。


如图,在最右侧的No Selection处左键单击,就可以在不同方法间跳转。这个跟xcode3.2.6是不同的,不过熟悉了一样方便。

 

applicationDidFinishLaunching这个方法会对CCDirector这个单例进行设置和实例化。在方法的最后一行代码,你会看到

 

    [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];

 

导演决定让HelloWorld这个场景启动和运行,然后调用场景中已经提前安排好的事情(通常在init这个方法中)

更改动画间隔

动画间隔决定了cocos2d更新屏幕的频率。此设置影响游戏可以达到的最大帧率。不过,动画间隔并不等于多少帧每秒。正相反,它表示的是cocos2d更新屏幕的 频率。这就是为什么它的参数是1.0/60 – 因为1除以60等于0.0167秒,这是在 两次屏幕更新之间的时间间隔。当然,如果你的游戏很复杂,CPU或者GPU将需 要更多的时间来显示下一帧,你的游戏就不可能保证从始至终都达到60帧每秒 的帧率。实际上,保持游戏运行在一个高的帧率上是你的责任。贯穿本书,我 将为你介绍各种提高游戏运行性能的技术。

在某些时候,把帧率设为30帧每秒可能更合适。对于那些很复杂的游戏,它们 的帧率可能在30和60帧每秒之间上下浮动的很厉害,设置一个低一点的帧率会对这样的游戏有所帮助。当你设置了一个低一点的帧率,而且游戏可以稳定的 保持在这个帧率,用户的体验会比使用一个高一些但是不稳定的帧率要好很多。

人的感觉是很复杂的东西。

注:iOS设备不支持超过60帧每秒的帧率,它的屏幕刷新率被锁定在60帧每秒

(Hz)。如果强迫cocos2d以超过60帧每秒的速度进行渲染,在最好的情况下,

可能会不产生任何效果。在最差的情况下,你的帧率可能反而会下降。如果你

想让cocos2d运行在最快的帧率下,把动画间隔设置为1.0/60。

 

对应代码:

[[CCDirector sharedDirector] setAnimationInterval:1.0/60];

 

显示帧每秒(FPS) 启用 FPS 显示后,在屏幕的左下角将会显示一个小数字。这是你程序的运行帧率,也就是每秒屏幕会被刷新的帧数。理想状态下,你的游戏应该运行在 60 帧 每秒的帧率,特别是那些动作游戏。有一些游戏,比如大多数的益智游戏,30 帧每秒就可以满足要求。FPS 显示可以帮助你跟踪当前的帧率和任何可能的问 题。

 

对应代码:

[[CCDirector sharedDirector] setDisplayFPS:YES];


下面是对applicationDidFinishLaunching中代码更详细的解释,适合喜欢钻研的新手看:

 

译注:时间关系,对这里的代码解释还不够详细,后续会把这部分内容再完善一下。

 

- (void) applicationDidFinishLaunching:(UIApplication*)application

{

         // 初始化程序运行的窗口,其中绑定了View Controller和EAGLView。UIWindow将//被设置为全屏,而EAGLView中将处理所有的OpenGL ES调用。

         window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

        

         // Try to use CADisplayLink director

//默认使用kCCDirectorTypeDisplayLink,具体细节无需了解

         // if it fails (SDK < 3.1) use the default director

         if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )

              [CCDirector setDirectorType:kCCDirectorTypeDefault];

        

        

         CCDirector *director = [CCDirector sharedDirector];

        

         // 初始化视图控制器

         viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];

         viewController.wantsFullScreenLayout = YES;

        

         //

         // Create the EAGLView manually手动创建EAGLView,用于游戏渲染,处//理opengl es命令

         //  1. Create a RGB565 format. Alternative: RGBA8

         //   2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition

         //

         //

         EAGLView *glView = [EAGLView viewWithFrame:[window bounds]

        pixelFormat:kEAGLColorFormatRGB565      

// kEAGLColorFormatRGBA8

                                                           depthFormat:0                                         // GL_DEPTH_COMPONENT16_OES

                                          ];

        

         // attach the openglView to the director 将openglView视图绑定在director导演上

         [director setOpenGLView:glView];

        

//       // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices

//       if( ! [director enableRetinaDisplay:YES] )

//            CCLOG(@"Retina Display Not supported");

        

         //

         // VERY IMPORTANT:

         // If the rotation is going to be controlled by a UIViewController

         // then the device orientation should be "Portrait".

         //

         // IMPORTANT:

         // By default, this template only supports Landscape orientations.

         // Edit the RootViewController.m file to edit the supported orientations.注意,要更改设备的初始方向,请在RootViewController.m里面修改,在这里改是没用的

         //

#if GAME_AUTOROTATION == kGameAutorotationUIViewController

         [director setDeviceOrientation:kCCDeviceOrientationPortrait];

#else

         [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];

#endif

        

         [director setAnimationInterval:1.0/60];

         [director setDisplayFPS:YES];

        

        

         // make the OpenGLView a child of the view controller

         [viewController setView:glView];

        

         // make the View Controller a child of the main window

         [window addSubview: viewController.view];

        

         [window makeKeyAndVisible];

        

         // Default texture format for PNG/BMP/TIFF/JPEG/GIF images

         // It can be RGBA8888, RGBA4444, RGB5_A1, RGB565

         // You can change anytime.

         [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];

 

        

         // Removes the startup flicker

         [self removeStartupFlicker];

        

         // Run the intro Scene

         [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];

}

 

//暂停运行游戏,通常是用户锁定iphone屏幕,或是有电话接入时,或任何其它力量导致游戏进入后台

 

- (void)applicationWillResignActive:(UIApplication *)application {

         [[CCDirector sharedDirector] pause];

}

//继续游戏,当用户解锁屏幕,或电话接听完毕后继续游戏

 

 

- (void)applicationDidBecomeActive:(UIApplication *)application {

         [[CCDirector sharedDirector] resume];

}

//当收到内存警报时,从内存中清除当前未使用的精灵纹理

 

请注意:你所有的图像文件(png,pvr)都被加载成GPU可以理解的opengl es纹理。而精灵则对应着这些纹理图。Cocos2d内置了一个纹理缓存管理器来保持这些纹理图。这样可以极大的加速创建新精灵,并充分利用已有的纹理图。不利的一面是,如果收到内存警报,cocos2d会将当前未使用的纹理图全部从内存中清除。因此,当游戏的场景切换时,有时需要手动释放当前的层和场景。关于这一点,大家争议不断。

 

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {

         [[CCDirector sharedDirector] purgeCachedData];

}

//

-(void) applicationDidEnterBackground:(UIApplication*)application {

         [[CCDirector sharedDirector] stopAnimation];

}

 

-(void) applicationWillEnterForeground:(UIApplication*)application {

         [[CCDirector sharedDirector] startAnimation];

}

//终止director,将EAGLView从程序窗口UIWindow解除绑定。

 

- (void)applicationWillTerminate:(UIApplication *)application {

         CCDirector *director = [CCDirector sharedDirector];

        

         [[director openGLView] removeFromSuperview];

        

         [viewController release];

        

         [window release];

        

         [director end];   

}

 

- (void)applicationSignificantTimeChange:(UIApplication *)application {

         [[CCDirector sharedDirector] setNextDeltaTimeZero:YES];

}


step 3. HelloWordLayer

 

HelloWorld类是继承自 CCLayer类的。

因为CCScene只是一个抽象的概念,默认的设置场景的方法是在你的类里面使用 一个静态初始化方法(static initializer)+(id) scene。此方法会生成一 个CCScene对象,并且将HelloWorld的对象添加到场景节点中。几乎在任何情况 下,CCScene都是在这里创建和使用的。

 

好吧,既然要深入了解,让我们看看scene这个类方法究竟干了些什么。

 

+(id) scene {

// 'scene' is an autorelease object.

CCScene *scene = [CCScene node]; // 'layer' is an autorelease object.

HelloWorld *layer = [HelloWorld node]; // add layer as a child to scene

[scene addChild: layer]; // return the scene

return scene;}

 

第一行是通过调用[CCScene node]来创建一个CCScene的实例化对象。如果你看不明白node是啥意思,让哥来告诉你,其实这行代码等同于[[[CCScene alloc ]init]autorelease]。那么,哥咋知道node的意思呢?狠简单,只要右键单击node,然后选择jump to definition,好吧,这下你无敌了。

 

首先,CCScene类的静态初始化方法+(id) node生成一个CCScene对象。接下来, 同样的+(id) node方法生成了HelloWorld节点,并被添加到场景中。然后场景被返回给调用者。

译注:

原来如此,[CCScene node]就是初始化一个对象而已。且慢,苹果官方的开发者文档不是说尽量不要使用自动释放对象吗?哎,虽然听苹果的话有好果果吃,但是太听话了就未免过于迂腐。Cocos2d的内存管理机制其实是很完善的,如果你非要用官方的规定来自己手动管理每一个场景,层,精灵等节点的内存,那么好吧,你会崩溃,最后游戏也会不知道神马原因在哪里就突然崩溃了。

 

如果不信,你大可以自己去尝试。

对于这一点,不同的教程,不同的开发者,苹果官方有不同的看法,大家自己选择何去何从吧。

以下是官方的内存管理指南:

http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt

/MemoryMgmt.html

 

提示:

想知道每个方法的具体定义和调用方式不?按住键盘上的Option,然后鼠标左键单击方法或消息的名称。

此外,对于一个未知的方法,还可以右键单击,然后选择Jump to Definition。

相信我,这一点对于新手学习非常重要!!!

接下来看看-(id)init方法。

// on "init" you need to initialize your instance

-(id) init

{

         // always call "super" init

         // Apple recommends to re-assign "self" with the "super" return value

         if( (self=[super init])) {

             

              // 采用静态初始化方法生成并初始化标签对象,按住option,单击labelWithString,你会了解更多的知识。

              CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];

 

              // 实例化一个CCDirector的单例,从而从CCDirector得到窗口的尺寸   

         CGSize size = [[CCDirector sharedDirector] winSize];

 

译注:该语句可以改成:

CGSize size = [CCDirector sharedDirector].winSize;

这样的写法无疑很合其它编程语言转移过来的兄弟胃口

        

         // 将标签放在屏幕中央,注意ccp其实是cocos2d对于CGPointMake的宏定义,类似的还有很多,别恐慌

              label.position =  ccp( size.width /2 , size.height/2 );

             

         // 将标签作为子节点添加到场景层中,注意,很多新手会忘了这一点,然后杯具的发现精灵并未出现在屏幕中。此外你可以在调用addChild之前或之后赋予位置信息。

              [self addChild: label];

 

//生成并初始化精灵对象

       

        CCSprite *spaceCargoShip = [CCSprite spriteWithFile:@"SpaceCargoShip.png"];

 

//将精灵放在屏幕中央

        [spaceCargoShip setPosition:ccp(size.width/2, size.height/2)];

 

//将精灵作为子节点添加到场景层中

        [self addChild:spaceCargoShip];

       

//说明一个moveAction动作,在15秒内移动到屏幕的左侧外,细节后面再解释

        id moveAction = [CCMoveTo actionWithDuration:15.0f position:ccp(-size.width/2, size.height/2)];

//太空货船执行这个动作

        [spaceCargoShip runAction:moveAction];

         }

         return self;

}

 

 

你可能会注意到一个有些奇怪的地 方:self = [super init]这个调用中,发送给super对象的init信息所返回的 值被赋给了self。如果你有C++的编程经验,你可能会对此很不理解。不需要沮丧!这是因为Objective-C必须手动调用super类的init方法。不存在对父类的 自动调用。而且我们必须把[super init]的返回值赋给self,因为我们有可能 得到一个空值(nil):

如果你很介意上述[super init]的写法,以下是另一种写法。它的作用和上面 的写法完全一样。

-(id) init { self = [super init];

}

if (self != nil) { //在此添加init方法的代码

} return self;

小技巧:如何修改cocos2d所开发游戏的默认屏幕方向

 

在RootViewController.m文件里面,修改如下代码

#elif GAME_AUTOROTATION == kGameAutorotationUIViewController

    //

    // EAGLView will be rotated by the UIViewController

    //

    // Sample: Autorotate only in landscpe mode

    //

    // return YES for the supported orientations

    

    return ( UIInterfaceOrientationIsPortrait( interfaceOrientation ) );

 

红色字体的就是可以更改的地方

请注意,在applicationDidFinishLaunching里面修改是无用的。

 

 

让HelloWorld更好玩一点(这部分内容先不补充了,后面翻完了回头再完善)

 

挑战:

cocos2d官方所提供的模板无疑是非常宝贵的资源,建议开发者要多尝试一下。


原创粉丝点击