【译】Cocos2d-x官方文档 第2章 Cocos2d-x基本概念
这一章假设您刚刚开始使用Cocos2d-x,并且打算开始制作您梦想中的游戏。别担心,这将会是一个愉快的过程!
让我们开始吧!
Cocos2d-x是一个跨平台的游戏引擎。游戏引擎就是一种提供所有游戏都需要的通用功能的软件。您可能在其他听过这个概念,只是被叫做API或者框架库,但是在本指引里我们将其称为“游戏引擎”。
游戏引擎包含很多组件,这些组件被组合在一起用时能够减少游戏开发时间,并且通常比自制引擎有更好的表现。游戏引擎通常由下面的组件组成:渲染器,2D/3D图形,碰撞检测,物理引擎,音效,控制器支持,动画等。游戏引擎通常支持多个平台,使得您可以非常容易地开发游戏,并且毫不费力地将其部署到多个平台。
Cocos2d-x游戏引擎提供了一组简化的API来开发跨平台的移动或桌面应用。通过把强大的功能封装在简单易用的API内,Cocos2d-x使您能集中精力于您的游戏的开发,而不用担心底层的技术实现。只要您愿意,您就可以选择让Cocos2d-x来负责帮您搞定底层的这些琐碎的事情。
Cocos2d-x提供了场景(Scene),场景切换(Transition),精灵(Sprite),菜单(Menu),3D精灵(Sprite3D),音频(Audio)等等对象,涵盖您创作游戏时需要的方方面面。
主要的几个组件
尽管看上去可能像是个挺大的挑战,但是Cocos2d-x入门其实非常简单。在进一步深入之前我们需要理解几个Cocos2d-x中使用的概念。Cocos2d-x里面的几个核心概念是场景(Scene),场景节点(Node),精灵(Sprite),菜单(Menu)和动作(Action)。找找您最喜欢的某个游戏,您会找到这些组件,虽然可能形式会有所不同。
让我们看看下面的图片,看起来有一点像您玩过的某个非常受欢迎的游戏。

让我们再看看下图,截图中组成游戏的各个组件被标示了出来。

您会看到一个菜单、一些精灵和一些标签,这些在Cocos2d-x中都有对应的组件。看看您的游戏的一些策划文档,看看都有哪些组件,可能其中一些已经与这里提到的匹配了。
导演(Director)
Cocos2d-x使用了导演(Director)的概念,就像电影中的导演一样!导演对象控制操作的流程,告诉其它必须的对象做些什么。想象一下您就是执行制片人,您告诉导演怎么做!导演对象控制场景的替换和切换。导演对象是一个共享的单例(也就是说,该类同一时刻只有一个实例)对象,您可以在代码的任何地方调用它。
下面是一个典型的游戏流程的例子,导演对象负责根据您的游戏的规则进行场景的切换:

您是您的游戏的导演。您决定会发生什么,什么时候发生以及怎样发生。掌控一切!
场景(Scene)
您也许希望您的游戏中有一个主菜单,几个关卡以及一个结局场景。那么您应该如何将这些东西组织成那样呢?对您猜对了,场景(Scene)。想一下您最喜欢的电影,您会发现电影通常被划分成若干个场景,或者一个一个独立的故事情节。如果我们把同样的想法应用到游戏中,我们会形成几个场景的概念,无论游戏有多简单。
让我们再看看与之前的图类似的另一张图片:

场景图(Scene Graph)
场景图是用于整理和布置图形场景的一种数据结构。场景图包含了一棵场景节点(Node)的树型结构(是的,这就是所谓的场景图,但其实它是用树来表示的)。

这听起来/看起来很复杂。我很肯定您会问为什么您仍然需要关心这些技术细节,Cocos2d-x不是把这些琐碎的事情都处理好了吗?让我们看看场景是如何被渲染器绘制出来的,理解这点很重要。
当您开始把节点,精灵,动画等加到您的游戏中时,您希望能确保绘制出来的内容是您希望的。但是如果绘制出来的内容与您希望的不一样怎么半?如果您的精灵藏在了背景的后面,而其实您希望它绘制在最上面怎么办?很简单,只需要回到刚刚的问题,在一张纸上跑一跑场景图,我敢肯定您能轻易地找到错误。
由于场景图是一棵树,所以就能遍历这个树。Cocos2d-x使用中序遍历算法。中序遍历算法下树的左边先被遍历,然后是根节点,然后是树的右边。由于树的右边最后被渲染,所以它显示在最上面。

