精通Cocos2d-x游戏开发(基础卷)
上QQ阅读APP看书,第一时间看更新

2.2 分析HelloCpp

HelloCpp是Cocos2d-x的一个入门例子,在第1章中已经将其运行起来了,下面先介绍一下HelloCpp目录结构,这也是所有Cocos2d-x程序的一个基本结构,如图2-2和图2-3所示。

图2-2 2.x目录

图2-3 3.x目录

main.cpp和main.h是win32下的程序入口,不同平台有不同的入口,这部分代码只负责在当前平台下启动Cocos2d-x。想要详细了解win32下是如何启动的,只需要阅读《Windows程序设计》的第3章窗口与消息,照着写一个简单的窗口Application即可。基于Android和iOS,在最纯粹的环境下,写一个HelloWrold类型的程序运行起来,不是一件很难的事情,但能让程序员建立对这个平台的一个整体认识,帮助解决在这个平台下碰到的一些问题,例如,有些问题不清楚是Cocos2d-x的问题,还是系统的问题,那么把代码简化,把问题简化,再来解决,则可以免除很多干扰(也就是使用排除法)。

接下来是AppDelegate和HelloWorldScene,AppDelegate是Cocos2d-x内部的入口,从操作系统到Cocos2d-x,会先进入到AppDelegate。在引擎初始化完成之后,会调用AppDelegate的applicationDidFinishLaunching方法,在AppDelegate中执行游戏的初始化,设置分辨率并启动场景,如图2-4所示。Cocos2d-x在几个主要平台上的详细运行流程,会在第14章中详细介绍,本章只介绍一个精简流程。

图2-4 启动流程

bool AppDelegate::applicationDidFinishLaunching()
{
    //初始化director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    //初始化OpenGL视图
    if(!glview)
    {
        glview = GLViewImpl::create("Cpp Empty Test");
        director->setOpenGLView(glview);
    }
    //设置OpenGL视图到director中
   director->setOpenGLView(glview);
   //设置屏幕分辨率
  glview->setDesignResolutionSize(designResolutionSize.width,
  designResolutionSize.height,   ResolutionPolicy::NO_BORDER);
    //开启显示左下角的FPS状态信息
    director->setDisplayStats(true);
    //设置FPS帧率,默认的帧率就是1.0 / 60,也就是每秒执行60帧
    director->setAnimationInterval(1.0 / 60);
    //创建HelloWorld场景,这个对象会交由Cocos2d-x管理,不需要手动释放
    auto scene = HelloWorld::scene();
    //运行HelloWorld场景
    director->runWithScene(scene);
    return true;
}

AppDelegate调用HelloWorld::scene()创建了一个HelloWorld场景然后启动HelloWorld,在HelloWorld的init中,构建了整个场景所需要的内容。构建场景树的过程就是create节点,然后调用场景中节点的addChild方法,将创建的节点添加到节点树中。在HelloWorld场景中构建了一个菜单、菜单项、文本和图片。

//在init中构建节点
bool HelloWorld::init()
{
    //1. 先执行父类的init
    if ( !Layer::init() )
    {
        return false;
    }
    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();
    //2. 关闭按钮是一个MenuItemImage,表示一个菜单项,非选中状态和选中状态分别是
    CloseNormal.png和CloseSelected.png,当它被单击的时候,会调用HelloWorld的
    menuCloseCallBack函数
    auto closeItem = MenuItemImage::create(
                                        "CloseNormal.png",
                                        "CloseSelected.png",
                                        CC_CALLBACK_1(HelloWorld::menuClose
                                        Callback, this));
    //设置关闭按钮的位置,其在窗口的右下角
    closeItem->setPosition(origin + Vec2(visibleSize) - Vec2(closeItem->
     getContentSize() / 2));
    //创建菜单,上面创建的菜单项需要添加到菜单中才有效
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);
    //创建HelloWorld的文本,字体是Arial,字号是TITLE_FONT_SIZE.
    auto label = LabelTTF::create("Hello World", "Arial", TITLE_FONT_SIZE);
    //将文本放置到中间偏上的位置,并添加到场景中
    label->setPosition(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->
                            getContentSize().height);
    this->addChild(label, 1);
    //添加背景到场景中并居中
    auto sprite = Sprite::create("HelloWorld.png");
    sprite->setPosition(Vec2(visibleSize / 2) + origin);
    this->addChild(sprite);
    return true;
}

