了解完Cocos2D-x的基本概念和概念类之后,是不是有一种蠢蠢欲动的冲动,想要探究Cocos2D-x是如何完成这一切的。接着我将通过对Cocos2D-x自代的HelloCpp项目进行分析,初步了解Cocos2D-x游戏的基本框架,揭开Cocos2D-x神秘的面纱。

 
   作为一个Hello World程序,HelloCpp的功能非常简单。打开一个OpenGL窗口,在里面显示了一张Cocos2D-x的log图片,在图片的上面写着"Hello World"。在右下角有一个按钮,用来退出程序。在下角显示了当前的帧率。如下图:
  
       HelloCpp项目提供了android、blackberry、ios、linux、mac和win32的工程,体现了Cocos2D-x的跨平台性。这里我们只针对win32工程。
  
       关于Cocos2D-x版本下载和环境配置非常简单,我这里就不再赘述。如有不明白的地方,可以上网搜索,有很多介绍这方面的文章。
 
       整个HelloCpp程序分为两个部分,这也是Cocos2D-x应用程序开发模板程序的共同结构。如下图:

   在Classes目录下,放着程序的主要部分,包括场景(CCScene)、布景(CCLayer)、精灵(CCSprite)等游戏元素相关的代码,而在每个工程的目录下(Win32是proj.win32)放着与平台相关的入口程序(proj.win32里就包含一个main.cpp/h)。
   先从入口开始,查看main.cpp代码,如下:
   int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
   {
      UNREFERENCED_PARAMETER(hPrevInstance);//这是一个编译宏,用于告诉编译器,忽略未使用变量而产生的告警信息。
      UNREFERENCED_PARAMETER(lpCmdLine);
      AppDelegate app;      // 创建一个Cocos2D-x程序实例
      CCEGLView* eglView = CCEGLView::sharedOpenGLView(); 
      eglView->setFrameSize(960, 640 ); //处理windows窗体注册和创建
      return CCApplication::sharedApplication()->run(); //运行程序实例
   }
      写过win32程序的人,一定对_tWinMain函数不会感到陌生,Cocos2D-x也是从这里作为入口开始的。这个函数简洁到极致,只有寥寥几行代码,甚至看不到Windows的窗口注册和消息机制。因为Cocos2D-x把很多Windows窗口相关程序代码封装到CCEGLView中,该类被统一放到Cocos2D-x的win32平台中(cocos2d-2.0-x-2.0.2\cocos2dx\platform\win32目录)。这样做的目的,无疑是为了方便跨平台。
     一般游戏程序都需要一个主循环,那Cocos2D-x的主循环在哪里呢?它就藏在CCApplication的成员函数run里,下面查一下处理代码:
int CCApplication::run()
{
    PVRFrameEnableControlWindow(false);
 
    // Main message loop:
    MSG msg;
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nLast;
    LARGE_INTEGER nNow;
 
    QueryPerformanceFrequency(&nFreq);
    QueryPerformanceCounter(&nLast);
 
    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())
    {
        return 0;
    }
 
    CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
    pMainWnd->centerWindow();
    ShowWindow(pMainWnd->getHWnd(), SW_SHOW);
 
    //主消息循环
    while (1)
    {
        if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // Get current time tick.
            QueryPerformanceCounter(&nNow);
 
            // If it's the time to draw next frame, draw it, else sleep a while.
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();            }
            else
            {
                Sleep(0);
            }
            continue;
        }
 
        if (WM_QUIT == msg.message)
        {
            // Quit message loop.
            break;
        }
 
        // Deal with windows message.
        if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
 
    return (int) msg.wParam;
}
     这段代码主要在处理windows消息机制,熟悉windows程序开发的应该很眼熟。我们重点关注CCDirector::sharedDirector()->mainLoop(),这行代码是Cocos2D-x的主循环处理,完成场景的渲染。
 
  下面深入每一个类进行探索,看一看Cocos2D-x的运行机制。
  首先要查看的类是AppDelegate类,头文件定义如下:
class  AppDelegate : private cocos2d::CCApplication
{
public:
    AppDelegate();
    virtual ~AppDelegate();
    virtual bool applicationDidFinishLaunching(); //程序启动时调用
    virtual void applicationDidEnterBackground(); //程序被切换到后台处于非激活时调用
    virtual void applicationWillEnterForeground();//程序被切换到前台处于激活状态时调用
};
  当我最初看到这个AppDelegate时,还以为使用类似C#中的代理机制。但查看头文件定义才知道,它是私有继承于CCApplication。而CCApplication就是一个App,AppDelegate可以理解为MyApp。这里采用私有继承的目的是隐藏接口。这里AppDelegate实现了父类CCApplication的三个虚函数。而这三个虚函数是CCApplication从CCApplicationProtocol接口继承而来,但它并未实现这些接口,所以不能直接创建CCApplication实例。
  applicationDidFinishLaunching函数非常关键中,因为它将加载Cocos2D-x的概念类和类之间的关联关系,代码如下:
    bool AppDelegate::applicationDidFinishLaunching() {
    CCDirector *pDirector = CCDirector::sharedDirector(); //这里得到一个导演
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
    TargetPlatform target = getTargetPlatform();
    if (target == kTargetIpad)
    {
        // ipad
       
        CCFileUtils::sharedFileUtils()->setResourceDirectory("iphonehd");
       
        // don't enable retina because we don't have ipad hd resource
        CCEGLView::sharedOpenGLView()->setDesignResolutionSize(960, 640, kResolutionNoBorder);
    }
    else if (target == kTargetIphone)
    {
        // iphone
       
        if (pDirector->enableRetinaDisplay(true))
        {
            // iphone hd
            CCFileUtils::sharedFileUtils()->setResourceDirectory("iphonehd");
        }
        else
        {
            CCFileUtils::sharedFileUtils()->setResourceDirectory("iphone");
        }
    }
    else
    {
        // android, windows, blackberry, linux or mac
        // use 960*640 resources as design resolution size
        CCFileUtils::sharedFileUtils()->setResourceDirectory("iphonehd");
        CCEGLView::sharedOpenGLView()->setDesignResolutionSize(960, 640, kResolutionNoBorder);
    }
 
    // turn on display FPS
    pDirector->setDisplayStats(true);
    // set FPS. the default value is 1.0/60 if you don't call this
    pDirector->setAnimationInterval(1.0 / 60);
    // create a scene. it's an autorelease object
    CCScene *pScene = HelloWorld::scene(); //这里得到一个场景
    // run
    pDirector->runWithScene(pScene); //导演启动场景渲染
    return true;
}
     当程序启动后,applicationDidFinishLaunching函数将会被调用,函数内将获得一个导演和创建一个场景,然后导演调用runWithScene函数渲染该场景。
     这里只有导演和场景,还少了布景层和精灵。它们在哪里创建的呢?一切奥秘就藏在HelloWorld里,在这里将解答我们的疑问。看一下HelloWorld的头文件,如下:
class HelloWorld : public cocos2d::CCLayer
{
public:
    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init(); 
 
    // there's no 'id' in cpp, so we recommand to return the exactly class pointer
    static cocos2d::CCScene* scene();
   
    // a selector callback
    void menuCloseCallback(CCObject* pSender);
 
    // implement the "static node()" method manually
    CREATE_FUNC(HelloWorld);
};
    可以看到HelloWorld类继承自布景层CCLayer。在它的头文件里定义了四个成员函数。前三个成员函数,很容易理解它们的用途。而最后一个有点奇怪,需要解释一下。CREATE_FUNC其实是一个宏定义,如下:
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
    __TYPE__ *pRet = new __TYPE__(); \
    if (pRet && pRet->init()) \
    { \
        pRet->autorelease(); \
        return pRet; \
    } \
    else \
    { \
        delete pRet; \
        pRet = NULL; \
        return NULL; \
    } \
}
    这个宏为HelloWorld添加一个静态成员函数create,并返回这个类自身的指针。在创建的时候,自动调用了类的init函数对类进行初始化,并且使用autorelease。(autorelease是Cocos2D-x的内存管理机制,欲详细了解请查看Cocos2D-x内存管理专题)来自动释放对象实例。
   还是关注HelloWorld实例的创建,答案就在下面这行代码里。
CCScene *pScene = HelloWorld::scene(); //这里获取一个场景
    这行代码明明是获取一个场景,HelloWorld实例又在哪里创建的呀。还是看一看HelloWorld的scene函数的实现,如下:
