这一节将向游戏开发者介绍HelloWorld项目的目录结构,以及各个部分是如何协同工作的。通过本节的学习,你会对各个部分之间的联系有个大致的了解。
图13.22显示了HelloWorld项目的项目导航面板。
图13.22
cocos2d项目引用两类不同的文件:源码文件和资源文件(包括图像文件、声音文件和属性文件等)。Default.png文件是在iOS加载应用程序时显示的图像,Icon.png是应用程序部署在设备上的图标。不同设备上可能使用这些文件类型的不同版本。例如,Retina 设备上会加载带有@2x后缀的图像文件。关于应用程序图标、启动图像等图像尺寸和格式的完整列表,可以参考苹果公司的开发者文档中的Custom Icon and Image Creation Guidelines,具体网址是:http://developer.apple.com/library/ios/ documentation/userexperience/conceptual/ mobilehig/IconsImages/IconsImages.html#//apple_ref/doc/uid/TP40006556-CH14-SW1。
fps_images.png文件用于显示帧率,是不允许删除或修改的。
Info.plist文件包含了一些与项目相关的配置信息,同时也可以在这个文件中修改一些重要的设置,如项目图标、支持设备的方向等。
任何一个使用Xcode开发的应用程序或游戏都是从main.m开始的。在导航区域的“Supporting Files”组下面可以找到main.m文件。main.m的代码如下。
程序清单:codes/13/13.3/HelloWorld/HelloWorld/main.m
#import <UIKit/UIKit.h>
int main(int argc,char *argv[]) {
@autoreleasepool {
int retVal = UIApplicationMain(argc,argv,nil,@"AppController");
return retVal;
}
}
简单来说,main函数创建了@autoreleasepool(自动释放池),然后调用UIApplicationMain来启动程序。其中,AppController是实现UIApplicationDelegate协议的类,AppController类的实现包含在AppDelegate文件中。在启用了ARC的cocos2d应用程序中,Autorelease Pool(自动释放池)可以确保内存的对象最终被释放。
该组中的另一个文件Prefix.pch是预编译头文件,用于加快编译速度。pch表示Pre-Compiled Header,游戏开发者应该把不常变化的框架(Frameworks)头文件添加到该文件中。这样做可以使框架的代码提前被编译,并对所有类可见。
不过,这样做要注意一个问题:如果pch前缀头文件中的一个头文件发生了变化,项目中的所有代码都将会重新编译。所以,pch前缀头文件中只应该添加那些极少或者从来都不发生变化的头文件。
游戏开发者可以将cocos2d.h头文件添加到pch前缀头文件中,因为它几乎不会改变。而且,一开始就将cocos2d.h添加到预编译头文件中是一个非常好的习惯,这样在其他源文件中就不必再写“#import "cocos2d.h"”了。在预编译头文件中加入cocos2d.h,代码如下。
程序清单:codes/13/13.3/HelloWorld/HelloWorld/Prefix.pch
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#endif
每个iOS程序都有一个AppDelegate类,用于实现UIApplicationDelegate协议。AppDelegate实现的方法用于处理iOS应用程序的状态改变。例如,可以通过它来处理用户来电或者系统内存不足时程序需要采取的措施。在查看AppDelegate方法的具体实现代码前,先要了解AppDelegate.h中的类定义。在AppDelegate.h中定义了AppController这个类,它直接继承自NSObject类,并且遵循UIApplicationDelegate和CCDirectorDelegate两个协议。
程序加载完成后,立即执行的方法就是applicationDidFinishLaunchingWithOptions:,在该方法中,通常都是应用启动后需要执行的初始化代码。
在Xcode中打开AppDelegate.m文件,找到applicationDidFinishLaunchingWithOptions方法,代码如下。
程序清单:codes/13/13.3/HelloWorld/HelloWorld/AppDelegate.m
-(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// 创建程序主窗口
window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// 创建CCGLView对象,用于游戏中的渲染
// 从cocos2d v2.0之后,CCGLView替代了之前的EAGLView
// 使用CCGLView将OpenGL ES命令发送给OpenGL ES驱动
CCGLView *glView =[CCGLView viewWithFrame:[window_ bounds]
pixelFormat:kEAGLColorFormatRGB565depthFormat:0
preserveBackbuffer:NOsharegroup:nilmultiSampling:NO
numberOfSamples:0];
// ①创建导演对象
director_ =(CCDirectorIOS*) [CCDirector sharedDirector];
director_.wantsFullScreenLayout = YES;
// 将FPS(Frames Per Second)和SPF(Second Per Frame)显示设置为开
// cocos2d会自动计算游戏的当前帧数,并显示在屏幕的左下角
// 在调试过程中FPS显示对我们调试会非常有用
// 在游戏正式上传到设备时可以将其设置为NO,则FPS和SPF不会再显示在设备中
[director_ setDisplayStats:YES];
// 将动画间距设置为每秒60次,这也是cocos2d中的默认设置
// 在正常情况下,cocos2d每秒最高会刷新屏幕中的显示60次
[director_ setAnimationInterval:1.0/60];
// 将openglView绑定到director_导演对象上
[director_ setView:glView];
// 将director_的代理对象设置为self(自身),用于获取屏幕信息
[director_ setDelegate:self];
// 设置2D或3D投射
[director_ setProjection:kCCDirectorProjection2D];
// [director setProjection:kCCDirectorProjection3D];
// 启用Retina高清显示模式
if(! [director_ enableRetinaDisplay:YES] )
CCLOG(@"Retina Display Not supported");
// 设置cocos2d的纹理格式
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// 在iPad或Retina高清显示模式下,使用CCFileUtils自动添加图片的后缀
CCFileUtils *sharedFileUtils = [CCFileUtils sharedFileUtils];
[sharedFileUtils setEnableFallbackSuffixes:NO];
[sharedFileUtils setiPhoneRetinaDisplaySuffix:@"-hd"];
[sharedFileUtils setiPadSuffix:@"-ipad"];
[sharedFileUtils setiPadRetinaDisplaySuffix:@"-ipadhd"];
// 设置PVR图像具备多重透明度
[CCTexture2D PVRImagesHavePremultipliedAlpha:YES];
// ②初始化IntroLayer场景,并使用pushScene方法切换到该场景
[director_ pushScene: [IntroLayer scene]];
// 使用director_创建NavigationController导航控制器
navController_ = [[UINavigationController alloc] initWithRootViewController:director_];
// 设置navigationBarHidden为YES
navController_.navigationBarHidden = YES;
// 将NavigationController导航控制器设置为根视图控制器
[window_ setRootViewController:navController_];
// 设置主窗口可见
[window_ makeKeyAndVisible];
return YES;
}
在applicationDidFinishLaunchingWithOptions方法中,①号代码创建了一个CCDirector导演对象,在cocos2d中,CCDirector导演对象负责游戏的循环运行,并且在游戏中渲染所有的图像,它会掌控游戏的运行、暂停或停止。AppController中包括了CCDirector响应iOS操作系统在内的暂停或继续等各个事件。代码如下。
程序清单:codes/13.3/HelloWorld/HelloWorld/AppDelegate.m
// 当操作系统暂停应用时执行,调用此方法来暂停游戏和所有的计时器
// 当玩家在玩游戏的过程中锁定了iPad或iPhone,或者有电话打进来
// 或者其他类似事件发生需要强迫游戏进入后台时,会调用此方法
-(void)applicationWillResignActive:(UIApplication *)application
{
if([navController_ visibleViewController] == director_ )
[director_ pause];
}
// 当玩家解锁iPad或iPhone,或电话已接听完毕时,会调用此方法来继续游戏和所有的计时器
-(void)applicationDidBecomeActive: (UIApplication *)application
{
if( [navController_ visibleViewController] == director_ )
[director_ resume];
}
// 在iOS 4.0及以后的版本中,开始支持应用在后台运行,此时会停止运行屏幕中的动画
-(void)applicationDidEnterBackground: (UIApplication*)application
{
if( [navController_ visibleViewController] == director_ )
[director_ stopAnimation];
}
// 当应用重新回到前台来运行时,重新启动屏幕中的动画
-(void)applicationWillEnterForeground: (UIApplication*)application
{
if( [navController_ visibleViewController] == director_ )
[director_ startAnimation];
}
// 应用程序即将中止时调用此方法
// 该方法会终止CCDirector的控制,并从应用程序的UIWindow中解除对CCGLView的绑定
// 同时还将结束游戏循环,从内存中清除所有的纹理和计时器
-(void)applicationWillTerminate: (UIApplication *)application
{
CC_DIRECTOR_END();
}
// 当系统收到内存不足的警告时,会调用此方法从内存中清除未在屏幕中显示的纹理图。
-(void)applicationDidReceiveMemoryWarning: (UIApplication *)application
{
[[CCDirector sharedDirector] purgeCachedData];
}
// 将上一次时间调用和当前事件调用间的增量时间设置为0。
// 该方法的调用场景是:两次调用之间已经过了太长的时间。
// 这通常是由于iPhone重新调整了系统时间而导致。
-(void)applicationSignificantTimeChange: (UIApplication *)application
{
[[CCDirector sharedDirector] setNextDeltaTimeZero:YES];
}
在applicationDidFinishLaunchingWithOptions方法中,编号②代码初始化IntroLayer场景,并使用pushScene方法切换到该场景。下面我们来看看IntroLayer类的实现。
IntroLayer类继承自CCLayer,因为CCDirector只能运行CCScene对象,所以需要实现IntroLayer的scene方法,它会把IntroLayer当作唯一的子节点添加进去,并返回一个CCScene。打开IntroLayer.m文件,找到scene方法,代码如下。
程序清单:codes/13/13.3/HelloWorld/HelloWorld/IntroLayer.m
+(CCScene *)scene
{
// 创建CCScene场景对象
CCScene *scene = [CCScene node];
// 创建IntroLayer层对象
IntroLayer *layer = [IntroLayer node];
// 将layer添加为scene的子节点
[scene addChild: layer];
// 返回scene对象
return scene;
}
以前的cocos2d版本中是不存在IntroLayer类的,这是cocos2d 2.0新加的类,它的主要目的就是在进入游戏之前,可以显示游戏制作公司的Logo等信息。显示Logo的实现代码在onEnter方法当中,代码如下(程序清单同上)。
-(void)onEnter
{
[super onEnter];
// 获得设备支持的窗口大小
CGSize size = [[CCDirector sharedDirector] winSize];
CCSprite *background;
// 判断设备是iPhone还是iPad,并据此来加载不同的背景资源图片
if(UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone ){
background = [CCSprite spriteWithFile:@"Default.png"];
background.rotation = 90;
} else {
background = [CCSprite spriteWithFile:@"Default-Landscape~ipad.png"];
}
// 设置背景图片的位置居中
background.position = ccp(size.width/2,size.height/2);
// 把背景图片添加到IntroLayer中
[self addChild: background];
// 触发一个定时器方法调用,在1秒后会调用makeTransition函数
[self scheduleOnce:@selector(makeTransition:)delay:1];
}
接下来看看makeTransition方法的实现,代码如下(程序清单同上)。
-(void)makeTransition: (ccTime)delta
{
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:1.0
scene:[HelloWorldLayer scene] withColor:ccWHITE]];
}
该方法只有一句代码调用,就是使用CCDirector配合CCTransitionFade场景切换特效类,把HelloWorldLayer类在1秒屏幕变白后展示出来。最后来看看HelloWordLayer类的实现。
HelloWorldLayer类继承自CCLayer。因为CCScene只是一个抽象的接口,而默认设置场景的方法通常是在类里使用一个静态初始化方法+(id)scene。此方法会创建一个CCScene对象,并且将HelloWorldLayer的对象添加为场景的子节点。代码如下。
程序清单:codes/13/13.3/HelloWorld/HelloWorld/HelloWorldLayer.m
+(CCScene *)scene
{
// 创建CCScene场景对象
CCScene *scene = [CCScene node];
// 创建HelloWorldLayer层对象
HelloWorldLayer *layer = [HelloWorldLayer node];
// 将layer添加为scene的子节点
[scene addChild: layer];
// 返回scene对象
return scene;
}
接下来再看看-(id)init初始化方法的实现,代码如下(程序清单同上)。
-(id) init
{
if((self=[super init])) {
// 创建和初始化一个标签对象
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World"
fontName:@"Marker Felt" fontSize:64];
// 实例化得到一个CCDirector的单例,并获得窗口的尺寸
CGSize size = [[CCDirector sharedDirector] winSize];
// 将标签位置设置在屏幕中央,ccp是cocos2d对于CGPointMake的宏定义
label.position = ccp(size.width /2,size.height/2);
// 将label添加为scene的子节点
[self addChild: label];
// 下面是初始化游戏中心的成就榜和高分榜的代码,此处先略去暂时不讨论
}
return self;
}
以上就是HelloWorld项目的详细实现过程。读者之前已经学习过iOS基础知识,所以这里并没有对所有类的所有实现代码进行逐行解释和分析。但通过以上详细代码分析,我们初步了解了一个cocos2d应用的启动和运行过程,这将为以后开发更复杂的游戏或应用程序打下扎实的基础。
在HelloWorld项目中,使用了场景(CCScene)、层(CCLayer)、导演(CCDirector)等核心类,在cocos2d的开发中,几乎任何一款cocos2d游戏中都会用到这些类,所以了解它们的作用、属性和方法,以及如何将这些类组织在一起,是我们学习cocos2d游戏开发的必修课。掌握了这些基本知识之后,大家就会发现使用cocos2d开发游戏其实很简单。接下来重点介绍cocos2d中的核心类。