原博地址:第一个cocos2d程序的代码分析

在第一讲中已经新建了第一个cocos2d程序,运行效果如下:

在这讲中我们来分析下里面的代码,了解cocos2d的工作原理,看看屏幕上的这个"Hello World"是如何显示出来的。

这是HelloWorld项目的代码结构:

下面,我们开始分析项目中的这些源文件:

从程序的入口点开始

这么多源文件在这里,究竟先看哪个呢?有些人可能会先挑内容少的来看,觉得这样就可以轻松解决掉一个源文件了,其实这是不对的,这样看起来更加是一头雾水,根本搞不清楚每个源文件之间的联系。正确的做法应该是从程序的入口开始看起,慢慢理顺各个源文件之间的关系。

有过iOS开发经验的朋友都知道,一个iOS程序的入口是main函数,这个main函数存在于main.m中。打开main.m文件,看看main函数的内容:

[java]  view plain copy
  1. int main(int argc, char *argv[]) {
  2. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  3. int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
  4. [pool release];
  5. return retVal;
  6. }

重点是这句代码:UIApplicationMain(argc, argv, nil, @"AppDelegate"),UIApplicationMain函数会根据第4个参数传入的类名创建一个应用程序的代理对象,这里创建的是AppDelegate对象,因此AppDelegate对象就是整个应用程序的代理。

那应用程序的代理有什么作用呢?

UIApplicationMain函数创建好应用程序的代理之后,会开启事件循环,一旦监听到系统事件,就会通知代理对象,并调用代理对象相关的生命周期方法来处理事件。比如:

* ios设备的内存极其优先,如果一个应用程序占用了太多内存,操作系统会发出内存警告,在应用程序接收到这个事件后它会调用代理的applicationDidReceiveMemoryWarning方法,代理在这个方法内可以进行释放内存的操作以防止操作系统强制终止应用程序的运行

* 当应用程序成功加载完毕后,会调用代理的application:didFinishLaunchingWithOptions:方法,一般会在这个方法里面初始化应用程序的第一个界面

AppDelegate解读

从上面的分析可知,一般会在AppDelegate的application:didFinishLaunchingWithOptions:方法中初始化应用程序的第一个界面,是的,开发者会在该方法中添加cocos2d的所有初始化代码

打开AppDelegate.m,查看application:didFinishLaunchingWithOptions:方法

提示:我在这个方法里面加了相应的中文注释

[java]  view plain copy
  1. - (void) applicationDidFinishLaunching:(UIApplication*)application
  2. {
  3. // 初始化窗口
  4. window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  5. // 设置CCDirector的类型
  6. if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
  7. [CCDirector setDirectorType:kCCDirectorTypeDefault];
  8. // 获取CCDirector的单例对象
  9. CCDirector *director = [CCDirector sharedDirector];
  10. // 初始化控制器
  11. viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
  12. viewController.wantsFullScreenLayout = YES;
  13. // 创建一个视图对象
  14. EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
  15. pixelFormat:kEAGLColorFormatRGB565
  16. depthFormat:0];
  17. // 关联这个视图对象到CCDirector
  18. [director setOpenGLView:glView];
  19. // 设置屏幕方向
  20. #if GAME_AUTOROTATION == kGameAutorotationUIViewController
  21. // 如果是使用UIViewController来实现旋转,就设置竖屏
  22. [director setDeviceOrientation:kCCDeviceOrientationPortrait];
  23. #else
  24. // 其他情况,就设置横屏
  25. [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
  26. #endif
  27. // 设置刷新间隔时间
  28. [director setAnimationInterval:1.0/60];
  29. // 设置是否要显示FPS
  30. [director setDisplayFPS:YES];
  31. // 设置控制器的视图
  32. [viewController setView:glView];
  33. // 添加控制器的视图到window中
  34. [window addSubview: viewController.view];
  35. // 显示window
  36. [window makeKeyAndVisible];
  37. // 设置纹理格式
  38. [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
  39. // 消除启动时的闪烁
  40. [self removeStartupFlicker];
  41. // 设置第一个显示的屏幕
  42. [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];
  43. }

看完这个方法后,你可能会一头雾水,看到一大堆没见过的API,这些就是cocos2d的API。

1.UIKit与OpenGL

这么多看不懂的代码,该怎么解读呢?有些人可能会硬着头皮一行一行按顺序往下解读,这样往往是事倍功半。

一般是先找自己能够看懂的代码,比如这几句:

[java]  view plain copy
  1. // 初始化控制器
  2. viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
  3. viewController.wantsFullScreenLayout = YES;
  4. // 设置控制器的视图
  5. [viewController setView:glView];
  6. // 添加控制器的视图到window中
  7. [window addSubview: viewController.view];
  8. // 显示window
  9. [window makeKeyAndVisible];

可以看出,屏幕 上显示的内容就是这个glView视图,这个glView是EAGLView类 型的:

[java]  view plain copy
  1. // 创建一个视图对象
  2. EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
  3. pixelFormat:kEAGLColorFormatRGB565
  4. depthFormat:0];