CCScene* HelloWorld::scene()
{
    // 'scene' is an autorelease object
    CCScene *scene = CCScene::create(); //这里真正创建一个场景CCScene
   
    // 'layer' is an autorelease object
    HelloWorld *layer = HelloWorld::create(); //创建一个布景CCLayer
 
    // add layer as a child to scene
    scene->addChild(layer);//将布景添加到场景里
 
    // return the scene
    return scene;//返回创建的场景
}
    现在一切都清楚了,原来在scene函数内创建了场景和布景,而且将布景添加到场景中并返回了场景。
    这里似乎只有场景和布景,还差什么呢?是精灵。那精灵又是在哪里创建的呢?答案是在HelloWorld的初始化init函数里。还是给出init函数的实现代码,如下:
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
   
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
 
    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.
 
    // add a "close" icon to exit the progress. it's an autorelease object
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
                                        "CloseNormal.png",
                                        "CloseSelected.png",
                                        this,
                                        menu_selector(HelloWorld::menuCloseCallback));
   
    if (CCApplication::sharedApplication()->getTargetPlatform() == kTargetIphone)
    {
        pCloseItem->setPosition(ccp(visibleSize.width - 20 + origin.x, 20 + origin.y));
    }
    else
    {
        pCloseItem->setPosition(ccp(visibleSize.width - 40 + origin.x, 40 + origin.y));
    }
 
    // create menu, it's an autorelease object
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
    pMenu->setPosition(CCPointZero);
    this->addChild(pMenu, 1);
 
    /////////////////////////////
    // 3. add your codes below...
 
    // add a label shows "Hello World"
    // create and initialize a label
    CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);
 
    // position the label on the center of the screen
    pLabel->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height - 50 + origin.y));
 
    // add the label as a child to this layer
    this->addChild(pLabel, 1);
 
    // add "HelloWorld" splash screen"
    CCSprite* pSprite = CCSprite::create("HelloWorld.png"); //这里创建精灵
 
    // position the sprite on the center of the screen
    pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
 
    // add the sprite as a child to this layer
    this->addChild(pSprite, 0);//将精灵添加到布景中
    return true;
}
    这段代码有点长,它将在HelloWorld类创建时被调用。它将给HelloWorld添加一个图像菜单(关闭按钮)、一个Label标签以及一个精灵。
    HelloWorld还有一个函数menuCloseCallback,这是一个事件响应函数。它用来响应关闭按钮被按下时的事件。它的处理很简单就是关闭程序。
 
    现在整个Cocos2D-x的基本框架讲解完了。它虽然很简单,但是却是Cocos2D-x开发的基础。
   
    刚开始接触Cocos2D-x代码时,里面有一些惯用法和宏定义可能不是很适应,不过慢慢就会习惯了。下面例举HelloWorld中使用的一些惯用法和宏,如下:
    ccp(visibleSize.width - 20 + origin.x, 20 + origin.y)//这里ccp是辅助宏,用来创建CCPoint。#define ccp(__X__,__Y__) cocos2d::CCPointMake((float)(__X__), (float)(__Y__))
    CCSprite* pSprite = CCSprite::create("HelloWorld.png");//使用create而不是new创建对象实例
    CCLayer::init();//使用init初始化一个对象
    CCDirector *pDirector = CCDirector::sharedDirector();//shared前缀表示一个类的单例(单例设计模式)
-------------------------------------------------------------------------------------------------------------------

注:本人在本博客的原创文章采用创作共用版权协议http://creativecommons.org/licenses/by-nc-sa/2.5/cn/), 要求署名、非商业用途和保持一致。要求署名包含注明我的网名及文章来源(我的博客地址:http://www.cnblogs.com/binbingg)。

