深入源码,学习Application,ApplicationProtocol,AppDelegate三个类的作用,以及相互之间的关系,并且了解cocos2d-lua下mac/ios的启动流程。
ios/mac下的AppController—>AppDelegate—>cocos2d::Application(ios/mac)::run()
。
提示:以下是本篇文章正文内容,下面案例可供参考
使用命令新建工程
cocos new -l lua -p com.game.myluatest -d ./ MyGameLuaTest
使用xcode打开MyGameLuaTest/frameworks/runtime-src/proj.ios_mac/MyGameLuaTest.xcodeproj工程。左边栏目项目代码中MyGameLuaTest下ios/mac就是对应平台下的启动源码:
// cocos2d application instance
static AppDelegate s_sharedApplication;
创建了一个静态全局AppDegate对象,直到程序结束,自动销毁。
class AppDelegate : private cocos2d::Application
{
public:
AppDelegate();
virtual ~AppDelegate();
virtual void initGLContextAttrs();
/**
@brief Implement Director and Scene init code here.
@return true Initialize success, app continue.
@return false Initialize failed, app terminate.
*/
virtual bool applicationDidFinishLaunching();
/**
@brief The function be called when the application enter background
@param the pointer of the application
*/
virtual void applicationDidEnterBackground();
/**
@brief The function be called when the application enter foreground
@param the pointer of the application
*/
virtual void applicationWillEnterForeground();
};
由此可见AppDelegate继承了Application。有4个方法是由AppDelegate类中实现。这4个方法来自于ApplicationProtocol。
以下是ApplicationProtocol.h源码:
/** Subclass override the function to set OpenGL context attribution instead of use default value.
* And now can only set six attributions:redBits,greenBits,blueBits,alphaBits,depthBits,stencilBits.
* Default value are(5,6,5,0,16,0), usually use as follows:
* void AppDelegate::initGLContextAttrs(){
* GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};
* GLView::setGLContextAttrs(glContextAttrs);
* }
*/
virtual void initGLContextAttrs() {
}
/**
* @brief Implement Director and Scene init code here.
* @return true Initialize success, app continue.
* @return false Initialize failed, app terminate.
* @js NA
* @lua NA
*/
virtual bool applicationDidFinishLaunching() = 0;
/**
* @brief This function will be called when the application enters background.
* @js NA
* @lua NA
*/
virtual void applicationDidEnterBackground() = 0;
/**
* @brief This function will be called when the application enters foreground.
* @js NA
* @lua NA
*/
virtual void applicationWillEnterForeground() = 0;
然而 cocos2d::Application 又继承了 ApplicationProtocol,并且AppDelegate中只有4个方法:
1.initGLContextAttrs:初始化glContext;
2.applicationDidFinishLaunching:app加载完成,在此方法实现Director以及Scene的初始化;
3.applicationDidEnterBackground:app进入后台时候的回调方法;
4.applicationWillEnterForeground:app进入到前台的时候的回调方法;
cocos2dx通过设置app代理的方式,在只修改AppDelegate中的代码来实现跨平台功能;即如果需要在回到后台时候,实现一些功能,比如清理一些不常用的文理缓存,只需要在applicationDidEnterBackground的方法中实现,即可实现跨平台的效果。即各个平台(ios/mac/android/win32/winrt/linux)同时适用。为什么,接下来继续看下面代码。
#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
#include "platform/mac/CCApplication-mac.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#include "platform/ios/CCApplication-ios.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/android/CCApplication-android.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include "platform/win32/CCApplication-win32.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
#include "platform/winrt/CCApplication.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_LINUX
#include "platform/linux/CCApplication-linux.h"
#endif
在头文件源码中,在不同平台下引入不同的CCApplication的头文件
以下是CCApplication-ios.h源码:继承了ApplicationProtocol
//在CCApplication-ios.h文件中
class CC_DLL Application : public ApplicationProtocol
...省略若干
/**
@brief Run the message loop.
*/
int run();
/**
@brief Get the current application instance.
@return Current application instance pointer.
*/
static Application* getInstance();
protected:
static Application * sm_pSharedApplication;
以下是CCApplication-ios.mm源码:
Application* Application::sm_pSharedApplication = 0;
Application::Application()
{
CC_ASSERT(! sm_pSharedApplication);
sm_pSharedApplication = this;
}
/
// static member function
//
Application* Application::getInstance()
{
CC_ASSERT(sm_pSharedApplication);
return sm_pSharedApplication;
}
回到第一点中的AppController.mm的源码,初始化全局静态的AppDelegate的对象;其实默认调用了父类的CCApplication无参数构造函数。即次例CCApplication-ios.mm中的静态成员变量sm_pSharedApplication是本身CCApplication对象实例的一个指针。因此cocos2d::Application *app = cocos2d::Application::getInstance()返回的是各个平台下CCApplication对象的实例。
AppController.mm源码:
// cocos2d application instance
static AppDelegate s_sharedApplication;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
cocos2d::Application *app = cocos2d::Application::getInstance();
...省略若干行代码
//run the cocos2d-x game scene
app->run();
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
cocos2d::Application::getInstance()->applicationDidEnterBackground();
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
cocos2d::Application::getInstance()->applicationWillEnterForeground();
}
1.当AppController.mm中调用app->run();方法时候,就是调用了CCApplication其父类ApplicationProtocol中的applicationDidFinishLaunching方法,但是唯一实现就是AppDelegate文件中。
2.当ios的AppController.mm进入applicationDidEnterBackground方法时候(应用转到后台)会调用cocos2d::Application::getInstance()->applicationDidEnterBackground();
3.当iosAppController进入applicationWillEnterForegroundy应用将要前台)的时候调用cocos2d::Application::getInstance()->applicationWillEnterForeground();
CCApplication-ios.mm源码:
int Application::run()
{
if (applicationDidFinishLaunching())
{
[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
}
return 0;
}
然后调用[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
即CCDirectorCaller中的startMainLoop开启ios中屏幕刷新的定时器。CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。源码中创建一个新的 CADisplayLink 对象,把它添加到一个runloop中,并给它提供一个 target 和selector 在屏幕刷新的时候调用。在每次刷新屏幕的时候调用
cocos2d::Director* director = cocos2d::Director::getInstance();
director->mainLoop(dt)
CCDirectorCaller.mm源码:
-(void) startMainLoop
{
// Director::setAnimationInterval() is called, we should invalidate it first
[self stopMainLoop];
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
[displayLink setFrameInterval: self.interval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
-(void) doCaller: (id) sender
{
if (isAppActive) {
cocos2d::Director* director = cocos2d::Director::getInstance();
EAGLContext* cocos2dxContext = [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context];
if (cocos2dxContext != [EAGLContext currentContext])
glFlush();
[EAGLContext setCurrentContext: cocos2dxContext];
CFTimeInterval dt = ((CADisplayLink*)displayLink).timestamp - lastDisplayTime;
lastDisplayTime = ((CADisplayLink*)displayLink).timestamp;
director->mainLoop(dt);
}
}
此时正式进入Cocos2dx主循环当中。
CCDirector.cpp源码:
void Director::mainLoop(float dt)
{
_deltaTime = dt;
_deltaTimePassedByCaller = true;
mainLoop();
}
void Director::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
2.mac下的Info.plist
以ios启动流程为例,在文件AppController.mm中,创建AppDelegate对象,AppDelegate继承CCApplication,而CCApplication则继承ApplicationProtocol;然后AppController.mm中调用了CCApplication的run方法,而run方法中调用了ApplicationProtocol的applicationDidFinishLaunching方法(实现方法在AppDelegate中),还有开启了ios中屏幕刷新的定时器CADisplayLink,每次屏幕刷新调用doCaller方法,此方法中执行了CCDirector中的mainLoop(dt)方法,从而进入了Cocos2dx的主循环当中。