cocos2d利用OpenGL将内容都渲染到了这个glView上,最后呈现到屏幕上给用户看。因此,cocos2d的本质是在UIKit和OpenGL之间进行了转换。

2.CCDirector

cocos2d中以节点(CCNode)为基本元素,整个游戏都是由节点构成的,其实一个很重要的节点元素就是场景(CCScene类,继承自CCNode),一个游戏里面可能有很多个场景,比如闯关游戏,可以一个关卡就是一个场景,一个游戏设置界面也可以是一个场景。怎样才能正常显示一个场景呢?那么就需要CCDirector这个类,没有CCDirector,就不能显示场景。

CCDirector类是整个cocos2d游戏引擎的核心,全局只有一个实例,通过[CCDirector sharedDirector]可以获取这个单例对象。

[java]  view plain copy
  1. // 获取CCDirector的单例对象
  2. CCDirector *director = [CCDirector sharedDirector];

CCDirector的主要用途:

* 运行、替换、推入和弹出场景(即场景过渡)

* 访问正在运行的场景

* 暂停、恢复、终止游戏

* 在UIKit和OpengGL之间转换坐标

* 获取窗口的尺寸

下面这句代码非常关键:

[java]  view plain copy
  1. // 关联这个视图对象到CCDirector
  2. [director setOpenGLView:glView];

给CCDirector设置视图后,CCDirector才知道场景(CCScene)上的内容要渲染到哪个视图上面。因此,说CCDirector是UIKit和OpenGL之间的桥梁,一点也不为过

cocos2d提供了4种类型的CCDirector,不同类型的CCDirector有不同的更新游戏状态的方式,这些更新方式会对游戏的性能、与UIKit视图的兼容性产生很大影响。

下面这句代码就是设置CCDirector的类型:

[java]  view plain copy
  1. // 设置CCDirector的类型,如果iOS<3.1,就不支持kCCDirectorTypeDisplayLink,使用kCCDirectorTypeDefault
  2. if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
  3. [CCDirector setDirectorType:kCCDirectorTypeDefault];

可以供设置的4种CCDirector:

kCCDirectorTypeNSTimer \ kCCDirectorTypeDefault 最慢
kCCDirectorTypeMainLoop 比NSTimer快,但是与UIKit视图存在兼容性问题
kCCDirectorTypeThreadMainLoop 比NSTimer快,但是与UIKit视图存在兼容性问题
kCCDirectorTypeDisplayLink 最快,最实用,但iOS版本至少是3.1

3.设置屏幕方向

通过CCDirector来设置屏幕方向

