Cocos2d-x学习笔记:完全掌握C++ API与游戏项目开发 (未来书库)
上QQ阅读APP看书,第一时间看更新

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 添加资源文件完成