版本:Cocos2dx 3.10
环境:Xcode
语言:C++/Lua
简要的说明下cocos2d-Lua项目的启动,方便对后续内容的理解:
main
开始调用Application
Application
负责管理游戏的生命周期,平台和语言的设置Application::run
对OpenGL窗口初始化,并初始化FPS, LuaEngine等main.lua
后表示应用程序启动mainLoop
,根据FPS的设定进行渲染更新和内存管理这个就是启动流程的大概步骤,主要的类接口有:
Application
应用程序类,负责管理生命周期,平台和语言等AppDelegate
继承于Application
主要负责生命周期相关它是整个游戏的入口点,负责管理游戏的生命周期、平台和语言等。
class CC_DLL Application : public ApplicationProtocol {
public:
Application();
virtual ~Application();
static Application* getInstance();
// 设置游戏动画的帧率间隔,FPS
virtual void setAnimationInterval(float interval) override;
// 运行应用程序
int run();
// 获取当前语言类型,比如中文,英文等
virtual LanguageType getCurrentLanguage() override;
// 获取当前语言的代码
virtual const char * getCurrentLanguageCode() override;
// 获取应用程序目标平台类型,比如Mac,IOS,Android等
virtual Platform getTargetPlatform() override;
// 获取cocos版本号
virtual std::string getVersion() override;
// 打开URL链接
virtual bool openURL(const std::string &url) override;
};
启动流程的主要方法是:run
int Application::run() {
// 初始化OpenGL的属性,主要有:
// red,green,blue,alpha,depth,stencil,multisamplesCount
initGLContextAttrs();
// 检测应用程序启动逻辑相关,如果成功则true, 失败则false
if(!applicationDidFinishLaunching()) {
return 1;
}
long lastTime = 0L;
long curTime = 0L;
// 获取Director和OpenGL的实例对象
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
glview->retain();
unsigned int ctx_updated_count = 0;
// 游戏主循环,实时监测OpenGL窗口是否关闭
while (!glview->windowShouldClose()) {
lastTime = getCurrentMillSecond();
// 执行场景的绘制相关
director->mainLoop();
// 检测处理OpenGL窗口的输入事件相关
glview->pollEvents();
// 计算当前帧时间监测是否等待下一阵休眠
curTime = getCurrentMillSecond();
if (curTime - lastTime < _animationInterval) {
usleep(static_cast<useconds_t>((_animationInterval - curTime + lastTime)*1000));
}
}
if (glview->isOpenGLReady()) {
director->end();
director->mainLoop();
}
glview->release();
return 0;
}
在mainLoop
中,它的处理主要是:
void Director::mainLoop() {
if (! _invalid) {
// 渲染场景,处理程序输入事件,监听等
drawScene();
// 内存管理相关
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
关于渲染相关,可参考博客:渲染机制简介
关于内存管理, 可参考博客: 内存管理机制
它主要用于处理应用程序的生命周期,主要有三个接口:
applicationDidFinishLaunching
应用程序启动applicationDidEnterBackground
应用程序进入后台applicationWillEnterForeground
应用程序回到前台class AppDelegate : private cocos2d::Application {
public:
AppDelegate();
virtual ~AppDelegate();
virtual void initGLContextAttrs();
// 主要负责设置应用程序的FPS, 设置窗口等
virtual bool applicationDidFinishLaunching();
// 主要负责暂停动画,声音等
virtual void applicationDidEnterBackground();
// 主要负责恢复动画,声音等
virtual void applicationWillEnterForeground();
};
在启动应用程序时,我们看下它的逻辑处理:
bool AppDelegate::applicationDidFinishLaunching()
{
// 设置动画间隔,即FPS
Director::getInstance()->setAnimationInterval(1.0 / 60.0f);
// 初始化LuaEngine
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);
// 设置Lua的堆栈环境
lua_State* L = engine->getLuaStack()->getLuaState();
// 注册tolua++接口相关,方便C++与Lua交互
lua_module_register(L);
register_all_packages();
// 设置加密
LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));
// 设置Lua的资源搜索路径相关
FileUtils::getInstance()->addSearchPath("src");
FileUtils::getInstance()->addSearchPath("res");
// 执行Lua内的main文件
if (engine->executeScriptFile("main.lua")) {
return false;
}
return true;
}
至此大概的启动流程结束。
在Application::run
中,主循环有个逻辑处理:
int Application::run() {
// 检测OpenGL窗口是否关闭
while (!glview->windowShouldClose()) {
// ..
}
}
windowShouldClose
是OpenGL提供,用于检测窗口是否关闭。
在项目运行的过程中,如果需要告知OpenGL窗口关闭,需要调用OpenGL提供的方法:end()
。
告知项目,项目要结束运行了。
但我们在项目开发过程中,结束项目运行,会这样编写:
cc.Director:getInstance():end();
这个方法的主要处理逻辑是:
void Director::end() {
// 该变量有且只有在此处为true
_purgeDirectorInNextLoop = true;
}
_purgeDirectorInNextLoop
是一个布尔类型的变量,被赋值为 true的情况下,也只有这里。
而通过该变量的判定逻辑处理在Director::mainLoop
每帧刷新的主循环中。
void Director::mainLoop() {
if (_purgeDirectorInNextLoop) {
_purgeDirectorInNextLoop = false;
purgeDirector();
} else if (! _invalid) {
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
该变量会作为一个开关进行调用purgeDirector
。简单的说,这个方法就是做项目退出的收尾处理。
void Director::purgeDirector() {
reset();
// 退出OpenGLView
// 执行end方法后,windowShouldClose会收到关闭请求
if (_openGLView) {
_openGLView->end();
_openGLView = nullptr;
}
// 调用Ref::release进行内存管理释放相关
release();
}
void Director::reset() {
// 场景退出
if (_runningScene) {
_runningScene->onExit();
_runningScene->cleanup();
_runningScene->release();
}
_runningScene = nullptr;
_nextScene = nullptr;
// 定时器关闭
getScheduler()->unscheduleAll();
// 移除事件相关
if (_eventDispatcher) {
_eventDispatcher->removeAllEventListeners();
}
_scenesStack.clear();
// 停止动画
stopAnimation();
// 释放字体相关
FontFNT::purgeCachedData();
FontAtlasCache::purgeCachedData();
FontFreeType::shutdownFreeType();
// purge all managed caches
DrawPrimitives::free();
AnimationCache::destroyInstance();
SpriteFrameCache::destroyInstance();
GLProgramCache::destroyInstance();
GLProgramStateCache::destroyInstance();
FileUtils::destroyInstance();
AsyncTaskPool::destroyInstance();
UserDefault::destroyInstance();
GL::invalidateStateCache();
RenderState::finalize();
destroyTextureCache();
}
通过这些代码分析,我们汇总下关于项目结束流程的步骤:
Application::run
中的主循环中,通过windowShouldClose()
每帧检测窗口是否需要关闭Director::end()
方法设定标记_purgeDirectorInNextLoop
为trueDirector::mainLoop()
中,通过标记执行purgeDirector()
方法对项目进行收尾处理,主要操作:
windowShouldClose
收到OpenGL关闭窗口请求,停止主循环刷新// purge all managed caches
AnimationCache::destroyInstance();
SpriteFrameCache::destroyInstance();
destroyTextureCache();
void Director::destroyTextureCache() {
if (_textureCache) {
_textureCache->waitForQuit();
CC_SAFE_RELEASE_NULL(_textureCache);
}
}
这段代码是关于动画、精灵帧和纹理缓存的删除。
如果我们在项目开发中,尤其做内存优化处理的过程中,删除一定要严格按照此种方式,执行顺序:
动画 --> 精灵帧 --> 纹理
最后,祝大家学习生活愉快!