[java]  view plain copy
  1. // 设置屏幕方向
  2. #if GAME_AUTOROTATION == kGameAutorotationUIViewController
  3. // 如果是使用UIViewController来实现旋转,就设置竖屏
  4. [director setDeviceOrientation:kCCDeviceOrientationPortrait];
  5. #else
  6. // 其他情况,就设置横屏
  7. [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
  8. #endif

GAME_AUTOROTATION是一个宏定义,可以用来判断设备对旋转的支持情况,iOS第1和2代设备上面的旋转是十分耗性能的,但从iOS第3代设备开始,可以使用UIViewController来轻松实现自动旋转。

下面列出设备支持的所有方向:

kCCDeviceOrientationPortrait
kCCDeviceOrientationPortraitUpsideDown
kCCDeviceOrientationLandscapeLeft
kCCDeviceOrientationLandscapeRight

4.设置游戏帧率

大家都知道,游戏界面上的内容是需要经常刷新的,比如一个子弹打出去,需要经常刷新子弹的位置。刷新速度就取决于游戏帧率。

[java]  view plain copy
  1. [director setAnimationInterval:1.0/60];

这里设置的1.0/60并不是指游戏帧率,是指刷帧的时间间隔,即屏幕连续2次刷新之间的时间间隔:1.0/60秒。换算一下,可以得出游戏帧率为60fps(frame per seconds,帧/秒),即1秒钟刷新60帧。当然,如果游戏比较复杂,CPU/GPU需要画大于1.0/60秒的时间来刷新屏幕,就无法保证游戏始终保持60fps的刷新速度,会造成帧率不稳定。如果游戏的帧率发生大幅度波动,会造成时快时慢的效果,会严重降低玩家的用户体验,因此,复杂游戏的帧率不用设置得太高,最好设置为30fps。

注意:由于设备的限制,在iOS设备上的帧率不能大于60fps。如果强迫cocos2d以大于60fps的帧率进行渲染,很有可能会反而使帧率降低。因此,要想达到最快的渲染速度,使用60fps的帧率即可。

5.显示游戏帧率

[java]  view plain copy
  1. // 设置是否要显示FPS
  2. [director setDisplayFPS:YES];

设置了显示帧率后,屏幕左下角显示的数字就是游戏的帧率,当前是59.9fps。

cocos2d会隔一段时间就更新这个数值,通过修改ccConfig.h的CC_DIRECTOR_FPS_INTERVAL值可以调整刷新数值的时间间隔,默认是0.1,即1秒钟更新10次。ccConfig.h在项目的libs/cocos2d文件夹中。

[java]  view plain copy
  1. #ifndef CC_DIRECTOR_FPS_INTERVAL
  2. #define CC_DIRECTOR_FPS_INTERVAL (0.1f)
  3. #endif

6.设置游戏的第一个场景

[java]  view plain copy
  1. [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];

这里调用CCDirector的runWithScene:方法来设置游戏的第一个场景,这个场景对象是通过[HelloWorldLayer scene]这个静态方法创建。

因此,需要搞清楚屏幕上显示的"Hello World"是怎么弄出来的,还得查看HelloWorldLayer这个类

7.在发出内存警告时释放资源

[java]  view plain copy
  1. - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
  2. [[CCDirector sharedDirector] purgeCachedData];
  3. }

HelloWorldLayer解读

打开HelloWorldLayer.h,可以发现HelloWorldLayer继承了CCLayer,意为图层

[java]  view plain copy
  1. @interface HelloWorldLayer : CCLayer
  2. {
  3. }
  4. // returns a CCScene that contains the HelloWorldLayer as the only child
  5. +(CCScene *) scene;
  6. @end

那之前看到的场景(CCScene)和图层(CCLayer)究竟有什么联系呢?

下面我先详细阐述场景(CCScene)和图层(CCLayer)之间的关系:

一个游戏里面可能有很多个场景,比如闯关游戏,可以一个关卡就是一个场景,一个游戏设置界面也可以是一个场景。一个场景里面又可以包含多个图层。

拿捕鱼达人来说,下面列出捕鱼达人中的4个场景:

场景1
场景2

场景3
场景4

可以看出,不同的业务逻辑就放在不同的场景中。但是,如果一个场景过于复杂,场景里面又可以分出多个图层(CCLayer)。

比如场景4中的捕鱼界面,根据图层的功能,大致可以分为3个图层:

1> 底部的Background Layer是背景层,用来显示背景图片

2> 中间的Game Layer是游戏层,用来显示游戏中的精灵(CCSprite,可以用来表示游戏中的人物、道具等),这里的鱼就是精灵

