Cocos2d-TestCpp之项目结构分析

        Cocos2d-x虽然很火,但是相关的学习资料还是很少的。在看完《Cocos2d-x权威指南》的基础内容之后,我跟着学习过“老G的博客”,也看过一些“地球人也阻止不了程序猿们学习Cocos2d-x了”之类的帖子。总感觉介绍和讲解的内容是零零散散的。这非常不利于对于Cocos2d-x引擎的整体理解和掌握。(求大爷们不要灭我,只是个人观点额)

        我相信,那些写帖子的同学们、老师们…大神们,都有自己的学习方法,必定不是通过看帖子来学习新东西的。

        好了,不废话了。希望和大家一起学习,一起进步。为了给自己鼓劲,在此也为自己附上一句话。

        有梦想,并坚持,人生就有希望。     

                                                                —— 北大校长


——————————————————————-美丽的分割线——————————————————————-


任何一个程序都有一个开始执行的入口,这个入口通常叫做“main”。那么Cocos2d-x在Win32平台下的入口是int APIENTRY _tWinMain:

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // create the application instance
    AppDelegate app;
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setViewName("TestCpp");
    eglView->setFrameSize(480, 320);
    return CCApplication::sharedApplication()->run();
}

AppDelegate:

        程序源码中的注释:The reason for implement as private inheritance is to hide some interface call by CCDirector。(这句话如何理解,看各位自己的功力了,我就不做坑爹的翻译了^ ^)

包含三个方法:

1、virtual bool applicationDidFinishLaunching();

        应用程序相关资源加载完成后执行此方法。可以简单的认为,这个就是程序的初始化函数。

        功能:

                (1)初始化应用程序中唯一的CCDirector导演。

                (2)设置OpenGL的相关参数等。

                (3)创建场景,创建布景层,并将布景层放入场景中,作为子节点。

                (4)pDirector->runWithScene(pScene);  设置运行的第一个场景。

2、void AppDelegate::applicationDidEnterBackground()

        原文中的注释:This function will be called when the app is inactive。表示应用程序当前状态是“不活跃”(来电话)时,这个方法将被执行。

        功能:

                (1)停止游戏当中的动画。

                (2)暂停游戏的背景音乐。

                (3)暂停游戏的所有音效。

3、void AppDelegate::applicationWillEnterForeground()

        应用程序从后台再次被唤醒时执行此方法。

        功能:恢复动画、背景音乐、所有音效。


CCEGLView:

        由于Cocos2d-x是基于OpenGL ES图形引擎的,所有在图形绘制方面所遵守的规则都是OpenGL的规则,比如坐标系统。

CCEGLView* eglView = CCEGLView::sharedOpenGLView();
        上面的代码用来获得OpenGL的视图。关于图形学的视图这个概念可以在网上搜搜。当然,暂时还没太多必要。就把这理解为“获得应用程序的画面”吧。

        那么有了视图这个对象指针后,我们就可以设置这个视图的名称和大小。

CCApplication::sharedApplication()->run();

        应用程序相关的参数设置完成后,就可以调用run()方法,进入应用程序的消息循环。  

   

——————————————————————-美丽的分割线——————————————————————-


        马上进入正题了,这里非常关键,因此我也会非常小心的分析,免得误人子弟,当然,还有自己~奋斗

        学习Cocos2d-x的各种特性,莫过于学习TestCpp项目了,这是公开的“秘密”。开始我学习这个TestCpp的时候头昏脑胀的,一点头绪没有,因为程序的结构对于我这样的新人来说太复杂,更别说是用C++编写的程序….可恶的宏啊、可恶的高级特性啊抓狂。完全没心思找那些示例代码了,因为和这个程序结构是紧紧相连的啊。

        但如论怎么说,这个是学习Cocos2d-x最好的资料了,目前还没发现比这个更好的。

        既然这样,那就下定决心搞定它吧~!


        OK,先来看看项目的整体文件。

        文件列表:main.cpp、main.h、AppDelegate.h/AppDelegate.cpp、tests.h、testResource.h、VisibleRect.h/VisbleRect.cpp

                            testBasic.h/testBasic.cpp、controller.h/controller.cpp

         除了上面的文件,Classes包(文件夹)中还有许多继承于CCLayer、CCScene的类,那些都是示例子场景示例子布景层

        一一击破:

                main.h/main.cpp和AppDelegate.h/AppDelegate.cpp 在前面我们已经搞定了。

                tests.h:#include所有Classes包中的头文件、一些字符串、枚举的标记变量等。(C/C++头文件,通过包含的方法,就可以使用头文件内的资源)

                testResource.h:图片路径的字符串等。

                VisibleRect.h/VisbleRect.cpp:作者封装的一个管理坐标的类,可以直接让显示对象居中、靠左、靠右对齐等。(可以Copy下来,以后自己用)

                testBasic.h/testBasic.cpp:TestScene,继承于CCScene场景类,是所有示例子场景的爹。

                controller.h/controller.cpp:TestController,继承于CCLayer布景层类,是被应用程序第一个创建的布景层,里面有一个CCMenu菜单(主菜单)。

         到此,一切文件我们都熟悉了。现在我们深入的调查一下,这个示例程序到底是如何运行这么多东东的。


