在第一講中已經新建了第一個cocos2d程序,運行效果如下:
在這講中我們來分析下里面的代碼,瞭解cocos2d的工作原理,看看屏幕上的這個"Hello World"是如何顯示出來的。
這是HelloWorld項目的代碼結構:
下面,我們開始分析項目中的這些源文件:
從程序的入口點開始
這麼多源文件在這裏,究竟先看哪個呢?有些人可能會先挑內容少的來看,覺得這樣就可以輕鬆解決掉一個源文件了,其實這是不對的,這樣看起來更加是一頭霧水,根本搞不清楚每個源文件之間的聯繫。正確的做法應該是從程序的入口開始看起,慢慢理順各個源文件之間的關係。
有過iOS開發經驗的朋友都知道,一個iOS程序的入口是main函數,這個main函數存在於main.m中。打開main.m文件,看看main函數的內容:
- int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
- [pool release];
- return retVal;
- }
重點是這句代碼:UIApplicationMain(argc, argv, nil, @"AppDelegate"),UIApplicationMain函數會根據第4個參數傳入的類名創建一個應用程序的代理對象,這裏創建的是AppDelegate對象,因此AppDelegate對象就是整個應用程序的代理。
那應用程序的代理有什麼作用呢?
UIApplicationMain函數創建好應用程序的代理之後,會開啓事件循環,一旦監聽到系統事件,就會通知代理對象,並調用代理對象相關的生命週期方法來處理事件。比如:
* ios設備的內存極其優先,如果一個應用程序佔用了太多內存,操作系統會發出內存警告,在應用程序接收到這個事件後它會調用代理的applicationDidReceiveMemoryWarning方法,代理在這個方法內可以進行釋放內存的操作以防止操作系統強制終止應用程序的運行
* 當應用程序成功加載完畢後,會調用代理的application:didFinishLaunchingWithOptions:方法,一般會在這個方法裏面初始化應用程序的第一個界面
AppDelegate解讀
從上面的分析可知,一般會在AppDelegate的application:didFinishLaunchingWithOptions:方法中初始化應用程序的第一個界面,是的,開發者會在該方法中添加cocos2d的所有初始化代碼
打開AppDelegate.m,查看application:didFinishLaunchingWithOptions:方法
提示:我在這個方法裏面加了相應的中文註釋
- - (void) applicationDidFinishLaunching:(UIApplication*)application
- {
- // 初始化窗口
- window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
- // 設置CCDirector的類型
- if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
- [CCDirector setDirectorType:kCCDirectorTypeDefault];
- // 獲取CCDirector的單例對象
- CCDirector *director = [CCDirector sharedDirector];
- // 初始化控制器
- viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
- viewController.wantsFullScreenLayout = YES;
- // 創建一個視圖對象
- EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
- pixelFormat:kEAGLColorFormatRGB565
- depthFormat:0];
- // 關聯這個視圖對象到CCDirector
- [director setOpenGLView:glView];
- // 設置屏幕方向
- #if GAME_AUTOROTATION == kGameAutorotationUIViewController
- // 如果是使用UIViewController來實現旋轉,就設置豎屏
- [director setDeviceOrientation:kCCDeviceOrientationPortrait];
- #else
- // 其他情況,就設置橫屏
- [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
- #endif
- // 設置刷新間隔時間
- [director setAnimationInterval:1.0/60];
- // 設置是否要顯示FPS
- [director setDisplayFPS:YES];
- // 設置控制器的視圖
- [viewController setView:glView];
- // 添加控制器的視圖到window中
- [window addSubview: viewController.view];
- // 顯示window
- [window makeKeyAndVisible];
- // 設置紋理格式
- [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
- // 消除啓動時的閃爍
- [self removeStartupFlicker];
- // 設置第一個顯示的屏幕
- [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];
- }
看完這個方法後,你可能會一頭霧水,看到一大堆沒見過的API,這些就是cocos2d的API。
1.UIKit與OpenGL
這麼多看不懂的代碼,該怎麼解讀呢?有些人可能會硬着頭皮一行一行按順序往下解讀,這樣往往是事倍功半。
一般是先找自己能夠看懂的代碼,比如這幾句:
- // 初始化控制器
- viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
- viewController.wantsFullScreenLayout = YES;
- // 設置控制器的視圖
- [viewController setView:glView];
- // 添加控制器的視圖到window中
- [window addSubview: viewController.view];
- // 顯示window
- [window makeKeyAndVisible];
- // 創建一個視圖對象
- EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
- pixelFormat:kEAGLColorFormatRGB565
- depthFormat:0];
2.CCDirector
cocos2d中以節點(CCNode)爲基本元素,整個遊戲都是由節點構成的,其實一個很重要的節點元素就是場景(CCScene類,繼承自CCNode),一個遊戲裏面可能有很多個場景,比如闖關遊戲,可以一個關卡就是一個場景,一個遊戲設置界面也可以是一個場景。怎樣才能正常顯示一個場景呢?那麼就需要CCDirector這個類,沒有CCDirector,就不能顯示場景。
CCDirector類是整個cocos2d遊戲引擎的核心,全局只有一個實例,通過[CCDirector sharedDirector]可以獲取這個單例對象。
- // 獲取CCDirector的單例對象
- CCDirector *director = [CCDirector sharedDirector];
CCDirector的主要用途:
* 運行、替換、推入和彈出場景(即場景過渡)
* 訪問正在運行的場景
* 暫停、恢復、終止遊戲
* 在UIKit和OpengGL之間轉換座標
* 獲取窗口的尺寸
下面這句代碼非常關鍵:
- // 關聯這個視圖對象到CCDirector
- [director setOpenGLView:glView];
給CCDirector設置視圖後,CCDirector才知道場景(CCScene)上的內容要渲染到哪個視圖上面。因此,說CCDirector是UIKit和OpenGL之間的橋樑,一點也不爲過
cocos2d提供了4種類型的CCDirector,不同類型的CCDirector有不同的更新遊戲狀態的方式,這些更新方式會對遊戲的性能、與UIKit視圖的兼容性產生很大影響。
下面這句代碼就是設置CCDirector的類型:
- // 設置CCDirector的類型,如果iOS<3.1,就不支持kCCDirectorTypeDisplayLink,使用kCCDirectorTypeDefault
- if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
- [CCDirector setDirectorType:kCCDirectorTypeDefault];
kCCDirectorTypeNSTimer \ kCCDirectorTypeDefault | 最慢 |
kCCDirectorTypeMainLoop | 比NSTimer快,但是與UIKit視圖存在兼容性問題 |
kCCDirectorTypeThreadMainLoop | 比NSTimer快,但是與UIKit視圖存在兼容性問題 |
kCCDirectorTypeDisplayLink | 最快,最實用,但iOS版本至少是3.1 |
3.設置屏幕方向
通過CCDirector來設置屏幕方向
- // 設置屏幕方向
- #if GAME_AUTOROTATION == kGameAutorotationUIViewController
- // 如果是使用UIViewController來實現旋轉,就設置豎屏
- [director setDeviceOrientation:kCCDeviceOrientationPortrait];
- #else
- // 其他情況,就設置橫屏
- [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
- #endif
下面列出設備支持的所有方向:
kCCDeviceOrientationPortrait | |
kCCDeviceOrientationPortraitUpsideDown | |
kCCDeviceOrientationLandscapeLeft | |
kCCDeviceOrientationLandscapeRight |
4.設置遊戲幀率
大家都知道,遊戲界面上的內容是需要經常刷新的,比如一個子彈打出去,需要經常刷新子彈的位置。刷新速度就取決於遊戲幀率。
- [director setAnimationInterval:1.0/60];
注意:由於設備的限制,在iOS設備上的幀率不能大於60fps。如果強迫cocos2d以大於60fps的幀率進行渲染,很有可能會反而使幀率降低。因此,要想達到最快的渲染速度,使用60fps的幀率即可。
5.顯示遊戲幀率
- // 設置是否要顯示FPS
- [director setDisplayFPS:YES];
cocos2d會隔一段時間就更新這個數值,通過修改ccConfig.h的CC_DIRECTOR_FPS_INTERVAL值可以調整刷新數值的時間間隔,默認是0.1,即1秒鐘更新10次。ccConfig.h在項目的libs/cocos2d文件夾中。
- #ifndef CC_DIRECTOR_FPS_INTERVAL
- #define CC_DIRECTOR_FPS_INTERVAL (0.1f)
- #endif
6.設置遊戲的第一個場景
- [[CCDirector sharedDirector] runWithScene: [HelloWorldLayer scene]];
因此,需要搞清楚屏幕上顯示的"Hello World"是怎麼弄出來的,還得查看HelloWorldLayer這個類
7.在發出內存警告時釋放資源
- - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
- [[CCDirector sharedDirector] purgeCachedData];
- }
HelloWorldLayer解讀
打開HelloWorldLayer.h,可以發現HelloWorldLayer繼承了CCLayer,意爲圖層
- @interface HelloWorldLayer : CCLayer
- {
- }
- // returns a CCScene that contains the HelloWorldLayer as the only child
- +(CCScene *) scene;
- @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方法的實現
- +(CCScene *) scene
- {
- // 創建一個場景對象
- CCScene *scene = [CCScene node];
- // 創建一個圖層對象
- HelloWorldLayer *layer = [HelloWorldLayer node];
- // 添加圖層到場景中
- [scene addChild: layer];
- // 返回場景
- return scene;
- }
這裏使用[CCScene node]創建了一個CCScene對象,使用[HelloWorldLayer node]創建了一個HelloWorldLayer對象。
最後通過[scene addChild: layer]將圖層layer添加到場景scene中,可以看出layer是scene的一個子節點。實際上addChild:方法也是屬於CCNode的,可以看出每個CCNode都有自己的子節點。
- -(id) init
- {
- // always call "super" init
- // Apple recommends to re-assign "self" with the "super" return value
- if( (self=[super init])) {
- // create and initialize a Label
- CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
- // ask director the the window size
- CGSize size = [[CCDirector sharedDirector] winSize];
- // position the label on the center of the screen
- label.position = ccp( size.width /2 , size.height/2 );
- // add the label as a child to this Layer
- [self addChild: label];
- }
- return self;
- }
- CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
- CGSize size = [[CCDirector sharedDirector] winSize];
- label.position = ccp( size.width /2 , size.height/2 );
- #define ccp(__X__,__Y__) CGPointMake(__X__,__Y__)
- [self addChild: label];
就這樣,"Hello World"就顯示到我們的屏幕上了
分析可得,我們這個遊戲裏面存在着3個節點:
CCScene裏面有個CCLayer,CClayer裏面有個CCLabelTTF
總結
說了這麼多內容,最後做個大總結:
1.要想利用cocos2d在屏幕上顯示點東西,就必須使用CCDirector運行一個場景(CCScene),比如
- [[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的子類,因此,它們都是節點。一個節點又可以包含多個子節點
原文地址:http://blog.csdn.net/q199109106q/article/details/8591706
感謝作者~!