3> 顶部的UI DOM Menu是菜单层,用来显示一些菜单按钮、控制按钮

再查看HelloWorldLayer.m中scene方法的实现

[java]  view plain copy
  1. +(CCScene *) scene
  2. {
  3. // 创建一个场景对象
  4. CCScene *scene = [CCScene node];
  5. // 创建一个图层对象
  6. HelloWorldLayer *layer = [HelloWorldLayer node];
  7. // 添加图层到场景中
  8. [scene addChild: layer];
  9. // 返回场景
  10. return scene;
  11. }

CCScene和CCLayer都继承自CCNode,可以通过CCNode的+(id) node方法快速创建一个自动释放的对象。

这里使用[CCScene node]创建了一个CCScene对象,使用[HelloWorldLayer node]创建了一个HelloWorldLayer对象。

最后通过[scene addChild: layer]将图层layer添加到场景scene中,可以看出layer是scene的一个子节点。实际上addChild:方法也是属于CCNode的,可以看出每个CCNode都有自己的子节点。

那"Hello World"这段文字是怎么显示到屏幕上的呢?看看HelloWorldLayer的init方法

[java]  view plain copy
  1. -(id) init
  2. {
  3. // always call "super" init
  4. // Apple recommends to re-assign "self" with the "super" return value
  5. if( (self=[super init])) {
  6. // create and initialize a Label
  7. CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
  8. // ask director the the window size
  9. CGSize size = [[CCDirector sharedDirector] winSize];
  10. // position the label on the center of the screen
  11. label.position =  ccp( size.width /2 , size.height/2 );
  12. // add the label as a child to this Layer
  13. [self addChild: label];
  14. }
  15. return self;
  16. }

首先初始化了一个CCLabelTTF标签对象,标签显示的文字为"Hello World",使用Marker Felt字体,字体大小为64,CCLabelTTF继承自CCSprite,属于精灵,CCSprite又继承自CCNode。

[java]  view plain copy
  1. CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];

接着获取屏幕的宽度和高度

[java]  view plain copy
  1. CGSize size = [[CCDirector sharedDirector] winSize];

设置标签在父节点中的位置(默认情况下,精灵的中心点会在position属性所指定的位置上)

[java]  view plain copy
  1. label.position =  ccp( size.width /2 , size.height/2 );

cpp其实是个宏定 义

[java]  view plain copy
  1. #define ccp(__X__,__Y__) CGPointMake(__X__,__Y__)

最后添加标签到图层中

[java]  view plain copy
  1. [self addChild: label];

就这样,"Hello World"就显示到我们的屏幕上了

分析可得,我们这个游戏里面存在着3个节点:

CCScene里面有个CCLayer,CClayer里面有个CCLabelTTF

总结

说了这么多内容,最后做个大总结:

1.要想利用cocos2d在屏幕上显示点东西,就必须使用CCDirector运行一个场景(CCScene),比如

[java]  view plain copy
  1. [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];

2.场景(CCScene)可以添加多个图层(CCLayer),每个图层又可以添加其他子节点,比如精灵(CCSprite)

3.cocos2d会利用OpenGL将场景(CCScene)中的所有内容渲染到UIKit视图上(这里用的是EAGLView),EAGLView被添加到UIWindow中,最终显示在屏幕上

4.大部分情况下,都是直接面向cocos2d进行开发,即直接用cocos2d的CCSprite、CCLayer、CCScene等类进行开发,不需要关心OpenGL与UIKit之间的转换

5.cocos2d的基本元素是节点(CCNode),屏幕上的任何东西都可以称之为节点,像我们常用的CCSprite、CCLayer、CCScene,都是CCNode的子类,因此,它们都是节点。一个节点又可以包含多个子节点