CCApplication::sharedApplication()->run()

        main.cpp中,上面的代码执行完之后就进入了消息循环,程序就正常运行了。

        当程序相关资源加载和设置完成之后就开始执行AppDelegate类中的applicationDidFinishLaunching()方法。

bool AppDelegate::applicationDidFinishLaunching()
{
    // As an example, load config file
    // XXX: This should be loaded before the Director is initialized,
    // XXX: but at this point, the director is already initialized
    CCConfiguration::sharedConfiguration()->loadConfigFile("configs/config-example.plist");

    // 获得导演实例
    CCDirector *pDirector = CCDirector::sharedDirector();
    // 设置OpenGL视图
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
    // 获取屏幕的分辨率(在Win32下,屏幕的大小是自己设置的,在main.cpp中看到eglView->setFrameSize(480, 320);)
    CCSize screenSize = CCEGLView::sharedOpenGLView()->getFrameSize();
    // 理想的分辨率
    CCSize designSize = CCSizeMake(480, 320);

    // CCFileUtils文件操作类
    CCFileUtils* pFileUtils = CCFileUtils::sharedFileUtils();
    // 如果屏幕的高度大于理想的高度,执行if内的代码
    if (screenSize.height > 320)
    {
        CCSize resourceSize = CCSizeMake(960, 640);
        std::vector<std::string> searchPaths;
        searchPaths.push_back("hd");                  
        pFileUtils->setSearchPaths(searchPaths);         

        pDirector->setContentScaleFactor(resourceSize.height/designSize.height); 
    }
    // 将视图设置为理想的分辨率。
    CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);
    // 创建一个场景和一个布景层
    CCScene * pScene = CCScene::create();
    CCLayer * pLayer = new TestController();
    // 设置后,自动管理对象的释放,不用手动释放。

    pLayer->autorelease();
    // 将布景层添加到场景中
    pScene->addChild(pLayer);
    // 运行场景
    pDirector->runWithScene(pScene);

    return true;
}

        分辨率的概念:移动平台设备的分辨率总是不统一的,什么大小的分辨率都有,因此Coco有一个处理办法,那就是使用缩放方法。

                第一种解决思路,当设备的分辨率改变了,那么读取不同分辨率的资源。(需要准备各种分辨率的资源)

                第二种解决思路,直接缩放图片资源的大小,计算公式:X = 设备分辨率/设计分辨率,然后将图片缩放X倍。(这个比较坑爹)

                第三种,思考ing。

        在上面代码的if()结构中,是读取大分辨率资源(图片等)。TestCpp项目有两个资源文件夹,一个是images,一个是hd。我们可以找到工程的所在的文件夹,看到只有一部分的图片有两个版本的,而且hd文件夹中的资源,很多都在images里面没有。这样做,可能为了一些示例表现得更好才没有小分辨率的吧。暂时不管它。

pFileUtils->setSearchPaths(searchPaths); 
        本来程序里精灵加载的图片都是搜索默认Resources文件夹下的images文件夹。当if()结构触发之后,就会执行上面的这行代码,改变搜索路径,转到hd文件夹下搜索资源。
        我们也可以看到一个技巧,它这里设置的搜索资源的路径可以是多个,searchPaths是一个vector容器。

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);

        上面代码将设置应用程序分辨率,第三个参数决定了不同尺寸时画面显示的效果。

                kResolutionExactFit:不同尺寸时,画面被拉伸。
                kResolutionNoBorder:不同尺寸时,没有黑边,画面被裁减。
                kResolutionShowAll:不同尺寸时,有黑边,如果设计的分辨率和屏幕分辨率不同,将显示黑边。

                (三种效果的说明是从coco源码定义中的注释找到的,我只是把英文通俗的翻译过来了,大致也能想象到这三种都是什么效果,等实际开发的时候再确定)

    CCScene * pScene = CCScene::create();
    CCLayer * pLayer = new TestController();

        细心的童鞋会发现在TestController布景层中有加入了主菜单,和右下角的关闭按钮。

pLayer->autorelease();
        我们知道,C++中都是成套使用new/delete的,但是在coco中,有内存自动管理的机制,非常方便,不易出现内存泄露。

        在网上一些关于coco的内存管理分析,大神们好像都建议使用这个,除非有特殊情况。

        不用时,使用release或autorelease,将来要用时,使用retain。

