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提供了很多方便操作场景的方法,这一章先上一碟开胃小菜。