场景图很容易说明,让我们看看下面的游戏场景:

将被作为一棵树进行渲染,下面是简化的图示:

另外一个需要考虑的问题是带有负的Z顺序(z-order)的元素会处于树的左边,而有正的Z顺序的元素会处于树的右边。排列元素时需要考虑到这点!您能按任意顺序添加元素,这些元素会按自定义的Z顺序自动地被排好序。
基于这个概念,我们可以将场景当成一系列场景节点(Node)对象的集合。让我们通过把这个场景拆分成带Z顺序的场景图来实现场景的布局:

左边的场景实际上是由多个场景节点对象组成的,这些场景节点被赋予了不同的Z顺序(z-order)因此能“堆”在其他的场景节点上。
在Cocos2d-x中,您可以通过addChild()API调用构造场景图(Scene Graph):
// Adds a child with the z-order of -2, that means
// it goes to the "left" side of the tree (because it is negative)
scene->addChild(title_node, -2);
// When you don't specify the z-order, it will use 0
scene->addChild(label_node);
// Adds a child with the z-order of 1, that means
// it goes to the "right" side of the tree (because it is positive)
scene->addChild(sprite_node, 1);
精灵(Sprites)
所有的游戏都包含精灵(Sprites)对象,你可能意识到也可能没有意识到它们是什么。精灵是一些在屏幕中移来移去的物体。您可以操作它们。游戏中的主角可能是一个精灵。我知道您会想,难道游戏中所有的图形对象不都是精灵吗?不是!为什么?我们说只有移来移去的才是精灵。如果不会移来移去,那么就只是一个场景节点(Node)。
再看看上面的那个图片,让我们指出哪些是精灵(Sprites),哪些是场景节点(Nodes):

精灵(Sprites)在任何游戏中都是很重要的。编写一个平台游戏(Platformer)时,可能会有一个使用某种图片实现的主角。这个主角就是一个精灵。
精灵(Sprites)很容易创建,精灵有下列可配置属性:位置(Position),旋转(Rotation),缩放(Scale),不透明度(Opacity),颜色(Color)等。
// This is how to create a sprite
auto mySprite = Sprite::create("mysprite.png");
// this is how to change the properties of the sprite
mySprite->setPosition(Vec2(500, 0));
mySprite->setRotation(40);
mySprite->setScale(2.0); // sets both the scale of the X and Y axis uniformly
mySprite->setAnchorPoint(Vec2(0, 0));
让我们来说明一下各个属性,考虑本章示例代码中的如下屏幕截图:

如果我们用mySprite->setPosition(Vec2(500, 0));语句来设置位置:

我们可以留意到精灵位置发生了变化,从原来的位置移动到了我们指定的新的位置。
如果我们用mySprite->setRotation(40);语句来设置一个新的旋转:

可以留意到精灵就被旋转到我们指定的方向上了。
如果我们用mySprite->setScale(2.0);语句来指定一个新的缩放:

我们看到通过修改代码,精灵再一次地发生了变化。
最后,所有场景节点(Node)(精灵(Sprite)是场景节点(Node)的子类)都有一个锚定点(Anchor Point)的值。目前为止我们还没有讨论过这点,现在是时候说明一下了。可以认为锚定点(Anchor Point)是一种当设置精灵的位置时,指定精灵的哪一部分会被用作基础坐标系的方法。
通过如下代码设置我们示例游戏中的角色的锚定点到0,0:
mySprite->setAnchorPoint(Vec2(0, 0));
这将导致对于任何setPosition()调用都使用精灵的左下角作为基准。让我们看看其他几个例子:

请注意每张图里的红点。红点标示了锚定点的位置。
正如你所看到的,锚定点(Anchor Point)在定位场景节点时非常有用。甚至可以通过动态地调整锚定点来模拟游戏中的某些效果。
实际上我们能调整精灵的各方面内容。但是,如果我们希望有一种自动的,由时间决定的方式在精灵上应用这些类型的变化呢?下面就会揭晓。
动作(Actions)
创建场景和加入精灵对象到屏幕只是我们需要做的事情的一部分。为了让游戏成为游戏我们需要让东西都动起来!动作(Action)对象是每个游戏必须的部分。使用动作能让我们在一定的时间间隔内变换场景节点对象。您是否需要移动一个精灵,并在移动结束时使用一个回调函数?没问题!您甚至能创建一系列的动作,然后在场景节点上执行。您可以改变场景节点的位置,旋转,缩放等属性。动作(Actions)的示例:移动指定距离(MoveBy),Rotate(旋转),Scale(缩放)。所有游戏都会使用动作。
让我们看看这一章的示例程序,下图是运行中的动作:

