Cocos2Dx应用由导演(CCDirector)、场景(CCSprite)、层(cclayer)和精灵(CCSprite)构成。导演负责管理整个游戏。映射到 *** 作系统,每个 *** 作系统需要的应用入口都不一样。可以看到CCApplication是平台相关的,每个 *** 作系统平台都有自己的CCApplication实现 ,具体代码放在platform下面。为了对开发者透明,Cocos2Dx给出了AppDelegate,一个AppDelegate就是继承自CCApplication的一个代表该应用的代理者。Cocos2Dx提供了Python脚本帮我我们生成所有平台的项目文件,自己不用手动去选择设置合适的CCApplication。
AppDelegate一般只是暴露CCApplicationProtocol的几个重载函数给开发者。
?1 2 3 4 5 6 7 8 9 | class AppDelegate: private cocos2d::CCApplication @H_301_45@ { public : AppDelegate(); virtual ~AppDelegate(); virtual bool applicationDIDFinishLaunching(); virtual voID applicationDIDEnterBackground(); applicationWillEnterForeground(); }; |
我们只需要给出applicationDIDFinishLaunching、applicationDIDEnterBackground和applicationWillEnterForeground的实现即可。暴露给开发者的应用入口,看不到 *** 作系统的细节,封装得很好。
在AppDelegate::applicationDIDFinishLaunching()中,我们会创建导演和场景,然后调用导演的runWithScene函数。
9 10 11 12 13 14AppDelegate::applicationDIDFinishLaunching()
@H_301_45@ {
CCDirector*pDirector=CCDirector::sharedDirector();
pDirector->setopenGLVIEw(CCEGLVIEw::sharedOpenGLVIEw());
CCSizescreenSize=CCEGLVIEw::sharedOpenGLVIEw()->getFrameSize();
CCSizedesignSize=CCSizeMake(480,320);
CCEGLVIEw::sharedOpenGLVIEw()->setDesignResolutionSize(designSize.wIDth,designSize.height,kResolutionNoborder);
CCScene*pScene=CCScene::create();
cclayer*pLayer=
new
TestController();
pLayer->autorelease();
//将cclayer添加到自动回收池当中,但并不会立即释放。随后的addChild会调用cclayer的retain函数,增加一次饮用计数
pScene->addChild(pLayer);
pDirector->runWithScene(pScene);
return
true
;
}
我们创建好了导演,如何跟 *** 作系统的入口函数关联起来呢?同样以WIN32为例。
10APIENTRY_tWinMain(
HINSTANCE
hInstance,
hPrevInstance,monospace!important; color:grey!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-weight:bold!important; Font-size:1em!important; min-height:auto!important; background:none!important">LPTSTR
lpCmdline,monospace!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">nCmdshow)
@H_301_45@ UNREFERENCED_ParaMETER(hPrevInstance);
UNREFERENCED_ParaMETER(lpCmdline);
AppDelegateapp;
CCEGLVIEw*eglVIEw=CCEGLVIEw::sharedOpenGLVIEw();
eglVIEw->setVIEwname(
"TestCpp"
);
eglVIEw->setFrameSize(960,640);
return
CCApplication::sharedApplication()->run();
在 *** 作系统的入口函数,我们创建了AppDelegate对象。此时为调用AppDelegate的构造函数,其为空,进一步调用基类CCApplication的构造函数,也只是简单初始化一些成员,没有其他动作。CCEGLVIEw::sharedOpenGLVIEw()构造一个窗口,如果你了解WIN32 API,那么CCEGLVIEw的Create函数看着就非常熟悉。首先创建一个窗口样式WNDCLASS,然后创建一个窗口。窗口创建完毕后,调用initGL()初始化OpenGL。最后,检查是否支持触控,如果支持,调用user32.dll里的RegistertouchWindow函数将刚刚创建的窗口进行注册, *** 作系统将触控事件发送给我们创建的窗口。这个窗口后面会作为游戏界面的容器,我们的游戏也就能够接收到 *** 作系统发送过来的触控消息了。 14 15 16 17 18 19 @H_419_265@ 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @H_295_301@ 38 39 40 41 42 43 44 45 46 47 48 49 50
bRet=
false
;
do
{
CC_BREAK_IF(m_hWnd);
hInstance=GetModuleHandle(NulL);
WNDCLASSwc;
//windowsClassstructure
wc.style=CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
wc.lpfnWndProc=_WindowProc;
//WndProcHandlesMessages
wc.cbClsExtra=0;
//NoExtraWindowData
wc.cbWndExtra=0;
//NoExtraWindowData
wc.hInstance=hInstance;
//SetTheInstance
wc.hIcon=LoadIcon(NulL,IDI_WINlogo);
//LoadTheDefaultIcon
wc.hCursor=LoadCursor(NulL,IDC_ARROW);
//LoadTheArrowPointer
@H_984_403@ wc.hbrBackground=NulL;
//NoBackgroundrequiredForGL
wc.lpszMenuname=m_menu;
//
wc.lpszClassname=kWindowClassname;
//SetTheClassname
CC_BREAK_IF(!RegisterClass(&wc)&&1410!=GetLastError());
RECTrcDesktop;
GetwindowRect(GetDesktopWindow(),&rcDesktop);
WCHAR
wszBuf[50]={0};
MultiBytetoWIDeChar(CP_UTF8,m_szVIEwname,-1,wszBuf,
sizeof
(wszBuf));
m_hWnd=CreateWindowEx(
WS_EX_APPWINDOW|WS_EX_WINDOWEDGE,
//ExtendedStyleForTheWindow
kWindowClassname,0)!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">//Classname
wszBuf,0)!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">//WindowTitle
WS_CAPTION|WS_POPUPWINDOW|WS_MINIMIZEBox,0)!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">//definedwindowstyle
0,0)!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">//Windowposition
//Todo:InitializingwIDthwithalargevaluetoavoIDgettingawrongclIEntareaby'GetClIEntRect'function.
1000,0)!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">//WindowWIDth
//WindowHeight
NulL,0)!important; border:0px!important; bottom:auto!important; float:none!important; height:auto!important; left:auto!important; line-height:1.1em!important; outline:0px!important; overflow:visible!important; position:static!important; right:auto!important; top:auto!important; vertical-align:baseline!important; wIDth:auto!important; Font-size:1em!important; min-height:auto!important; background:none!important">//noparentwindow
//NoMenu
//Instance
NulL);
CC_BREAK_IF(!m_hWnd);
bRet=initGL();
if
(!bRet)destroyGL();
CC_BREAK_IF(!bRet);
s_pMainWindow=
this
;
;
}
while
(0);
m_bSupporttouch=ChecktouchSupport();
(m_bSupporttouch)
{
m_bSupporttouch=(s_pfRegistertouchWindowFunction(m_hWnd,0)!=0);
}
bRet;
在WIN 32入口函数_tWinMain的最后,我们调用CCApplication::sharedApplication()->run(),实际上是调用的是cocos2dx\platform\win32\CCApplication.cpp的run函数。 39
//在注册表里设置PVRFrame的快捷键。PVRFrame是由ImaginationTechnologIEs开发的一套模拟OpenGLES的环境。
//http://community.imgtec.com/developers/powervr/tools/pvrvframe/
PVRFrameEnableControlWindow(
);
//目的是获取精确时间
queryPerformanceFrequency(&nFreq);
queryPerformanceCounter(&nLast);
//调用AppDelegate的applicationDIDFinishLaunching回调函数创建好了当前游戏场景
(!applicationDIDFinishLaunching())
{
0;
}
//游戏场景已经设置了,可以显示窗口
CCEGLVIEw*pMainWnd=CCEGLVIEw::sharedOpenGLVIEw();
@H_984_403@ pMainWnd->centerWindow();
ShowWindow(pMainWnd->getHWnd(),SW_SHOW);
//主循环
(1)
{
(!PeekMessage(&msg,NulL,PM_REMOVE))
{
//获取当前时间
queryPerformanceCounter(&nNow);
//达到了指定的时间间隔,绘制下一帧,否则放弃cpu
(nNow.QuadPart-nLast.QuadPart>m_nAnimationInterval.QuadPart)
{
nLast.QuadPart=nNow.QuadPart;
CCDirector::sharedDirector()->mainLoop();
}
else
{
Sleep(0);
}
continue
;
}
}
(
)msg.wParam;
queryPerformanceFrequency(&nFreq)和queryPerformanceCounter(&nLast)目的是获取精确的时间间隔。在定时前先调用queryPerformanceFrequency()函数获得机器内部计时器的时钟频率。接着在需要严格计时的事件发生前和发生之后分别调用queryPerformanceCounter(),利用两次获得的计数之差和时钟频率,就可以计算出事件经历的精确时间。数据类型LARGE_INTEGER既可以是一个作为8字节长的整数,也可以是作为两个4字节长的整数的联合结构,其具体用法根据编译器是否支持64位而定。这里使用的是64位的QuadPart。 setAnimationInterval使用的单位是秒,内部通过Tick来做的判断,所以setAnimationInterval内部将传入的帧间隔乘以了始终频率。
nNow存放着当前的Tick,nLast存放着前一帧绘制的时间,通过检查是否过了我们设定的帧间隔时间等价的Tick来决定是否绘制下一帧。如果需要绘制,调用CCdisplaylinkDirector::mainLoop()。这里已经到了所有平台通用的代码了。
为了比较,我们再看看AndroID的应用入口。代码位于cocos2dx\platform\androID\CCApplication.cpp。AndroID使用的CCApplication很简单,直接就去调用AppDelegate的applicationDIDFinishLaunching回调函数创建好了当前游戏场景。因为AndroID的窗口创建是通过Java Cocos2dxActivity实现的。
但AndroID怎么走到CCDirector::sharedDirector()->mainLoop()呢?毕竟mainLoop()才是Cocos2Dx的主循环。CCApplication的run()实现只是调用了applicationDIDFinishLaunching。我们反过来看,首先看AndroID上谁调用了CCDirector::sharedDirector()->mainLoop():
cocos2dx\platform\androID\jni&IExcl;¢Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp
3
JNIEXPORT
JNICALLJava_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(jnienv*env){
@H_301_45@ cocos2d::CCDirector::sharedDirector()->mainLoop();
Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender是一个本地方法,从名字上,可以看出它映射到的Java方法是:Cocos2dxRenderer的private static native voID nativeRender()。这个Java方法会在Cocos2dxRenderer的voID onDrawFrame(final GL10 gl)中被唯一地调用。 onDrawFrame是androID.opengl.GLSurfaceVIEw.Renderer提供了一个专供覆盖的方法。用来为GLSurfaceVIEw提供渲染所需的Render。
GLSurfaceVIEw是一个很好的基类对于构建一个使用OpenGL ES进行部分或全部渲染的应用程序。它可以帮助我们更容易地使用OpenGL ES渲染你的应用程序。它提供了粘合代码把OpenGL ES连接到你的视图系统,也提供粘合代码使得OpenGL ES按照Acticity(活动)的生命周期工作,它创建和管理一个独立的渲染线程,产生平滑的动画。 在AndroID上开发动画,主要也是提供一个自己的继承自androID.opengl.GLSurfaceVIEw.Renderer的Render。
androID.opengl.GLSurfaceVIEw.Renderer还有两个方法:
onSurfaceCreated() :在开始渲染的时候被调用
onSurfaceChanged():该方法在Surface大小改变时被调用
onSurfaceChanged在Cocos2Dx中什么也不需要做。onSurfaceCreated会调用Cocos2dxRenderer.nativeInit(this.mScreenWIDth,this.mScreenHeight),对应的C++原生代码一般都是App自己提供的实现。比如:
samples\Cpp\HelloCpp\proj.androID\jni\hellocpp\main.cpp
19
(!CCDirector::sharedDirector()->getopenGLVIEw())
{
CCEGLVIEw*vIEw=CCEGLVIEw::sharedOpenGLVIEw();
vIEw->setFrameSize(w,h);
AppDelegate*pAppDelegate=
AppDelegate();
CCApplication::sharedApplication()->run();
}
else
ccGlinvalIDateStateCache();
CCshadercache::sharedshadercache()->reloadDefaultShaders();
ccDrawInit();
CCTextureCache::reloadAllTextures();
@H_984_403@ CCNotificationCenter::sharednotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND,NulL);
CCDirector::sharedDirector()->setGLDefaultValues();
}
AndroID进一步的细节已经涉及到AndroID内部实现。这里就不进一步讨论了。 回到CCdisplaylinkDirector::mainLoop()。由于CCDirector只有CCdisplaylinkDirector一个子类,所以代码中使用CCDirector::mainLoop()的地方就是CCdisplaylinkDirector::mainLoop()。
13
)
@H_301_45@ (m_bPurgeDirecotorInNextLoop)
m_bPurgeDirecotorInNextLoop=
;
purgeDirector();
}
else
(!m_bInvalID)
{
drawScene();
CCPoolManager::sharedPoolManager()->pop();
}
如果程序调用了CCDirector的end()函数,设置m_bPurgeDirecotorInNextLoop为true,意味需要进行CCDirector的清理工作了。end()并不会自己做清理工作。设置m_bPurgeDirecotorInNextLoop后,Cocos2Dx还是会等到帧间隔时间到期以后,才真正地去做清理工作。 如果程序并没有调用end(),还在继续运行,每当帧间隔时间到期以后,调用drawScene()绘制新的场景。随后,做一次内存回收池的释放。
40
//计算上次drawScene到现在的时间变化dt,dt会传给调度器
calculateDeltaTime();
(!m_bPaused)
{
m_pScheduler->update(m_fDeltaTime);
}
//清除当前缓冲区的颜色缓冲和深度缓冲
glClear(GL_color_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
(m_pNextScene)
{
setNextScene();
}
kmGLPushmatrix();
@H_984_403@ //绘制当前的场景
(m_pRunningScene)
{
m_pRunningScene->visit();
}
//通知setNotificationNode注册的CCNode
(m_pNotificationNode)
{
m_pNotificationNode->visit();
}
(m_bdisplayStats)
showStats();
}
kmGLPopMatrix();
m_uTotalFrames++;
(m_pobOpenGLVIEw)
{
m_pobOpenGLVIEw->swapBuffers();
}
(m_bdisplayStats)
{
calculateMPF();
}
drawScene会调用CCScene的visit()方法。CCScene使用的是基类CCNode的visit()方法。 50 51 52 53 54 55 56 57 58
//如果当前Scene不可见,直接返回,它的子节点也同样不会被绘制
(!m_bVisible)
;
kmGLPushmatrix();
(m_pGrID&&m_pGrID->isActive())
{
m_pGrID->beforeDraw();
}
->transform();
CCNode*pNode=NulL;
unsigned
i=0;
@H_984_403@ (m_pChildren&&m_pChildren->count()>0)
{
//对当前Scene的所有子节点进行排序
sortAllChildren();
//先绘制zOrder<0的子节点
ccArray*arrayData=m_pChildren->data;
for
(;i<arrayData->num;i++)
pNode=(CCNode*)arrayData->arr[i];
(pNode&&pNode->m_nZOrder<0)
{
//绘制子节点,可以是CCScene,CCSprite,cclayer
pNode->visit();
else
{
break
;
}
//绘制本Scene
->draw();
//最后绘制zOrder>=0的子节点
(;i<arrayData->num;i++)
{
pNode=(CCNode*)arrayData->arr[i];
(pNode)
{
pNode->visit();
}
}
}
else
{
//绘制本Scene
->draw();
}
m_uOrderOfArrival=0;
(m_pGrID&&m_pGrID->isActive())
{
m_pGrID->afterDraw(
);
}
kmGLPopMatrix();
CCNode::visit()通过zOrder来绘制自身和放在当前Scene里面的子节点。zOrder 越大,绘制出来的结果越在上层。子节点的绘制交给子节点自己处理,做了很好的隔离。visit()只是CCNode的访问入口,真正的绘制是CCNode的draw()完成的。CCNode的不同子类,比如CCSprite,cclayer都有自己的实现,但CCScene默认使用的是CCNode的空实现。 到现在为止,我们已经能够看到一个游戏的界面了。
总结 以上是内存溢出为你收集整理的Cocos2Dx之游戏启动过程-欧阳左至全部内容,希望文章能够帮你解决Cocos2Dx之游戏启动过程-欧阳左至所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
赞
(0)
打赏
微信扫一扫
支付宝扫一扫
Cocos2D瓦块地图高清屏(retina)显示比例问题的解决
上一篇
2022-05-26
Cocos2Dx之动画-欧阳左至
下一篇
2022-05-26
评论列表(0条)