- 怎 么样使用 Cocos2d-x 快速开发游戏,方法很简单,你可以看看其自带的例程,或者从网上搜索教程,运行起第一个 SceneHelloWorldScene,然后在 HelloWorldScene 里面写相关逻辑代码,添加我们的层、精灵等 ~ 我们并不一定需要知道 Cocos2d-x 是如何运行或者在各种平台之上运行,也不用知道 Cocos2d-x 的游戏是如何运行起来的,它又是如何渲染界面的 ~~~
- 两个入口
- 问题的推测
- 程序的流程 (这里以 Linux 的实现为主,其它平台触类旁通即可)
- AppDelegate 与 CCApplication
- 从 CCApplication 到 CCDirector
- CCEGLView 的收尾工作
- cocos2d-x 程序的结束流程
- cocos2d-x 的整体把握
怎么样使用 Cocos2d-x 快速开发游戏,方法很简单,你可以看看其自带的例程,或者从网上搜索教程,运行起第一个 SceneHelloWorldScene,然后在 HelloWorldScene 里面写相关逻辑代码,添加我们的层、精灵等 ~ 我们并不一定需要知道 Cocos2d-x 是如何运行或者在各种平台之上运行,也不用知道 Cocos2d-x 的游戏是如何运行起来的,它又是如何渲染界面的 ~~~
我们只用知道 Cocos2d-x 的程序是由
print('Hello World!')
我们可以不用关心其是怎么实现的,我们只要知道这样就能打印一句话就够了,这就是
Cocos2d-x 自带的例程已经足够丰富,但是有些问题并不是看看例子,调用其方法就能明白的事情,在这里一叶遇到了如下问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
// AppDelegate.cpp 文件
AppDelegate::AppDelegate()
{
CCLog(
"AppDelegate()"
);
// AppDelegate 构造函数打印
}
AppDelegate::~AppDelegate()
{
CCLog(
"AppDelegate().~()"
);
// AppDelegate 析构函数打印
}
// 程序入口
bool
AppDelegate::applicationDidFinishLaun
{
// initialize director
CCDirector *pDirector = CCDirector::sharedDirector();
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
// 初始化,资源适配,屏幕适配,运行第一个场景等代码
...
...
...
return
true
;
}
void
AppDelegate::applicationDidEnterBackg
{
CCDirector::sharedDirector()->pause();
}
void
AppDelegate::applicationWillEnterFore
{
CCDirector::sharedDirector()->resume();
}
|
此时我并不知道程序运行时,何时调用 AppDelegate 的构造函数,析构函数和程序入口函数,我们只要知道,程序在这里调用了其构造函数,然后进入入口函数执行其过程,最后再调用其析构函数即可。然而事与愿违,在实际执行的过程中,发现程序只调用其构造函数和入口函数,而直到程序结束运行,都
发生这样的情况,让我
两个入口
程序入口的概念是相对的,AppDelegate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#include "main.h"
#include "../Classes/AppDelegate.h"
#include "cocos2d.h"
#include
#include
#include
#include
USING_NS_CC;
// 500 is enough?
#define MAXPATHLEN 500
int
main(
int
argc,
char
**argv)
{
// get application path
int
length;
char
fullpath[MAXPATHLEN];
length = readlink(
"/proc/self/exe"
, fullpath,
sizeof
(fullpath));
fullpath[length] =
'\0'
;
std::string resourcePath = fullpath;
resourcePath = resourcePath.substr(0, resourcePath.find_last_of(
"/"
));
resourcePath +=
"/../../../Resources/"
;
// create the application instance
AppDelegate app;
CCApplication::sharedApplication()->setResourceRootPath(resourcePath.c_str());
CCEGLView* eglView = CCEGLView::sharedOpenGLView();
eglView->setFrameSize(720, 480);
//
return
CCApplication::sharedApplication()->run();
}
|
在这里我们看见了程序的真正入口,包含一个 main 函数,从此进入,执行 cocos2d-x 程序。
我们看到 main 就知道其是入口函数,那么没有 main 函数就没有入口了吗?显然不是,以 Android 平台启动 cocos2d-x 程序为例。我们找到 Android 平台与上面
1
|
#include
"cocos2d.h"
#include
"AppDelegate.h"
#include
"platform/android/jni/JniHelper.h"
#include #include #define
"main"
#define
"C"
{ jint JNI_OnLoad(JavaVM *vm,
void
*reserved) { JniHelper::setJavaVM(vm);
return
JNI_VERSION_1_4; }
void
Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h) {
if
(!CCDirector::sharedDirector()->getOpenGLView()) { CCEGLView *view = CCEGLView::sharedOpenGLView(); view->setFrameSize(w, h); AppDelegate *pAppDelegate =
new
AppDelegate(); CCApplication::sharedApplication()->run(); }
else
{ ccDrawInit(); ccGLInvalidateStateCache
|
我们并没有看到所谓的 main 函数,这是由于不同的平台封装所以有着不同的实现,在 Android 平台,默认是使用 Java 开发,可以使用 Java 通过 Jni 调用 C++ 程序,而这里也正式如此。我们暂且只需知道,由 Android 启动一个应用,通过各种峰回路转,最终执行到了
所以说程序的入口是相对的,正如博文开始的
这里我们参考了两个不同平台的实现, Linux 和 Android 平台 cocos2d-x 程序入口 main.cpp的实现,那么其它平台呢,如 iOS ,Win32 等 ~~~ 殊途同归,其它平台程序的入口必然包含着其它平台的不同
问题的推测
我们就从 Linux 和 Android 这两个平台的入口函数开始,看看 cocos2d-x 的执行流程到底为何?何以发生只执行了 AppDelegate 的构造函数,而没有析构函数。在查看 cocos2d-x 程序代码时,我们只关注
// Linux 平台关键代码 int main(int argc, char **argv) { // 初始化等内容 ... ... // 创建 app 变量 AppDelegate app; ... ... // 执行 核心 run() 方法 return CCApplication::sharedApplication()->run(); } // Android 平台关键代码 void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h) { if (!CCDirector::sharedDirector()->getOpenGLView()) { CCEGLView *view = CCEGLView::sharedOpenGLView(); view->setFrameSize(w, h); // 创建 AppDelegate 对象 AppDelegate *pAppDelegate = new AppDelegate(); // 执行 核心 run() 方法 CCApplication::sharedApplication()->run(); } else { ... ... } }
不同的平台,却实现相同操作,创建
程序的流程 (这里以 Linux 的实现为主,其它平台触类旁通即可)
AppDelegate 与 CCApplication
我们从
1
|
// [cocos2dx-path]/cocos2dx/platform/linux/CCApplication.cpp ... // 此变量为定义了一个 CCApplication 的静态变量,也及时自己类型本身,实现单例模式 CCApplication * CCApplication::sm_pSharedApplication = 0; ... // 构造函数,将所创建的 对象直接付给其静态变量 CCApplication::CCApplication() { // 断言在此决定着此构造函数只能运行一次 CC_ASSERT(! sm_pSharedApplication); sm_pSharedApplication = this; } CCApplication::~CCApplication() { CC_ASSERT(this == sm_pSharedApplication); sm_pSharedApplication = NULL; m_nAnimationInterval = 1.0f/60.0f*1000.0f; } // run 方法,整个 cocos2d-x 的主循环在这里开始 int CCApplication::run() { // 首次启动调用初始化函数 if (! applicationDidFinishLaun
|
从上面的内容可以看出,从
(这里的实现机制,不做详细说明,简单说来:applicationDidFinishLaun
比较重要的所在,for 循环并没有循环退出条件,所以 run 方法永远不会返回。那么是怎么结束的呢!要学会存疑!
从 CCApplication 到 CCDirector
cocos2d-x 程序已经运行起来了,我们继续下一步,mainLoop
// [cocos2dx-path]/cocos2dx/CCDirector.cpp ... // 定义静态变量,实现单例模式 static CCDisplayLinkDirector *s_SharedDirector = NULL; ... // 返回 CCDirector 实例 CCDirector* CCDirector::sharedDirector(void) { // 判断静态变量,以保证只有一个实例 if (!s_SharedDirector) { s_SharedDirector = new CCDisplayLinkDirector(); s_SharedDirector->init(); } // CCDisplayLinkDirector 为 CCDirector 的子类,这里返回了其子类 return s_SharedDirector; } // mainLoop 方法的具体实现 void CCDisplayLinkDirector::mainLoop(void) { // 此变量是我们需要关注,并且跟踪的,因为它决定着程序的结束时机 if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoo p = false; // 运行到此,说明程序的运行,已经没有逻辑代码需要处理了 purgeDirector(); } else if (! m_bInvalid) { // 屏幕绘制,并做一些相应的逻辑处理,其内部处理,这里暂且不做过多探讨 drawScene(); // 这里实现了 cocos2d-x CCObject 对象的内存管理机制,对此有兴趣者,可以深入下去 CCPoolManager::sharedPoolManager()->pop(); } } // 弹出场景 CCScene void CCDirector::popScene(void) { CCAssert(m_pRunningScene != NULL, "running scene should not null"); m_pobScenesStack->removeLastObject(); unsigned int c = m_pobScenesStack->count(); if (c == 0) { // 如果没有场景,调用 end() 方法 end(); } else { m_bSendCleanupToScene = true; m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1); } } void CCDirector::end() { // 在 end 方法中,设置了变量为 true,这所致的结果,在 mainLoop 函数中,达成了运行 purgeDirector 方法的条件 m_bPurgeDirecotorInNextLoo p = true; } // 此方法做些收尾清理的工作 void CCDirector::purgeDirector() { ... if (m_pRunningScene) { m_pRunningScene->onExit(); m_pRunningScene->cleanup(); m_pRunningScene->release(); } // 做一些清理的工作 ... // OpenGL view // ###此句代码关键### m_pobOpenGLView->end(); m_pobOpenGLView = NULL; // delete CCDirector release(); } // 设置 openglview void CCDirector::setOpenGLView(CCEGLView *pobOpenGLView) { CCAssert(pobOpenGLView, "opengl view should not be null"); if (m_pobOpenGLView != pobOpenGLView) { // EAGLView is not a CCObject delete m_pobOpenGLView; // [openGLView_ release] // 为当前 CCDirector m_pobOpenGLView 赋值 m_pobOpenGLView = pobOpenGLView; // set size m_obWinSizeInPoints = m_pobOpenGLView->getDesignResolutionSize(); createStatsLabel(); if (m_pobOpenGLView) { setGLDefaultValues(); } CHECK_GL_ERROR_DEBUG(); m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher); m_pTouchDispatcher->setDispatchEvents(true); } }
游戏的运行以场景为基础,每时每刻都有一个场景正在运行,其内部有一个场景栈,遵循后进后出的原则,当我们显示的调用 end() 方法,或者弹出当前场景之时,其自动判断,如果没有场景存在,也会触发 end() 方法,以说明场景运行的结束,而游戏如果没有场景,就像演出没有了舞台,程序进入最后收尾的工作,通过修改变量
CCEGLView 的收尾工作
purgeDirector 方法之内,通过猜测与排查,最终定位到
// AppDelegate.cpp CCDirector *pDirector = CCDirector::sharedDirector(); pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
我们终于走到最后一步,看 CCEGLView 是如果负责收尾工作的:
// [cocos2dx-path]/cocos2dx/platform/linux.CCEGLView.cpp ... CCEGLView* CCEGLView::sharedOpenGLView() { static CCEGLView* s_pEglView = NULL; if (s_pEglView == NULL) { s_pEglView = new CCEGLView(); } return s_pEglView; } ... // openglview 结束方法 void CCEGLView::end() { glfwTerminate(); delete this; exit(0); }
end()
cocos2d-x 程序的结束流程
程序运行时期,由
从程序的 main 方法开始,再创建 AppDelegate 等对象,运行过程中确实通过 exit(0); 来退出程序。所以我们看到了 AppDelegate 构造函数被调用,而其析构函数没有被调用的现象。
exit(0);
cocos2d-x 的整体把握
在本文通过解决一个小疑问,而去分析 cocos2d-x 游戏的运行流程,当然其中很多细致末叶我们并没有深入下去。不去解决这个疑问也可以,知道没有调用析构函数,那我就不调用便是 (这也是简单的解决方法,也不用觉得这不可行 )。这里只是借着这个疑问,对 cocos2d-x 的流程稍作探寻而已。也没有贴一堆 cocos2d-x 源码去分析,其思路也有迹可循。
什么是 cocos2d-x ,它是 cocos2d 一个 C++ 的实现,除 C++ 之外,有 python ,Objective-C 等其它语言的实现,那该怎么去理解 cocos2d ,可以这么理解,cocos2d 是一个编写 2D 游戏的通用形框架,这种框架提供了一个通用模型,而这种模型或者说架构是
装载自 http://my.oschina.net/JeremyOuyang/blog/164403