pDirector->runWithScene(pScene);
       Coco 应用程序运行成功! 大笑

       那么童鞋们,最后我们震一下~看看大致的结构图。

       

——————————————————————-美丽的分割线——————————————————————-




                                                                                           图x.x        TestCpp中ActionTest示例结构图

       如果童鞋比较细心的话,可以发现我这个图其实是UML图。羡慕

       为什么是UML图?UML能很好的将程序结构表现出来。游戏编程除了要学习语言基础、游戏引擎,最重要的还是编码能力和游戏逻辑的思维。

       如有哪里绘制错误了,希望童鞋们能马上帮我指出来哭。好了,废话不讲了,简单分析下这个结构。

       

       TestController被创建时,构造函数中会创建一个CCMenu菜单,用来选择示例场景。在菜单的回调函数(菜单可以指定回调函数,通俗点讲,就是响应函数)中会确定调用哪一个示例场景并显示出来。代码如下:

TestController::TestController()
: m_tBeginPos(CCPointZero)
{
    // 右下角的关闭按钮
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(s_pPathClose, s_pPathClose, this, menu_selector(TestController::closeCallback) );
    CCMenu* pMenu =CCMenu::create(pCloseItem, NULL);
    // 设置关闭按钮的位置
    pMenu->setPosition( CCPointZero );
    pCloseItem->setPosition(ccp( VisibleRect::right().x-30, VisibleRect::center().y));

    // 创建主菜单,添加菜单项
    m_pItemMenu = CCMenu::create();
    for (int i = 0; i < TESTS_COUNT; ++i)
    {
        CCLabelTTF* label = CCLabelTTF::create(g_aTestNames[i].c_str(), "Arial", 24);     
        CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestController::menuCallback));
        m_pItemMenu->addChild(pMenuItem, i + 10000);
        pMenuItem->setPosition( ccp( VisibleRect::center().x, (VisibleRect::top().y - (i + 1) * LINE_SPACE) ));
    }
    // 设置菜单的固定大小,无论视图如何缩放。
    m_pItemMenu->setContentSize(CCSizeMake(VisibleRect::getVisibleRect().size.width, (TESTS_COUNT + 1) * (LINE_SPACE)));
    m_pItemMenu->setPosition(s_tCurPos);
    addChild(m_pItemMenu);
    // 允许触摸事件
    setTouchEnabled(true);

    addChild(pMenu, 1);

}

void TestController::menuCallback(CCObject * pSender)
{
    // get the userdata, it's the index of the menu item clicked
    CCMenuItem* pMenuItem = (CCMenuItem *)(pSender);
    int nIdx = pMenuItem->getZOrder() - 10000;

    // create the test scene and run it
    TestScene* pScene = CreateTestScene(nIdx);
    if (pScene)
    {
        pScene->runThisTest();
        pScene->release();
    }
}

       当在主场景(TestController)中选择一个菜单项时,就用上面的调用回调函数。pSender指向的就是被选择的菜单项。

       接着获取菜单项的z值,通过z值确定创建哪一个示例场景。(z值就是对象在容器对象里的前后的关系)可为什么是获取z值后减去了10000?

       我们找到创建菜单的地方(TestController的构造函数),有一个 m_pItemMenu->addChild(pMenuItem, i + 10000);   原来并不是从0开始的,而是从10000开始的。

       接着调用TestScene类中的静态方法 CreateTestScene(int nIdx),创建新场景。代码如下:

static TestScene* CreateTestScene(int nIdx)
{
    CCDirector::sharedDirector()->purgeCachedData();

    TestScene* pScene = NULL;

    switch (nIdx)
    {
    case TEST_ACTIONS:
        pScene = new ActionsTestScene(); break;
    case TEST_TRANSITIONS:
        pScene = new TransitionsTestScene(); break;
     case TEST_PROGRESS_ACTIONS:
         pScene = new ProgressActionsTestScene(); break;
    case TEST_EFFECTS:
        pScene = new EffectTestScene(); break;
    case TEST_CLICK_AND_MOVE:
        pScene = new ClickAndMoveTestScene(); break;
    ......
    return pScene;
 }

       接着调用场景的runThisTest()方法替换原有的场景,并运行。在Action示例中的代码如下:(其他示例场景也类似)

void ActionsTestScene::runThisTest()
{
    sceneIdx = -1;
    addChild(nextAction());
    // 通过导演的replaceScene方法改变场景
    CCDirector::sharedDirector()->replaceScene(this);
}

       回到UML图。每个示例子场景都继承于一个基类,都有共同的runThisTest()方法,但内部实现不同。(TestScene其实是一个抽象类,它包含了一个纯虚函数:virtual void runThisTest() = 0;。

       从主菜单的场景到示例的场景,切换就完成了。

       最后总结一下程序的执行过程。



       执行过程已经被我们搞定了,终于可以好好的去学习TestCpp中的示例了~!得意





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值