第一个cocos2d程序的代码分析相关推荐

  1. 三、第一个cocos2d程序的代码分析

    在 第一讲 中已经新建了第一个cocos2d程序,运行效果如下: 在这讲中我们来分析下里面的代码,了解cocos2d的工作原理,看看屏幕上的这个"Hello World"是如何显示 ...

  2. cocos2D(三)---- 第一个cocos2d程序的代码分析

    在第一讲中已经新建了第一个cocos2d程序,运行效果如下: 在这讲中我们来分析下里面的代码,了解cocos2d的工作原理,看看屏幕上的这个"Hello World"是如何显示出来 ...

  3. cocos2D 程序代码分析 3

    在第一讲中已经新建了第一个cocos2d程序,运行效果如下: 在这讲中我们来分析下里面的代码,了解cocos2d的工作原理,看看屏幕上的这个"Hello World"是如何显示出来 ...

  4. 静态代码分析工具列表分析---代码分析工具列表(30款工具)

    本文是一个静态代码分析工具的清单,共有30个工具.包括4个.NET工具.2个Ada工具.7个C++工具.4个Java工具.2个JavaScript工具.1个Opa工具.2个Packaging工具.3个 ...

  5. 静态代码分析工具清单:开源篇(各语言)

    本文是一个静态代码分析工具的清单,共有26个工具.包括4个.NET工具.2个Ada工具.7个C++工具.4个Java工具.2个JavaScript工具.1个Opa工具.2个Packaging工具.3个 ...

  6. 静态代码分析工具清单:开源篇

    http://hao.jobbole.com/static_code_analysis_tool_list_opensource_lang/?utm_source=blog.jobbole.com&a ...

  7. Part2:S3C2440裸跑一个LED程序

    Part2:S3C2440落跑一个LED程序 0 写在前面 1 明确"操作对象"和"操作过程" 2 代码编写和分析--看一个程序如何裸跑... 2.1 代码编写 ...

  8. vs 2015 C 语言,VS2015中C/C++代码分析

    VS2015中C/C++代码分析 03/31/2015 8 分钟可看完 本文内容 [原文发表时间]:2015/2/24 1:00 PM 来自 Joe Morris & Jim Springfi ...

  9. 【Java】如何编写、运行一个Java程序

    当我们开始学习一门语言时,实际操作必不可少.以下,通过一个实例来展示如何编写一个Java程序: 代码 public class HelloWorld{public static void main(S ...

最新文章

  1. 泉州服务器维修,泉州云服务器
  2. 第十一周学习总结--助教
  3. Spring.Net学习
  4. 在CRM定制中常用的Javascript
  5. 【mongodb系统学习之六】mongodb配置文件方式启动
  6. Spring Boot 五种热部署方式,再也不用老重启了!
  7. 互联网1分钟 |1221
  8. 网友怒喷运营商“为推广5G故意调慢4G网速!”官方紧急回应!
  9. Xcode添加pch文件
  10. 数据库知识 | 关系型数据库与非关系型数据库小记录
  11. pandas dataframe根据筛选结果修改值
  12. Comet OJ - Contest #3 题解
  13. java rfid 写入_RFID读写器函数JAVA调用示例
  14. html图片随圆点下标轮播,基于JavaScript实现轮播图代码
  15. 计算机网络(5.13)运输层- TCP的拥塞控制方法
  16. [统计学理论基础] 统计方法—F检验
  17. ArcGIS Server 发布地图服务遇到的问题
  18. BLOB/TEXT column ‘h_long_varbinary‘ used in key specification without a key length
  19. 《锋利的jQuery》学习总结
  20. Java 中的get set方法快捷键和含义

热门文章

  1. php 0x80070003,HOWTO: 解决 Windows DISM error ID3 0x80070003 故障
  2. MariaDB商标和版权
  3. ”填坑“ -- springboot 启动报错 “Process finished with exit code 1”
  4. 励志短语,懂得生存,学会竞争
  5. 我的一次华为虚拟化搭建记录:(一)、关于华为虚拟化的架构
  6. GateWay路由网关的概述与入门详解
  7. 全球及中国DHA粉行业消费需求调查与竞争前景研究报告2022-2028年
  8. Android-App-启动优化全记录,hashmap和concurrenthashmap的区别
  9. 钉钉、企业微信和飞书对比
  10. shell programming tutorial