5秒钟后,精灵会移动到新的位置:

动作对象很容易创建:
auto mySprite = Sprite::create("Blue_Front1.png");
// Move a sprite 50 pixels to the right, and 10 pixels to the top over 2 seconds.
auto moveBy = MoveBy::create(2, Vec2(50,10));
mySprite->runAction(moveBy);
// Move a sprite to a specific location over 2 seconds.
auto moveTo = MoveTo::create(2, Vec2(50,10));
mySprite->runAction(moveTo);
顺序动作集合(Sequences)和同时动作集合(Spawns)
能让精灵在屏幕中移动,我们就拥有用来创建游戏必须的一切了?不完全是这样。比如运行多个动作,Cocos2d-x也使用了几种不同的方法处理了这个问题。
就像听起来一样,顺序动作集合(Sequences)是按指定顺序一次执行的多个动作的集合。想要反过来执行顺序动作集合?没问题,Cocos2d-x中处理这件事情,并不需要额外的工作。
让我们看看下面这个图,一个逐渐移动精灵的顺序动作集合的示例:
![]()
此顺序动作集合很容易构建:
auto mySprite = Node::create();
// move to point 50,10 over 2 seconds
auto moveTo1 = MoveTo::create(2, Vec2(50,10));
// move from current position by 100,10 over 2 seconds
auto moveBy1 = MoveBy::create(2, Vec2(100,10));
// move to point 150,10 over 2 seconds
auto moveTo2 = MoveTo::create(2, Vec2(150,10));
// create a delay
auto delay = DelayTime::create(1);
mySprite->runAction(Sequence::create(moveTo1, delay, moveBy1, delay.clone(),
moveTo2, nullptr));
这个示例按顺序地运行了一个顺序动作集合,但是如果需要同事运行所有指定的动作呢?Cocos2d-x也支持这种方式,这种方式叫做同时动作集合(Spawns)。同时动作集合同时执行所有指定的动作。其中一些可能比另外一些执行的时间长,在这种情况下,它们不会同时执行结束。
auto myNode = Node::create();
auto moveTo1 = MoveTo::create(2, Vec2(50,10));
auto moveBy1 = MoveBy::create(2, Vec2(100,10));
auto moveTo2 = MoveTo::create(2, Vec2(150,10));
myNode->runAction(Spawn::create(moveTo1, moveBy1, moveTo2, nullptr));
为什么要用同时动作集合(Spawns)?有什么原因吗?当然!例如主角得到一个能量包时有多个动作。又例如在打败关卡最后的BOSS时需要多个动作。
父子节点关系
Cocos2d-x使用父子节点关系的概念。这意味着父节点的属性和变化,也会应用到子节点。考虑一个单独的精灵和一个拥有子节点的精灵:

在拥有子节点的情况下,改变父节点的旋转量也会导致其所有子节点发生旋转变化。

auto myNode = Node::create();
// rotating by setting
myNode->setRotation(50);
就像旋转一样,如果您改变了父节点的缩放量,则子节点也会被相应地缩放。

auto myNode = Node::create();
// scaling by setting
myNode->setScale(2.0); // scales uniformly by 2.0
将日志作为一种输出消息的方法
// a simple string
log("This would be outputted to the console");
// a string and a variable
string s = "My variable";
log("string is %s", s);
// a double and a variable
double dd = 42;
log("double is %f", dd);
// an integer and a variable
int i = 6;
log("integer is %d", i);
// a float and a variable
float f = 2.0f;
log("float is %f", f);
// a bool and a variable
bool b = true;
if b == true then
log("bool is true");
else
log("bool is false");
end
而且,正如您所期望的,如果您愿意,你可以用std::cout代替log(),然而log()可能对复杂的输出提供了更加方便的格式化功能。
结论
我们已经对Cocos2d-x里的很多概念过了一遍。深呼吸一下,不要担心,只需要一步一步深入就可以。Cocos2d-x和通用编程并不是一夜之间就可以学会的技能,需要实践和领会。请一定记住,论坛也能帮您解决您遇到的问题。
【版权声明】
原文地址:http://www.cocos2d-x.org/docs/programmers-guide/2/index.html
官方手册,基于文档版权协议自动获得授权。
