3.3 第一个Cocos2d-x游戏
我们编写的第一个程序一般都命名为HelloWorld,从它开始再学习其他的内容。这一节介绍的第一个Cocos2d-x游戏也命名为HelloWorld。
3.3.1 创建工程
在Cocos2d-x早期版本中,创建工程是通过安装在Visual Studio中的工程模板创建的,而Cocos2d-x 3.x创建工程是通过Cocos2d-x提供的命令工具cocos实现的。配置好环境后,进入终端,并从终端进入cocos目录,在终端中执行如下指令:
cocos new HelloWorld -p com.work6 -l cpp -d <工程生成目录>
通过上面的指令,我们在<工程生成目录>下面生成了名为HelloWorld的Cocos2d-x工程。打开HelloWorld目录,其中的内容如图3-4所示。
图3-4 HelloWorld工程中的内容
从图3-4中可以看出,生成的工程代码是适合于多平台的,其中Classes目录是放置一些通用类(与平台无关的),我们编写的C++代码主要放置在该目录下面。图3-4中cocos2d目录是放置Cocos2d-x引擎的源代码,其中包括了音效引擎、物理引擎等。
图3-4中的proj.android、proj.android-studio、proj.ios_mac、proj.linux、proj.win8.1-universal、proj.win10和proj.win32目录是放置与特定平台有关系的代码,其中proj.android和proj.android-studio是android平台特定代码;proj.ios_mac是iOS和Mac OS运行需要的特定代码。proj.win32是Win32平台运行所需要的特定代码,它可以在Windows下运行,模拟器是Win32窗口;proj.win8.1-universal是Windows Phone 8.1平台运行所需要的特定代码;proj.win10是Windows平台运行所需特定代码。proj.linux是Linux平台运行所需要的特定代码。
图3-4中Resources目录是放置工程需要的资源文件,这个目录中的内容是共享于全部平台下的。
3.3.2 Visual Studio工程文件结构
如果是在Windows下,可以通过Visual Studio工具编译和运行Cocos2d-x工程,通过Visual Studio工具打开proj.win32目录下面的Visual Studio解决方案HelloWorld.sln来进行编译和运行。
进入到proj.win32目录下,双击HelloWorld.sln解决方案文件,启动HelloWorld界面,如图3-5所示。
图3-5 在Visual Studio中启动HelloWorld.sln解决方案
在图3-5所示的解决方案中,HelloWorld工程的src文件夹中的内容是与图3-4的Classes目录内容对应的。HelloWorld工程的win32文件夹中的main.cpp和main.h是win32平台特有程序代码,通过它启动Win32窗口。
如果想看一下效果,可以单击按钮运行,如图3-6所示。
图3-6 程序运行效果
3.3.3 Xcode工程文件结构
如果在Mac OS X下,可以通过Xcode工具编译和运行Cocos2d-x工程,通过Xcode工具打开proj.ios_mac目录下面的HelloWorld.xcodeproj工程文件。
进入到proj.ios_mac目录下,双击HelloWorld.xcodeproj工程文件,启动HelloWorld界面,如图3-7所示。
图3-7 在Xcode中启动HelloWorld.xcodeproj工程
图3-7所示的Xcode中HelloWorld工程的Classes组中的内容是与图3-4的Classes目录内容对应的。HelloWorld工程的ios组是iOS平台特有程序代码,mac组是Mac OS X平台特有程序代码。
如果想看一下效果,可以单击工具栏中的按钮运行,在iPhone 6模拟器上运行的效果如图3-8所示。
图3-8 程序运行效果
3.3.4 代码解释
下面解释一下Cocos2d-x工程源程序文件,它们是位于Classes文件夹中的4个文件:AppDelegate.h、AppDelegate.cpp、HelloWorldScene.h和HelloWorldScene.cpp。
1. AppDelegate类
在AppDelegate.h和AppDelegate.cpp中分别声明和定义了AppDelegate类,AppDelegate类是Cocos2d-x引擎要求实现的游戏应用委托对象,在Cocos2d-x游戏运行的不同生命周期阶段会触发它的不同函数。
AppDelegate.h代码如下:
# ifndef _APP_DELEGATE_H_ # define _APP_DELEGATE_H_ # include "cocos2d.h" class AppDelegate : private cocos2d::Application { public: AppDelegate(); virtual ~AppDelegate(); virtual void initGLContextAttrs(); /* * 游戏启动时调用的函数,在这里可以初始化导演对象和场景对象 */ virtual bool applicationDidFinishLaunching(); /* * 游戏进入后台时调用的函数 */ virtual void applicationDidEnterBackground(); /* * 游戏进入前台时调用的函数 */ virtual void applicationWillEnterForeground(); }; # endif //_APP_DELEGATE_H_
从上面的代码可以看到,AppDelegate继承了cocos2d::Application,cocos2d::Application是Cocos2d-x引擎提供的基类。
AppDelegate.cpp代码如下:
# include "AppDelegate.h" # include "HelloWorldScene.h" USING_NS_CC; ① static cocos2d::Size designResolutionSize = cocos2d::Size(480, 320); ② static cocos2d::Size smallResolutionSize = cocos2d::Size(480, 320); static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768); static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536); ③ AppDelegate::AppDelegate() { } AppDelegate::~AppDelegate() { } //设置OpengGL上下文属性 void AppDelegate::initGLContextAttrs() { //设置OpenGL上下文属性,现在可以设置6个属性 //red,green,blue,alpha,depth(深度缓存),stencil(模板缓存) GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8}; GLView::setGLContextAttrs(glContextAttrs); } //如果使用包管理安装更多的包,不要修改或删除该函数 static int register_all_packages() { return 0; //flag for packages manager } bool AppDelegate::applicationDidFinishLaunching() { ④ //初始化director auto director = Director::getInstance(); ⑤ auto glview = director->getOpenGLView(); if(!glview) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) ⑥ glview = GLViewImpl::createWithRect("HelloWorld", Rect(0, 0, designResolutionSize.width, designResolutionSize.height)); #else glview = GLViewImpl::create("HelloWorld"); # endif director->setOpenGLView(glview); ⑦ } director->setDisplayStats(true); ⑧ director->setAnimationInterval(1.0 / 60); ⑨ //设置设计分辨率 glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER); ⑩ Size frameSize = glview->getFrameSize(); if (frameSize.height > mediumResolutionSize.height) { director->setContentScaleFactor(MIN(largeResolutionSize.height/designResolutionSize.height, largeResolutionSize.width/designResolutionSize.width)); } else if (frameSize.height > smallResolutionSize.height) { director->setContentScaleFactor(MIN(mediumResolutionSize.height/designResolutionSize.height, mediumResolutionSize.width/designResolutionSize.width)); } else { director->setContentScaleFactor(MIN(smallResolutionSize.height/designResolutionSize.height, smallResolutionSize.width/designResolutionSize.width)); } ⑪ register_all_packages(); ⑫ auto scene = HelloWorld::createScene(); ⑬ //run director->runWithScene(scene); ⑭ return true; } void AppDelegate::applicationDidEnterBackground() { ⑮ Director::getInstance()->stopAnimation(); ⑯ //如果使用SimpleAudioEngine播放音乐,则在该方法中暂停 //SimpleAudioEngine::getInstance()->pauseBackgroundMusic(); ⑰ } void AppDelegate::applicationWillEnterForeground() { ⑱ Director::getInstance()->startAnimation(); ⑲ //如果使用SimpleAudioEngine播放音乐,则在该方法中继续 //SimpleAudioEngine::getInstance()->resumeBackgroundMusic(); ⑳ }
上述代码第①行的USING_NS_CC是Cocos2d-x提供了一个宏,它的作用是用来替换using namespace cocos2d语句,随着我们学习深入,你会发现Cocos2d-x定义了很多宏,使用起来很方便。
代码第②行~第③行定义了几种分辨率尺寸。
代码第④行定义applicationDidFinishLaunching()函数是游戏程序启动时调用的函数。
代码第⑤行是初始化导演类Director,代码第⑥行是通过判断当前运行的平台。代码第⑦行是设置导演类的OpenGL视图。代码第⑧行设置是否在屏幕上显示帧率等信息,60.1就是当前的帧率,显示帧率一般是为了测试,实际发布时设置为不显示的,它会影响游戏的外观。第⑨行代码是设定计时器1.0 / 60秒间隔一次,即设定平均帧率为60。
代码第⑩行~第⑪行是设置解决屏幕适配问题,有关知识将会在后面章节介绍。
代码第⑫行调用register_all_packages()管理安装。
代码第⑬行创建场景对象Scene,第⑭行代码是运行该场景,这会使游戏进入该场景。
代码第⑮行定义的applicationDidEnterBackground()函数是游戏进入后台时调用的函数。第⑯行代码是停止场景中的动画。第⑰行代码是暂停背景音乐,默认是注释掉的,游戏中如果使用SimpleAudioEngine播放音乐,则在该方法中暂停播放音乐。
代码第⑱行定义的applicationWillEnterForeground()函数是游戏进入前台时调用的函数。第⑲行代码是开始场景中的动画。第⑳行代码是继续背景音乐,默认是注释掉的,如果使用SimpleAudioEngine播放音乐,则在该方法中实现继续播放音乐。
2. HelloWorld类
在HelloWorldScene.h和HelloWorldScene.cpp中分别声明和定义了HelloWorld类,HelloWorld类继承了cocos2d::Layer类,它被称为层(Layer),这些层被放到场景(Scene)中,场景类是cocos2d::Scene。注意,HelloWorldScene.h虽然命名为场景,但是它内部定义的HelloWorld类是一个层。
HelloWorldScene.h的代码如下:
# ifndef __HELLOWORLD_SCENE_H__ # define __HELLOWORLD_SCENE_H__ # include "cocos2d.h" class HelloWorld : public cocos2d::Layer ① { public: static cocos2d::Scene* createScene(); ② virtual bool init(); ③ void menuCloseCallback(cocos2d::Ref* pSender); ④ CREATE_FUNC(HelloWorld); ⑤ }; # endif //__HELLOWORLD_SCENE_H__
从上面的代码第①行可以看出,HelloWorld继承了cocos2d::Layer,HelloWorld是一个层,而不是场景。第②行代码是声明创建当前层HelloWorld所在场景的静态函数createScene()。第③行代码是声明初始化层HelloWorld实例函数。第④行代码是声明菜单回调函数menuCloseCallback,用于触摸菜单事件的回调。第⑤行代码中的CREATE_FUNC是一个Cocos2d-x定义的宏,它的作用相当于如下代码:
static HelloWorld* create() { HelloWorld *pRet = new HelloWorld(); if (pRet && pRet->init()) { pRet->autorelease(); return pRet; } else { delete pRet; pRet = NULL; return NULL; } }
从上述代码可见,CREATE_FUNC宏的作用是创建一个静态函数create,该函数可以用来创建层。
下面我们分别解释一下HelloWorldScene.cpp中的几个函数。
HelloWorldScene.cpp中createScene()代码如下:
Scene* HelloWorld::createScene() { auto scene = Scene::create(); ① auto layer = HelloWorld::create(); ② scene->addChild(layer); ③ return scene; }
createScene()函数是在游戏应用启动的时候,在AppDelegate的applicationDidFinishLaunching()函数中通过auto scene = HelloWorld::createScene()语句调用的。在createScene()中做了三件事情,首先创建了HelloWorld层所在的场景对象(见代码第①行),其次创建了HelloWorld层(见代码第②行),最后将HelloWorld层添加到场景scene中(见代码第③行)。
当调用HelloWorld::create()语句创建层的时候,会调用HelloWorld的实例函数init(),达到初始化HelloWorld层的目的,init ()代码如下:
bool HelloWorld::init() { ////////////////////////////// //1. 初始化父类 if ( !Layer::init()) ① { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); ② Vec2 origin = Director::getInstance()->getVisibleOrigin(); ③ ///////////////////////////// //2. 增加一个菜单项,单击它的时候退出程序 auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this)); ④ closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,origin.y + closeItem->getContentSize().height/2)); ⑤ auto menu = Menu::create(closeItem, NULL); ⑥ menu->setPosition(Vec2::ZERO); ⑦ this->addChild(menu, 1); ⑧ ///////////////////////////// //3. 在下面添加你自己的代码 auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24); ⑨ label->setPosition(Vec2(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.width/2 + origin.x, visibleSize.height/2 + origin.y)); ⑬ this->addChild(sprite, 0); ⑭ return true; }
在init()函数中主要是初始化HelloWorld层,其中包括了里面的精灵、菜单和文字等内容。其中第①行代码是初始化父类Layer,返回true则初始化成功,false则初始化失败。
第②行代码是视图的可视化尺寸,第③行代码是视图的可视化原点。
第④行代码是创建一个图片菜单项对象,单击该菜单项的时候回调menuCloseCallback函数。第⑤行代码是菜单项对象的位置,第⑥行代码是创建Menu菜单对象。第⑦行代码是菜单对象的位置。第⑧行代码是把菜单对象添加到当前HelloWorld层上,如图3-6或图3-8所示,运行界面右下角的就是刚刚添加的菜单对象。
第⑨~⑪行代码是将一个Hello World标签对象放置到层中,这个过程是:创建对象→设置对象的位置→把对象添加到层上。第⑨行代码是创建一个LabelTTF标签对象。第⑩行代码是设置标签对象位置为水平居中,在垂直方向上与屏幕顶对齐。第⑪行代码是将文本对象添加到层HelloWorld上。
第⑫行代码是创建精灵Sprite对象,它是图3-6中的Cocos2d-x的logo图标。第⑬行代码是设置精灵对象的位置,这个位置是屏幕的中央。第⑭行代码是将精灵对象添加到层HelloWorld上。
菜单回调函数menuCloseCallback代码如下:
void HelloWorld::menuCloseCallback(Ref* pSender) { Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) ① exit(0); # endif }
menuCloseCallback函数中使用了条件编译语句代码①行所示,用来判断是当前程序运行的是哪个平台,其中CC_TARGET_PLATFORM宏保存游戏运行的当前目标平台,CC_PLATFORM_IOS宏表示iOS平台,此外还有定义很多平台的宏,如果需要大家可以查询API。
3.3.5 Win32平台下设置屏幕
我们在Win32平台运行游戏是为了进行测试游戏应用,那么如何改变屏幕的大小呢?可以修改AppDelegate.cpp中的smallResolutionSize和designResolutionSize常量值代码如下:
# include "AppDelegate.h" # include "HelloWorldScene.h" USING_NS_CC; static cocos2d::Size designResolutionSize = cocos2d::Size(900, 640); ① static cocos2d::Size smallResolutionSize = cocos2d::Size(900, 640); ② static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768); static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536); AppDelegate::AppDelegate() { } AppDelegate::~AppDelegate() { } void AppDelegate::initGLContextAttrs() { … return true; } …
在上述代码中,将代码第①行的designResolutionSize和smallResolutionSize常量都设置为(900, 640),这里我们暂时不做解释,在后面的章节我们再解释。
3.3.6 工程中添加资源文件
在游戏开发过程中经常需要向工程中添加资源文件(图片、声音、视频和配置文件等),如果在Windows下通过Visual Studio工具运行Win32工程,我们把这些资源文件复制到资源管理器的Resources目录下就可以了。
而如果在Mac OS X下通过Xcode工具运行工程,则需要将这些资源文件添加到Xcode工程中,添加到工程的具体步骤是:在工程导航面板中,右键选择Resources组,弹出右键菜单如图3-9所示,选择菜单中的Add Files to “HelloWorld”弹出选择文件对话框,如图3-10所示,选中Destination→Copy items if needed可以使文件或文件夹从原始位置复制到当前工程目录中。Added foldes项目适用于添加文件夹时候,选中Create groups时,该文件夹将在Xcode中作为组(group),组在Xcode中颜色为黄色;选中Create folder references时,该文件夹将在Xcode中作为文件夹表示,文件夹在Xcode中颜色为蓝色。在Add to targets选中其中的Targets,可以使这些资源文件编译到Targets中,并随产品一起发布。
图3-9 添加资源文件到工程
图3-10 选择资源文件对话框
提示
Xcode中一个Targets编译之后是一个产品,这个产品可能是一个可执行程序包,也可以是一个库或框架。
在图3-10所示的对话框中选中要添加的资源文件或文件夹后,单击Add按钮添加这些文件到Xcode,如图3-11所示,添加了两个图片文件到Xcode的Resources组下面。
图3-11 添加资源文件完成