cocos2d-x应用窗口相关源码剖析
1 CCApplicationProtocol、CCApplication、AppDelegate类实现源码分析
什么是框架,框架最低要求
理论:
1)框架定义了一些协议(功能的抽象);目的:解耦合,通过函数指针实现。
2)框架实现了这层协议的调用,框架具有集成功能。
3)上层应用按照要求,去实现框架协议
代码兑现:
1)CCApplicationProtocol类定义了一套接口
2)CCApplication 实现了接口规则的调用
3)AppDelegate实现了接口
4)在CCApplication::run()利用多态特性,实现父类代码去调用子类(AppDelegate)重载函数;这也是多态的重要意义。典型的工程开发项目设计思路
问题1:是谁去调用了AppDelegate类的函数重载的函数?
virtual bool applicationDidFinishLaunching();
virtual void applicationDidEnterBackground();
virtual void applicationWillEnterForeground();
答案:
1)类继承分析:
class CC_DLL CCApplication : public CCApplicationProtocol
class AppDelegate : private cocos2d::CCApplication
AppDelegate->CCApplication->CCApplicationProtocol
2)在CCApplication中实现了协议的调用;
CCApplication::run()函数实现了win消息窗体创建及消息注册。
CCDirector::sharedDirector()->mainLoop();//后续分析
问题2:是谁调用了CCApplication::run()?
return CCApplication::sharedApplication()->run();
问题3:代码实践
23行,AppDelegate app;这句话能去掉吗?
28行,return CCApplication::sharedApplication()->run();换成app.run();?
问题4:应用程序对象是什么时候被创建的
CCApplication * CCApplication::sm_pSharedApplication = 0;
CCApplication::CCApplication()
: m_hInstance(NULL)
, m_hAccelTable(NULL)
{
m_hInstance = GetModuleHandle(NULL);
m_nAnimationInterval.QuadPart = 0;
CC_ASSERT(! sm_pSharedApplication);
sm_pSharedApplication = this;
}
2、Win32编程入门
问题1:cocos2d-x应用程序如何创建win窗口?
GetMessage: 从线程的消息队列取出一个消息 ;
TranslateMessage: 将msg结构传给Windows,进行一些转换,比如A键按下,转换成WM_CHAR消息等
DispatchMessage():再将msg结构传给Windows,Windows将该消息发给窗口过程,由窗口过程处理.
GetMessage是从系统为每个应用程序自动分配的消息对列的头部得到一个消息。
TranslateMessage是对一些键盘事件做预处理。
TranslateMessage是翻译需要翻译的消息
DispatchMessage()则会把翻译好的消息发送到系统的消息处理函数中,而这个函数又会把这个消息传递到注册窗体时用户指定的消息处理函数中。翻译消息不是简单的转换,一个消息被翻译后,可能会产生几个消息。
实战:
1) 新建项目、win32项目、填写名称及选择项目存储路径;根据 Win32 应用程序向导(windows应用程序),进行项目创建。分析源代码。
2) 注意:项目属性、不适unicode字符集;
3)程序进入点WinMain、窗口类注册与窗口诞生、启动消息循环、回调窗口过程。
请仔细分析;点击鼠标或键盘,win处理流程
1、 点击鼠标、鼠标发电信号,给鼠标驱动;
2、 鼠标驱动通过中断,发给win内核;
3、 win内核产生消息队列,并通知响应win应用窗口程序
4、 win应用程序处理消息。对应代码,在winproc回到地方,进行代码处理。
5、 编写键盘点击事件。
以上流程请仔细分析哪些是win操作系统做的,哪些是win应用程序做的。
代码模型:
- int APIENTRY _tWinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPTSTR lpCmdLine,
- int nCmdShow)
- {
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpCmdLine);
-
- // TODO: 在此放置代码。
- MSG msg;
- HACCEL hAccelTable;
-
- // 初始化全局字符串
- LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
- LoadString(hInstance, IDC_WIN3202, szWindowClass, MAX_LOADSTRING);
- MyRegisterClass(hInstance);
-
- // 执行应用程序初始化:
- if (!InitInstance (hInstance, nCmdShow))
- {
- return FALSE;
- }
-
- hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN3202));
-
- // 主消息循环:
- while (GetMessage(&msg, NULL, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
-
- return (int) msg.wParam;
- }
-
- //
- // 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
- //
- // 目的: 处理主窗口的消息。
- //
- // WM_COMMAND - 处理应用程序菜单
- // WM_PAINT - 绘制主窗口
- // WM_DESTROY - 发送退出消息并返回
- //
- //
- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- int wmId, wmEvent;
- PAINTSTRUCT ps;
- HDC hdc;
-
- switch (message)
- {
- case WM_COMMAND:
- wmId = LOWORD(wParam);
- wmEvent = HIWORD(wParam);
- // 分析菜单选择:
- switch (wmId)
- {
- case IDM_ABOUT:
- DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
- break;
- case IDM_EXIT:
- DestroyWindow(hWnd);
- break;
-
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- break;
- case WM_PAINT:
- hdc = BeginPaint(hWnd, &ps);
- // TODO: 在此添加任意绘图代码...
- EndPaint(hWnd, &ps);
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
-
- case WM_KEYDOWN:
- MessageBox(hWnd, "bombing lptest", "bombing caption", 0);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- return 0;
- }
问题1:win平台下,cocos2d-x框架源码中,那个类完成了win窗口类注册;
问题2:win平台下,cocos2d-x框架源码中,那个类完成win窗口消息循环;
问题3:win平台下,cocos2d-x框架源码中,那个类完成win窗口过程回调函数处理。
过程如下:
1)第24行CCEGLView* eglView = CCEGLView::sharedOpenGLView();
该函数会调用步骤2)
2) bool CCEGLView::Create()函数,完成win窗口类注册;其中279、299行为关键代码。
其中,CCEGLView::Create()会调用 CheckTouchSupport()函数,该函数动态加载系统函数,是应用程序加载动态库的重要方法之一,注意学习。
3) CCApplication类run()完成win消息循环接受处理。代码如下:
4)CCEGLView类的WindowProc函数,完成win窗口过程回调实现。函数运行如下:
LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)。该函数有众多关键性代码。
触屏回调、鼠标按键、键盘按键、应用程序后台处理等均有对应处理流程。
综上:
1)在main.pp程序入口_tWinMain()函数中,执行CCEGLView类对象初始化过程中,创建了win窗口类,然后注册窗口类,并注册消息回到函数WindowProc;
2)CCApplication的run函数进行消息循环处理。
至此:cocos2d-x win应用程序创建流程分析完毕。
4、结论
1、从程序员的角度理解框架,框架最低要求
1)框架定义了一些协议(功能的抽象);目的:解耦合,通过函数指针实现。
2)框架实现了这层协议的调用,框架具有集成功能。
3)上层应用按照要求,去实现框架协议。
2、cocos2d-x框架,基本上都可以分为两部分:
1. 一个入口主类,它定义了整个应用程序的生命周期,并提供一些全局的资源
2. 一些绘制到屏幕上的“页面”控件。
3、CCApplication类主要做的事
1)控制应用程序的生命周期
2)提供和管理一些全局的资源
3)循环绘制界面CCApplication::run()
应用程序的生命周期有一下几个虚方法:
bool initInstance();做一些简单全局变量的初始化工作
applicationDidFinishLaunching();资源加载完成之后发生
applicationDidEnterBackground();程序进入后台被挂起
applicationWillEnterForeground();程序从后台被唤醒
4、CCEGLView类主要完成的事情
1)openGL显示引擎初始化、销毁;
2)win窗口消息消息回到函数处理,调用。
3)frame缩放设置、屏幕适配ResolutionPolicy方案的框架搭建
总的来说,就是视图的显示管理了。
5、阅读开源源码是有方法的。
作为一个cocos2d-x程序员,从书上获得CCApplication类有什么功能,没有用;必须从代码中读出CCApplication类有什么功能才是你的。
后续章节进行,引擎显示部分模块的剖析。
cocos2d-x核心类剖析
1 cocos2d-x之CCDirector类1
问题2-1:CCDirector类是什么时候创建?
问题2-2:cocos2d-x应用窗口的消息回调函数是如何和CCDirector类创建连接起来的?
1、复习上一节:
创建AppDelegate对象后,全局唯一AppDelegate对象会执行run函数进行消息循环处理;创建CCEGLView对象后,该全局唯一CCEGLVie对象会处理win消息回调,也就是LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)。若不懂请回看第一节。
2、创建窗口对象后,第一次激活窗口显示的时,CCEGLView::WindowProc函数会做相应处理,关键代码就是444行:
CCApplication::sharedApplication()->applicationWillEnterForeground();
典型分析如下:
1)444行这块代码是典型的多态,用父类指针执行子类对象。
2)没有C++基础的同学,请思考,多态实现的三个条件,不管是C还是Java都一样。
(1有继承,2要虚函数重写,3父类指针指向子类对象),
3)多态的结果就是,从父类CCApplication,去执行子类AppDelegate::applicationWillEnterForeground()函数。
4)AppDelegate::applicationWillEnterForeground()函数会执行,CCDirector::sharedDirector()->startAnimation();就会被调用。
5)CCDirector::sharedDirector()就是这句话,第一次调用,开始产生CCDirector类的对象。
我们再看该函数内做了什么。
注意一点细节的是,创建CCDisplayLinkDirector类对象,继承关系如下:
class CCDisplayLinkDirector : public CCDirector。其中CCDisplayLinkDirector类重写了以下几个方法
virtual void mainLoop(void);
virtual void setAnimationInterval(double dValue);
virtual void startAnimation(void);
virtual void stopAnimation()。
6)我们再看s_SharedDirector->init()函数做了什么。
狐狸的尾巴终于露出来了。就是在这个在bool CCDirector::init(void)函数里面,cocos2d-x框架初始化了各种各样的器件。提醒你,一种典型的组合设计方法清晰可见、敬佩之情悠然而生。
在这里你仔细看看吧。最值得你看的地方。
// scheduler cocos2d-x中的调度器
m_pScheduler = new CCScheduler();
// action manager cocos2d-x中的动作管理器
m_pActionManager = new CCActionManager();
// touchDispatcher cocos2d-x中的触屏管理器
m_pTouchDispatcher = new CCTouchDispatcher();
// KeypadDispatcher pad派发器
m_pKeypadDispatcher = new CCKeypadDispatcher();
// create autorelease pool 内存自动清理管理器
CCPoolManager::sharedPoolManager()->push();
很多公司就是在这个地方进行cocos2d-x框架源码功能增强。比如:底层数据的加密;硬件设备调度等。
2 cocos2d-x之CCDirector类2
CCDirector与CCEGLView、AppDelegate产生关系图
cocos2d-x框架创应用类、视图类、导演类顺序示意图如下:
从图中可以总结如下:
1) cocos2d-x框架创建AppDelegate单例对象;
2) cocos2d-x框架创建CCEGLView单例对象,注册win窗口类和win窗口过程回调函数
3) cocos2d-x框架在CCEGLView类的win窗口过程回调函数中(处理窗口第一次显示消息),创建了CCDirector类对象。并初始化了CCDirector类各种管理功能(包括调度器、动作管理器、内存释放池等)。
4) cocos2d-x框架的AppDelegate的run函数,调用applicationDidFinishLaunching()入口,完成界面元素的创建,包括创建场景、初始化场景各个元素。
5) cocos2d-x框架的AppDelegate的run函数,按照默认帧频率循环调用CCDirector::sharedDirector()->mainLoop(),不断地显示win窗口。
6) 至此这个应用程序的初始化流程完毕。
3cocos2d-x内存管理机制
目标:搞清楚coco2d-x内存机制是如何管理精灵内存资源。
1、内存管理机制的基本概念及应用
1、 基础知识
cocos2dx采用的是引用计数的方式来管理对象的持有和释放。
所谓引用计数就是说,每个对象都会有一个属性用来记录当前被几个地方引用了。在释放内存的时候会根据这个引用计数来确定是否要用delete操作符来释放这个对象占用的内存。具体见CCObeject的默认构造函数,retain和release方法。
2实验:helloworld项目中
helloworld类.h中声明一个CCSprite *pSprite成员变量
1) helloworld类的
bool HelloWorld::init()
{
… … //省略代码
//创建一个精灵,
pSprite = CCSprite::create("CloseSelected.png");
pSprite->setPosition(ccp(200, 200));
//this->addChild(pSprite, 0, 99); 注意不addChild
… … //省略代码
return true;
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
CCLOG("getPosition begin");
CCPoint point = pSprite->getPosition();
CCLOG("getPosition end");
… … //省略代码
}
单击关闭按钮,程序会coredump。
3分析原因:
1) 加上这一句再做实验,程序正常,this->addChild(pSprite, 0, 99);
结论:
1)addChild函数认领了对象。让对象交给cocos2d-x内存管理机制去管理。
2)addchild函数里面调用了retain()方法,让对象引用次数+1;垃圾回收时,再-1,不会是零,所以精灵对象不会被delete。
4 内存管理机制的应用
应用层面结论:
1) Retain()函数:将引用计数加1;
2) Release()函数:将引用计数减1,如果引用计数为0,delete本对象。
3) autorelease()函数,将本对象加入内存管理池中,并将对象引用计数m_uReference为1,m_uAutoReleaseCount为1.
2、内存管理机制的深入理解
内存管理对象有两种方法:手工申请、手工释放;交给cocos2d-x内存管理机制
问题1:new一个从CCObject类继承的对象发生了什么?
结论:m_uReference=1;m_uAutoReleaseCount=0
问题2:手工创建object对象后,object析构函数做了什么?
测试:HelloWorld项目中,运行run1函数
结论:手工管理,手工释放
问题3:把对象执行autorelease函数,交给cocos2d-x内存管理机制,cocos2d-x做了什么?
分析如下:
问题3:为什么创建精灵以后不用释放,没有被删除?
问题4:精灵什么时候被删除
大家也参考《cocos2d-x高级开发教程》2.3.3节,书中介绍了CCPoolManager管理、CCAutoreleasePool的方法。篇幅原因,暂不赘述。
结论:
1) AddChild函数做了reatain操作保证了,保证精灵不会被垃圾器删除
2) 我们创建了精灵对象、或者CCObject对象,如果进行了autorelease操作,如果没有把这个节点添加到父节点(形象比喻:没有认领的话,),若跨多帧保存精灵对象,需要执行retain操作。
3) 对于精灵这样的节点对象,建议使用cocos2d-x内存管理机制。
OK 今天先到这里 ,下一章,我们来分析Cocos2d-x定时器机制等其他内容...
By:Net Fly