在创建MenuItemImage时,指定了回调函数menuCloseCallback,它是HelloWorld的成员函数,函数原型如下所示,传入一个Ref*,单击事件的发送者也就是我们所单击的MenuItemImage。在回调函数中,执行Director的end方法让游戏结束。

注册回调时,Cocos2d-x 2.x使用的是menu_selector(HelloWorld::menuCloseCallback),而Cocos2d-x 3.x则是CC_CALLBACK_1(HelloWorld::menuCloseCallback,this),具体的规则会在第5章中讲述。

void HelloWorld::menuCloseCallback(Ref* sender)
{
    Director::getInstance()->end();
}

图2-4和前面的代码介绍了显示图片和输入回调等内容,都是一些静态的内容。接下来调整一下HelloWrold的代码,做一些有趣的事情,用我们的代码来控制场景的内容,让它们“动”起来。

下面实现这样的功能:当每次单击按钮的时候,按钮的位置往左边偏移一些,而在每次update中,文本的位置往下偏移一点。要实现这两个功能,可以在HelloWorld场景类中定义两个成员指针,来指向文本对象和按钮对象,然后在update和按钮回调中编写相关操作。在这里不定义新的变量,可以用Tag来获取它们。Tag查询以及名字查询,可以减少一些成员变量的声明,但在操作非常频繁的时候,还是有必要添加一个成员变量来存放节点指针。总之,怎样简洁怎样做

我们需要在HelloWorld::init的代码中,将这两行代码添加到return true的前面,然后修改menuCloseCallBack中的代码,并添加一个update函数,(在头文件添加一行void update(float dt))。这两行代码将label的标签设置为123,方便以后查找节点。另外调用了scheduleUpdate函数,让Cocos2d-x每一帧都执行update方法。

label->setTag(123);
this->scheduleUpdate();

将menuCloseCallback回调函数的代码修改如下,完成每次单击按钮都往左边偏移一点的功能。

//pSender是发送者本身,在这里这个消息的发送者就是按钮,而我们要设置其位置
//需要将其转化成CCNode指针,这里用dynamic_cast这种写法
//这种是C++标准写法,用于有继承关系的指针转化,比直接(CCNode*)转化更加安全
//因为dynamic_cast中间会进行类型检查,当pSender不能转化为CCNode*的时候,会返回
一个空指针
//在判断的时候,养成常量在左的习惯,会减少很多不必要的麻烦
void HelloWorld::menuCloseCallback(Ref* pSender)
{
    Node* node = dynamic_cast<Node*>(pSender);
    if (NULL != node)
    {
        node->setPositionX(node->getPositionX() - 10.0f);
    }
}

在HelloWrold.cpp中添加如下代码,先获取子节点,如果获取到,则设置其位置,注意这里用到了一个50.0f * dt,dt作为一个参数传入,表示上一帧逝去的时间,1.0表示一秒钟,帮助我们控制文本平滑地运动。50.0f * dt表示每秒50单位的速度,100.0f * dt则表示每秒100单位的速度,这个单位可以简单理解为像素,但其并不等于实际的像素,会受到分辨率的影响。

//这里直接通过getChildByTag()函数获取文本对象,并且设置其偏移位置
void HelloWorld::update(float dt)
{
    Node* node = this->getChildByTag(123);
    if (NULL != node)
    {
        node->setPositionY(node->getPositionY() - 50.0f * dt);
    }
}

运行代码,可以看到HelloWorld慢慢落下来,每次单击按钮的时候,按钮都往左边移动一点,Cocos2d-x提供了很多方便操作场景的方法,这一章先上一碟开胃小菜。