程序启动从 main 主函数开始执行,主函数调用应用程序控制器 AppController,AppController 引用视图控制器 RootViewController 来构建游戏视图,在 AppController 的 didFinishLaunchingWithOptions 方法最后运行应用程序 Application,这时调用 AppDelete 的 applicationDidFinishLaunching 方法运行场景 HelloWorldScene,该方法实现结束,游戏进入主循环。
下面是一个简单的游戏界面:
让我们再来看一下,分解这个游戏界面的组件构成。
一个菜单(Menu),几个精灵(Sprite),和几个标签(Label)。
在使用 Cocos2d-x 开发游戏的过程中,你可以认为自己是执行制片人,告诉 导演(Director) 该怎么办!一个常见的 Director 任务是控制场景替换和转换。 Director是一个共享的单例对象,可以在代码中的任何地方调用。
这是一个典型的游戏流程实例。当您的游戏设计好时,Director 就负责场景的转换:
你是你的游戏的导演。你决定着发生什么,何时发生,如何发生。
在游戏开发过程中,你可能需要一个主菜单,几个关卡和一个结束场景。
这是一个主菜单场景,这个场景是由很多小的对象拼接而成,所有的对象组合在一起,形成了最终的结果。场景是被 渲染器(renderer) 画出来的。渲染器负责渲染精灵和其它的对象进入屏幕。为了更好的理解这个过程,我们需要讨论一下 场景图。
场景图(Scene Graph) 是一种安排场景内对象的数据结构,它把场景内所有的 节点(Node) 都包含在一个 树(tree) 上。(场景图虽然叫做"图",但实际使用一个树结构来表示)。
当你开发游戏的时候,你会添加一些节点,精灵和动画到一个场景中,你期望的是每一个添加的对象都能被正确的展示,可是如果有个对象没有被展示呢?可能你错误的把这个对象隐藏到背景中了。怎么办?画出场景图!
Cocos2d-x 使用 中序遍历来渲染场景,先遍历左子树,然后根节点,最后是右子树。
分解这个场景,看一下它有哪些元素,这些最终会被渲染为一个树。
另一点要考虑的是,z-order (即z轴坐标)为负的元素,z-order 为负的节点会被放置在左子树,非负的节点会被放在右子树。实际开发的过程中,你可以按照任意顺序添加对象,他们会按照你指定的 z-order 自动排序。
如上图,左侧的场景是由很多节点对象组成的,他们根据被指定的 z-order 相互叠加。在 Cocos2d-x 中,通过 Scene 的 addChild()
方法构建场景图。
scene->addChild(title_node, -2);
scene->addChild(label_node);
scene->addChild(sprite_node, 1);
渲染时 z-order 值大的节点对象会后绘制,值小的节点对象先绘制。如果两个节点对象的绘制范围有重叠,z-order 值大的可能会覆盖 z-order 值小的。(z轴指向由屏幕指向外,即z-order越小,该节点越早被渲染,会呈现在离我们更远的图层也就是更内层)
精灵是在屏幕上移动的对象,它能被控制。不是每个图形对象都是一个精灵,如果你能控制它,它才是一个精灵,如果无法控制,那就只是一个节点(Node)。
Sprite
很容易被创建,它有一些可以被配置的属性,比如:位置,旋转角度,缩放比例,透明度,颜色 等等。
auto mySprite = Sprite::create("mysprite.png"); // png图创建精灵
mySprite->setPosition(Vec2(500, 0)); // 设置精灵位置
mySprite->setRotation(40); // 设置精灵起始旋转角度
mySprite->setScale(2.0); // 设置缩放比例
mySprite->setAnchorPoint(Vec2(0, 0)); // 设置锚点
锚点(anchor point) ,所有的节点对象都有,Sprite
是 Node
的子类,自然也具有锚点。锚点是节点对象在计算坐标位置时的一个基准点。
动作(Action) 可以让精灵在场景中移动,从一个点移动到另外一个点。你还可以创建一个动作 序列(Sequence) ,让精灵按照这个序列做连续的动作,在动作过程中你可以改变精灵的位置,旋转角度,缩放比例等等。
Action
对象的创建:
auto mySprite = Sprite::create("Blue_Front1.png");
auto moveBy = MoveBy::create(2, Vec2(50,10)); // 将精灵向右移动 50 像素,并在 2 秒内向顶部移动 10 像素。
mySprite->runAction(moveBy);
auto moveTo = MoveTo::create(2, Vec2(50,10)); // 在 2 秒内将精灵移动到特定位置。
mySprite->runAction(moveTo);
如何执行多个 Action
? Cocos2d-x 通过 序列(Sequence) 来支持这种需求。
让我们来看一个通过序列控制精灵移动的例子:
创建 Sequence
:
auto mySprite = 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));
auto delay = DelayTime::create(1);
mySprite->runAction(Sequence::create(moveTo1, delay, moveBy1, delay.clone(),
moveTo2, nullptr));
如果想让所有的特定动作同时执行,通过引擎中的 Spawn
对象,你能让多个动作同时被解析执行。
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));
Cocos2d-x 的 节点关系,是被附属和附属的关系,就像数据结构中的父子关系,如果两个节点被添加到一个父子关系中,那么父节点的属性变化会被自动应用到子节点中。
需要注意的是,不是所有的父节点属性都会被自动应用到子节点,如改变父节点的锚点只会影响转换效果(比例缩放,位置变化,角度旋转,变形等),不会影响子节点锚点,子节点的锚点总会是左下角 (0,0)。
在游戏正在运行的时候,为了了解程序的运行过程或是为了查找一个 BUG,你想看到一些运行时信息,可以使用 log()
可以把信息输出到控制台:
// 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)
log("bool is true");
else
log("bool is false");