[原创]cocos2d-x研习录-第二阶 基本框架的更多相关文章

  1. [原创]cocos2d-x研习录-第二阶 概念类之导演类(CCDirector)

    CCDirector类是游戏的组织和控制中心(总指挥),它控制着主屏幕的显示.场景的切换和显示,以及游戏的开始.结束和暂停.它的继承关系图如下:    CCDirector继承自基类CCObject, ...

  2. [原创]cocos2d-x研习录-第二阶 概念类之节点类(CCNode)

    节点类CCNode在基本概念中并不存在,它是为了建立基本概念之间的关联关系而抽象出来的中间辅助类.这个类在Cocos2D-x中极为重要,它为概念类之间搭建了一座宏伟的桥梁.它的继承关系图如下:     ...

  3. [原创]cocos2d-x研习录-第二阶 基本概念

    在Cocos2D-x引擎中,有几个非常重要的概念:导演(CCDirector).摄像机(CCCamera).场景(CCSecen).布景(CCLayer).精灵(CCSPrite)和动作(CCActi ...

  4. [原创]cocos2d-x研习录-第二阶 概念类之摄相机类(CCCamera)

    在Cocos2D-x中,每个CCNode都拥有一个摄像机类CCCamera.只有通过CCCamera,CCNode才会被渲染出来.当CCNode发生缩放.旋转和位置变化时,都需要覆盖CCCamera, ...

  5. [原创]cocos2d-x研习录-第二阶 概念类之精灵类(CCSprite)

    上一节说布景层CCLayer是小容器,那么精灵类CCSprite就是容器添加的内容,它是构成游戏的主要元素.精灵这个名称应该是游戏专用,它表示游戏中玩家操作的主角.敌人.NPC(Non Player ...

  6. [原创]cocos2d-x研习录-第二阶 概念类之布场层类(CCLayer)

    上面说场景CCScene相当于一个大容器,那么布景层类CCLayer就是大容器里的若干个小容器.每个游戏场景CCScene会有很多层CCLayer,每一层CCLayer负责各自的任务.看一下CCLay ...

  7. [原创]cocos2d-x研习录-第二阶 概念类之场景类(CCScene)

    场景类CCScene是Cocos2D-x在屏幕显示的内容,相当于游戏关卡或界面.CCDirector任何时候只能显示一个场景CCScene,游戏中可能存在若干场景,CCDirector通过场景切换达到 ...

  8. [原创]cocos2d-x研习录—前言

    我认为很多开发者面对层出不穷的新技术.新思想和新理念,最为之苦恼的是找不到行之有效的学习方法,对于知识的本质缺乏认识,虽阅读了大量教材,却无法将其融入自己的知识体系,并搭建自己的知识树.不可否认,教材 ...

  9. [原创]cocos2d-x研习录-第一阶 背景介绍 之 cocos2d家族史

    Cocos2D是一个2D开源游戏引擎,它最早是由Ricardo Quesada(阿根廷人,社区简称Riq)和他的朋友们用Python开发的,用于开发2D游戏和基于2D图形的任何应用.最早引擎的名字源自 ...

随机推荐

  1. hdu2896病毒侵袭(ac自动机)

    链接 ac自动机的模板题 说2个注意的地方 一是题目说明包含所有ASCII字符,可以开到0-127 包含空格 题目会输入多个源串,在加完当前的val值时,不应清0,可以开个标记数组. #include ...

  2. 初试 Matlab 之去除水印

    这几天很痛苦地去学习了下用 Matlab 来处理图像,其实那些算法我觉得还不算很难理解,可是 Matlab 这种反人类的语法(可能对于我来说是这样吧,毕竟熟悉了 C++ / Java 的语法一时间很难 ...

  3. tomcat配置项目的图片路径不在项目下的处理

    <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWA ...

  4. 浏览器html页面乱码问题分析

    直接访问某html文件,浏览器显示编码是正常的,页面通过<meta charset="UTF-8">指定了编码方式,该文件存储编码也是utf8. 通过配置的org.sp ...

  5. Caché数据库学习笔记(4)

    目录 DeepSee的使用 数据.方法等的导入与导出 ======================================================== ================ ...

  6. ASP.NET复合控件

    ① DropDownList 下拉列表 会被编译为select option ps.name 服务端常用,id 客户端常用 一般用法: 一.将数据放进去 方法一:同WinForm相同,给定数据源,然后 ...

  7. PDF 补丁丁 0.4.3.1342 测试版发布:修复崩溃问题

    PDF 补丁丁 0.4.3.1342 测试版发布了. 此测试版修复了之前测试版在合并文件.书签编辑器.文档结构探查器中出现的崩溃问题. 推荐下载了0.4.3测试版的网友尽快更新.

  8. JavaScript对象中的属性(可写,可配置,可枚举,value,getter,setter)

    JavaScript中,对象包括3个特性,分别为,可扩展性,class标识符,属性. 如果对象的可扩展性为false,则不可为对象动态的添加属性.   对象包含分为存取器属性和值属性.存取属性为 {g ...

  9. 说一说vector<bool>

    vector<T>标准库模版类应该是绝大多数c++程序员使用频率比较高的一个类了.不过vector<bool>也许就不那么被程序员所了解.关于vector<bool> ...

  10. 利用MVVM设计快速开发个人中心、设置等模块

    我们在做iOS开发过程中,静态页面的开发比开发动态页面更让我们开发者抓狂.因为动态页面通常是一个页面一种cell样式,作为开发者只需要专注于定制好一种样式之后,就可以使用数据填充出较好的界面.而静态c ...