HelloCpp是Cocos2d-x自带的一个工程,它演示了Cocos2d-x最基本的使用方法和流程。先看一下它的基本构成
win32目录中包含了对应平台的代码,而Classes目录中包含了我们自己的实现代码。编译运行的结果如下图
main函数变形记
看到main命名的文件就会想到著名的main函数一定在这个文件里面,那么就让我们先看看这个文件吧。main.h里面主要是include了各种各样的头文件,main.cpp是我们真正需要关注的
1 int APIENTRY _tWinMain(HINSTANCE hInstance, 2 HINSTANCE hPrevInstance,128); line-height:1.5!important"> 3 LPTSTR lpCmdLine,128); line-height:1.5!important"> 4 int nCmdShow) 5 { 6 UNREFERENCED_PARAMETER(hPrevInstance); 7 UNREFERENCED_PARAMETER(lpCmdLine); 8 9 // create the application instance 10 AppDelegate app; 11 CCEGLView* eglView = CCEGLView::sharedOpenGLView(); 12 eglView->setFrameSize(960,640 ); 13 return CCApplication::sharedApplication()->run(); 14 }
1.1 APIENTRY
首先出现的不明物体是APIENTRY。我们先看看它的定义
1 #define APIENTRY WINAPI
APIENTRY是WINAPI的一个替身,而WINAPI是微软定义的宏,实际就是__stdcall
众所周知,__stdcall声明了函数从右至左将参数压栈并由被调用者清理堆栈的调用约定。
1.2 _tWinMain
如果我说_tWinMain是程序的入口函数,你会不会发飙:我去,入口函数不应该是main吗?是的!对于常规的C/C++程序而言main是入口函数的归宿,但在Windows程序中WinMain才是入口函数。
1.3 UNREFERENCED_PARAMETER
进入_tWinMain函数后就是两句很神奇的语句
要理解这句话首先是要搞清楚UNREFERENCED_PARAMETER是神马玩意儿。如果我告诉你它神马都不是,你信吗?
承认吧骚年,它真的神马都不是啊~(你逗我玩儿呐?)
AppDelegate的前世今生
_tWinMain函数的真正主体是从AppDelegate app开始的,所以我们就首先从AppDelegate说起。我们先看看AppDelegate的宗族关系
要把AppDelegate弄清楚搞明白,我们还得从源头开始。
2.1 CCApplicationProtocol
作为Cocos2d-x Application的源头,CCApplicationProtocal定义如下
整个CCApplicationProtocol除了析构函数以外的其他所有函数都是纯虚函数,这也就是它为什么叫Protocol的原因。每个函数的含义和作用在注释里有简要的说明,但具体的实现何其作用需要进一步才能理解。
2.2 CCApplication
作为对Cocos2d-x Application的抽象,CCApplication所扮演的角色是非常重要的。它的定义如下
2.2.1 构造函数
CCApplication的构造函数所完成的工作就是对成员变量的初始化
2.2.2 Application是唯一的
我们再把_tWinMain函数请出来仔细看看(这里去掉了几句无用的代码)
这是由sm_pSharedApplication引发的断言异常。sm_pSharedApplication是CCApplication类的一个protected的static成员,虽然在构造函数中将它赋值为this,但它的初始化却是0(空指针)
同时CCApplication提供一个public的static函数来访问它
若果你熟悉设计模式就能一眼看出来这个实现实际上是单例模式,它保证了CCApplication只有一个实例。
看到这里,你可能会惊呼:一定是_tWinMain函数的最后一句return CCApplication::sharedApplication()->run() 引入了错误。哎,事实总是残酷的!实际上异常早在这之前就发生了。我们在CCApplication::sharedApplication函数中下断点,F5运行程序,从CallStack可以看到异常在CCEGLView::WindowProc函数中就发生了
进入CCEGLView::WindowProc函数,很快我们就能发现引发异常的代码段
是不是被绕晕了?我也有点晕!没关系,我们把CCEGLView插入到这里来分析一下cocos2d-x是如何构成完整Windows程序框架的。熬过这话题就不会晕了。
2.2.3 构建Windows程序框架
从经典教科书《Windows程序设计》中可以看到,典型的Windows程序的框架大体如下
接下来,我们分析CCEGLView::setFrameSize函数。其实现如下
最后,就剩下“循环获取消息”这部分了。这个时候你不是想起了_tWinMain函数的最后一句代码了吗?让我们再看他一眼
锁定目标,我们进入CCApplication::run函数探究一番,毕竟这个函数也是我们之前列出的重要函数之一。
2.2.4 run出来的消息
CCApplication::run函数的实现如下
1)显示窗口
2)进入消息循环
到此,典型的Windows程序框架就完整了!如果你还是晕的,请从头再看一遍吧。
2.3 AppDelegate
作为对HelloWorld应用程序的抽象,AppDelegate从CCApplication派生而来,重载了纯虚函数
如注释所陈述的,applicationDidEnterBackground函数会在程序进入“非活跃”状态(即失去窗口焦点)时被调用,而applicationWillEnterForeground函数会在程序进入“活跃”状态(即获得窗口焦点)时被调用(可以自己在这个函数里面下断点看看具体执行的流程)。
舞台需要场景
演员站在舞台上,却表演于场景中
—— by 我挂科了
赋词一句略显文艺范儿,求勿喷!言归正传,首先我们来看看HelloWorld的继承关系
HelloWorld从CCLayer继承,而CCLayer又是一个非常复杂的(至少它的father太多了)。你一定觉得HelloWorld很复杂吧,其实它没有传说中那么复杂
3.1、CREATE_FUNC
其实HelloWorld还是很单纯的,因为CREATE_FUNC定义很简单
1)创建__TYPE__类型的对象指针
a)如果init函数执行成功,则调用该对象的autorelease函数并返回该对象指针
b)如果init函数执行失败,则释放该对象并返回NULL
将这个宏在HelloWorld类中展开,HelloWorld就露出了它的真面目了
3.2 梦回AppDelegate
还记得AppDelegate::applicationDidFinishLauching函数吗?它的实现中有这么一句
1)创建了一个空的CCScene对象scene
2)通过上面分析过的HelloWorld::create函数创建了一个HelloWorld对象layer,并将layer作为scene的一个子节点添加到scene中。
HelloWorld::create函数实际上调用的是CREATE_FUNC(HelloWorld);
3)将scene返回
3.3 初始化
在前面我们已经展示了HelloWorld::create函数,它创建了HelloWorld对象后会调用该对象的init函数来初始化对象。HelloWorld::init函数实现如下
2)创建一个菜单(CCMenu)并在其中添加一个菜单项(CCMenuItem),将菜单显示在HelloWorld中。点击这个菜单项可以关闭程序
3)创建一个标签(CCLabel),让它在HelloWorld中显示“HelloWorld”字串
4)创建一个精灵(CCSprite),让它在HelloWorld中显示图片“HelloWorld.png”
HelloWorld::init函数中所创建的都是我们在运行起来的界面中所能看到的东西,这其中涉及到一些类将在后面学习,这里不深究。我比较好奇的是菜单项所实现的功能:点击后关闭程序(不信你亲自点一下试试)。这是怎么实现的呢?仔细分析一下菜单项的创建过程
2 CCMenuItemImage *pCloseItem = CCMenuItemImage::create( 3 4 5 6 menu_selector(HelloWorld::menuCloseCallback));
最后一个参数形式很特别啊~ 我们先看看menu_selector是神马东西
又是一个宏!它所完成的事情是将_SELECTOR取址并强制转换为SEL_MenuHandler类型。那么SEL_MenuHandler又是什么类型呢?
它是一个函数指针,这类函数有一个CCObject*类型的参数。此时我们可以将创建菜单项的代码展开,看看其真实的原貌
HelloWorld::menuCloseCallback函数以回调函数的方式传入菜单项,在点击菜单项时被触发。也就是说实现关闭程序功能的是HelloWorld::menuCloseCallback函数