4.4 Node与Node层级架构
Cocos2d-x采用层级(树形)结构管理场景、层、精灵、菜单、文本、地图和粒子系统等节点(Node)对象。一个场景包含了多个层,一个层又包含多个精灵、菜单、文本、地图和粒子系统等对象。层级结构中的节点可以是场景、层、精灵、菜单、文本、地图和粒子系统等任何对象。
节点的层级结构如图4-15所示。
图4-15 节点的层级结构
Cocos2d-x JS API中的节点类是cc.Node,cc.Node类图如图4-16所示。cc.Node类是最为重要的根类,它是场景、层、精灵、菜单、文本、地图和粒子系统等类的根类。
图4-16 cc.Node类图
4.4.1 Node中重要的操作
作为根类,cc.Node有很多重要的方法,下面分别介绍:
□创建节点:var childNode = new cc.Node()。
□增加新的子节点:node.addChild(childNode,0,123),第二个参数Z轴绘制顺序,第三个参数是标签。
□查找子节点:var childNode = node.getChildByTag(123),通过标签查找子节点。
□node.removeChildByTag(123,true):通过标签删除子节点,并停止所有该子节点上的一切动作。
□node.removeChild(childNode,true):删除childNode节点,并停止所有该子节点上的一切动作。
□node.removeAllChildrenWithCleanup(true):删除node节点的所有子节点,并停止这些子节点上的一切动作。
□node.removeFromParentAndCleanup(true):从父节点删除node节点,并停止所有该节点上的一切动作。
4.4.2 Node中重要的属性
Node还有两个非常重要的属性:position和anchorPoint。
position(位置)属性是Node对象的实际位置,它往往需要配合anchorPoint属性使用。为了将一个Node对象(标准矩形)精准地放在屏幕某一个位置上,需要设置该矩形的anchorPoint(锚点)。anchorPoint属性是相对于position的比例,anchorPoint的计算公式是(w1/w2,h1/h2)。图4-17所示的锚点位于节点对象矩形内,w1是锚点到节点对象左下角的水平距离,w2是节点对象宽度;h1是锚点到节点对象左下角的垂直距离,h2是节点对象的高度。(w1/w2,h1/h2)计算结果为(0.5,0.5),所以anchorPoint为(0.5,0.5),anchorPoint的默认值就是(0.5,0.5)。
图4-17 anchorPoint为(0.5,0.5)
图4-18是anchorPoint为(0.66,0.5)的情况。
图4-18 anchorPoint为(0.66,0.5)
anchorPoint还有两个极端值:一个是锚点在节点对象矩形右上角,如图4-19所示,此时anchorPoint为(1,1);另一个是锚点在节点对象矩形左下角,如果图4-20所示,此时anchorPoint为(0,0)。
图4-19 anchorPoint为(1,1)
图4-20 anchorPoint为(0,0)
为了进一步了解anchorPoint的使用,我们修改HelloJS实例,修改app.js的ctor方法中的helloLabel代码:
var helloLabel = new cc.LabelTTF("Hello World", "Arial", 38); helloLabel.setPosition(size.width / 2, 0); ① // helloLabel.x = size.width / 2; ② // helloLabel.y = 0; ③ helloLabel.setAnchorPoint(cc.p(1.0, 1.0)); ④ // helloLabel.anchorX = 1.0; ⑤ // helloLabel.anchorY = 1.0; ⑥ this.addChild(helloLabel, 5);
上述代码第①行调用setPosition(x,y)方法设置position属性,也可以直接通过属性helloLabel.x和helloLabel.y设置(见第②行和第③行代码)。
第④行代码调用setAnchorPoint(x,y)方法设置anchorPoint属性,也可以直接通过属性helloLabel.anchorX和helloLabel.anchorY设置(见第⑤行和第⑥行代码)。
此外,由于有多个属性需要设置,我们可以通过helloLabel.attr({…})语句进行设置,代码如下:
helloLabel.attr({ x: size.width / 2, y: 0, anchorX: 1.0, anchorY: 1.0 });
运行结果如图4-21所示,helloLabel设置anchorPoint为(1.0,1.0)。
图4-21 helloLabel的anchorPoint为(1.0,1.0)
4.4.3 游戏循环与调度
每一个游戏程序都有一个循环在不断运行,它是由导演对象来管理和维护。如果需要场景中的精灵运动起来,可以在游戏循环中使用定时器(cc.Scheduler)对精灵等对象的运行进行调度。因为cc.Node类封装了cc.Scheduler类,所以也可以直接使用cc.Node中定时器的相关方法。
cc.Node中定时器的相关方法主要有:
□scheduleUpdate():每个Node对象只要调用该方法,那么这个Node对象就会定时地每帧回调一次自己的update(dt)方法。
□schedule(callback_fn,interval,repeat,delay):与scheduleUpdate方法功能一样,不同的是可以指定回调方法(通过callback_fn指定)。interval是时间间隔;repeat是执行的次数;delay延迟执行的时间。
□unscheduleUpdate():停止update(dt)方法调度。
□unschedule(callback_fn):指定具体方法停止调度。
□unscheduleAllCallbacks():停止所有的调度。
为了进一步了解游戏循环与调度的使用,我们修改HelloJS实例。修改app.js代码,添加update(dt)声明,代码如下:
var HelloWorldLayer = cc.Layer.extend({ sprite: null, ctor: function () { …… var closeItem = new cc.MenuItemImage( res.CloseNormal_png, res.CloseSelected_png, function () { cc.log("Menu is clicked!"); this.unscheduleUpdate(); }, this); …… var helloLabel = new cc.LabelTTF("Hello World", "Arial", 38); helloLabel.attr({ x: size.width / 2, y: 0, anchorX: 1.0, anchorY: 1.0 }); helloLabel.setTag(123); ① //更新方法 this.scheduleUpdate(); ② //this.schedule(this.update, 1.0/60, cc.REPEAT_FOREVER, 0.1); ③ this.addChild(helloLabel, 5); // add "HelloWorld" splash screen" this.sprite = new cc.Sprite(res.HelloWorld_png); this.sprite.attr({ x: size.width / 2, y: size.height / 2, scale: 0.5, rotation: 180 }); this.addChild(this.sprite, 0); this.sprite.runAction( cc.sequence( cc.rotateTo(2, 0), cc.scaleTo(2, 1, 1) ) ); helloLabel.runAction( cc.spawn( cc.moveBy(2.5, cc.p(0, size.height - 40)), cc.tintTo(2.5, 255, 125, 0) ) ); return true; }, update: function (dt) { ④ var label = this.getChildByTag(123); ⑤ label.x = label.x + 0.2; ⑥ label.y = label.y + 0.2; ⑦ } });
为了能够在ctor方法之外访问标签对象helloLabel,需要为标签对象设置Tag属性,其中第①行代码就是设置Tag属性为123。第⑤行代码是通过Tag属性重新获得这个标签对象。
为了能够开始调度,还需要在ctor方法中调用scheduleUpdate(见第②行代码)或schedule(见第③行代码)。
第④行代码的update(dt)方法是调度方法。精灵等对象的变化逻辑都是在这个方法中编写的。这个例子很简单,只是让标签对象动起来,第⑥行代码就是改变它的位置。
为了省电等目的,如果不再使用调度,一定不要忘记停止调度。可以在Close菜单项的点击事件中停止调度,代码如下:
var closeItem = new cc.MenuItemImage( res.CloseNormal_png, res.CloseSelected_png, function () { this.unscheduleUpdate(); }, this);
代码this.unscheduleUpdate()就是停止调度update,如果是其他的调度方法可以采用unschedule或unscheduleAllSelectors停止。