【唠叨】

    与2.x相比,节点类Node的属性和功能做了大幅度的修改与增加




【Node】

    Node类是绝大部分类的父类(并不是所有的类,例如Director类是直接继承Ref类的),如Scene、Layer、Sprite以及精灵集合SpriteBatchNode等等等等的父类都是Node。

    Node类包含了一些基本的属性、节点相关、Action动作的执行、以及定时器等相关的操作。

    当然Node也有父类,其父类为Ref。

    继承关系如下:

wKiom1QOafTQMRvyAAAXab9q2nY179.jpg

    一个节点的主要特点:

> 他们可以包含其他的节点对象(addChild, getChildByTag, removeChild, etc)

他们可以安排定期的回调(schedule, unschedule, etc)

他们可以执行一些动作(runAction, stopAction, etc)

子类节点通常意味着(单一的/所有的):

重写初始化资源并且可以安排回调

创建回调来操作进行的时间

重写“draw”来渲染节点

节点的属性有:位置、缩放、旋转、倾斜、锚点、内容大小、可见性


    下面将介绍一下节点主要常用的一些操作函数,以及新的功能特性。

    (1)节点的属性

    (2)节点的操作

    (3)动作相关Action

    (4)定时器相关schedule

    (5)整合NodeRBGA类

    (6)查找子节点enumerateChildren

    (7)渲染顺序zOrder

    (8)坐标转换


1、节点的属性

    节点的属性有:位置、缩放、旋转、倾斜、锚点、内容大小、可见性。

//
/**
 *	位置Position
 *		设置节点的坐标(x,y).在OpenGL中的坐标
 *		增加3D坐标
 *		增加标准化坐标设置
 */
	virtual void setPosition(const Vec2 &position); //Vec2坐标
	virtual void setPosition(float x, float y);     //(x,y),比Vec2更有效率
	virtual void setPositionX(float x);
	virtual void setPositionY(float y);
	virtual const Vec2& getPosition() const;
	virtual void  getPosition(float* x, float* y) const;
	virtual float getPositionX(void) const;
	virtual float getPositionY(void) const;
	
//增加3D坐标
	virtual void setPosition3D(const Vec3& position); //Vec3坐标
	virtual Vec3 getPosition3D() const;
	virtual void setPositionZ(float positionZ);
	virtual float getPositionZ() const;
	
//增加标准化坐标设置
	//Node的位置像素会根据它的父节点的尺寸大小计算
	//Size s = getParent()->getContentSize();
	//_position = pos * s;
	virtual void setNormalizedPosition(const Vec2 &position);
	virtual const Vec2& getNormalizedPosition() const;


/**
 *	放缩Scale
 *		设置节点的放缩比例. 对XYZ轴进行放缩
 *		例如一张图片. 放缩它的宽X,高Y,深Z
 */
	virtual void setScaleX(float scaleX);              //放缩宽X
	virtual void setScaleY(float scaleY);              //放缩高Y
	virtual void setScaleZ(float scaleZ);              //放缩深Z
	virtual void setScale(float scaleX, float scaleY); //X放缩fScaleX倍,Y放缩fScaleY倍
	virtual void setScale(float scale);                //XYZ同时放缩scale倍
	virtual float getScaleX() const;
	virtual float getScaleY() const;
	virtual float getScaleZ() const;
	virtual float getScale()  const;                   //当x,y放缩因子相同时,得到该节点的缩放因子


/**
 *	旋转Rotation
 *		设置节点的旋转角度. 负顺,正逆时针旋转
 *		增加3D旋转
 */
	virtual void setRotation(float rotation);
	virtual float getRotation() const;

//增加3D旋转
	virtual void setRotation3D(const Vec3& rotation); 
	virtual Vec3 getRotation3D() const;


/**
 *	倾斜Skew
 *		设置XY轴的倾斜角度
 *		setRotationalSkew()  模拟Flash的倾斜功能
 *		setSkew()            使用真正的倾斜功能
 */
	virtual void setSkewX(float skewX); //水平旋转倾斜.负顺时针变形
	virtual void setSkewY(float skewY); //垂直旋转倾斜
	virtual void setRotationSkewX(float rotationX);
	virtual void setRotationSkewY(float rotationY);
	virtual float getSkewX() const;
	virtual float getSkewY() const;
	virtual float getRotationSkewX() const;
	virtual float getRotationSkewY() const;


/**
 *	锚点AnchorPoint
 *		锚点就像一枚图钉,将图片钉在屏幕上.而锚点就是图片的坐标.
 *		当然图钉可以钉在图片的左下角,右上角,或者中心都可以.
 *		(0,0)表示左下角,(1,1)表示右上角
 *		默认的锚点是(0.5,0.5),即节点的正中心
 */
	virtual void setAnchorPoint(const Vec2& anchorPoint); //标准化的锚点
	virtual const Vec2& getAnchorPoint() const;           //标准化的锚点
	virtual const Vec2& getAnchorPointInPoints() const;   //返回绝对像素的锚点,即屏幕坐标

//是否忽略锚点的设置
	//若忽略锚点设置,锚点永远为(0,0)
	//默认值是false, 但是在Layer和Scene中是true
	//这是一个内部方法,仅仅被Layer和Scene使用,不要自行调用!
	virtual void ignoreAnchorPointForPosition(bool ignore);
	virtual bool isIgnoreAnchorPointForPosition() const;


/**
 *	内容大小ContentSize
 *		contentSize依然是相同的,无论节点是缩放或者旋转 
 *		所有的节点都有大小,图层和场景有相同的屏幕大小
 */
	virtual void setContentSize(const Size& contentSize);
	virtual const Size& getContentSize() const;

/**
 *	可见性Visible
 */
	virtual void setVisible(bool visible);
	virtual bool isVisible() const;
//


2、节点的操作

    节点的操作有:标记、名字、自定义数据、父节点管理、子节点管理、其他操作管理。

//
/**
 *	标记与名字 Tag and Name
 *		setTag  : 给节点设置一个编号
 *		setName : 给节点设置一个名字
 */
	virtual void setTag(int tag);
	virtual void setName(const std::string& name);
	virtual int getTag() const;
	virtual std::string getName() const;


/**
 *	自定义数据UserData/Object
 *		setUserData   : 设置一个用户自定义的数据. 可以为一个数据块, 结构体或者一个对象.
 *		setUserObject : 设置一个用户自定义的对象. 和userData类似, 但它是一个对象而不是void*
 */
	virtual void setUserData(void *userData);
	virtual void setUserObject(Ref *userObject);
	virtual void* getUserData();
	virtual Ref* getUserObject();


/**
 *	设置父节点Parent
 *		setParent , removeFromParent
 */
	virtual void setParent(Node* parent);
	virtual Node* getParent();
	virtual void removeFromParent();
	virtual void removeFromParentAndCleanup(bool cleanup); //true则删除该节点的所有动作及回调函数.


/**
 *	管理子节点Child
 *		addChild , 
 *		getChildBy** , getChildren       , getChildrenCount
 *		removeChild  , removeAllChildren
 *		reorderChild , sortAllChildren
 */
	//添加子节点
	//localZOrder	Z轴顺序为了绘画的优先权
	//tag			节点编号,可通过tag获取子节点
	//name      	节点名字,可通过name获取子节点
	virtual void addChild(Node * child);
	virtual void addChild(Node * child, int localZOrder);
	virtual void addChild(Node* child, int localZOrder, int tag);
	virtual void addChild(Node* child, int localZOrder, const std::string &name);

	//获取子节点
	virtual Node* getChildByTag(int tag) const;
	virtual Node* getChildByName(const std::string& name) const;
	virtual Vector& getChildren();     //获得所有子节点,并以Vector数组返回
	virtual ssize_t getChildrenCount() const; //子节点总数

	//删除子节点
	virtual void removeChild(Node* child, bool cleanup = true);
	virtual void removeChildByTag(int tag, bool cleanup = true);
	virtual void removeChildByName(const std::string &name, bool cleanup = true);
	virtual void removeAllChildren();                        //删除所有节点
	virtual void removeAllChildrenWithCleanup(bool cleanup); //cleanup为true则删除子节点的所有动作

	//重排子节点
	//重新排序一个子节点,设定一个新的z轴的值
	//child			它必须是已经添加的
	//localZOrder	Z轴顺序为了绘画优先级
	virtual void reorderChild(Node * child, int localZOrder);
	virtual void sortAllChildren(); //重新排序所有子节点


/**
 *	其他操作管理
 */
	virtual void onEnter();                    //节点开始进入舞台时调用.即创建时调用.
	virtual void onEnterTransitionDidFinish(); //节点进入舞台后调用.即创建完后调用.
	virtual void onExit();                     //节点离开舞台时调用.即移除时调用
	virtual void onExitTransitionDidStart();   //节点离开舞台前调用.

	//绘制节点
	virtual void draw() final;
	//递归访问所有子节点,并重新绘制	
	virtual void visit() final;

	//返回包含Node(节点)的Scene(场景). 
	//若不属于任何的场景,它将返回nullptr
	virtual Scene* getScene() const;
	
	//返回节点在父节点坐标中的矩形边界框
	virtual Rect getBoundingBox() const;
	
	//暂停所有的活动着的动作和调度器
	virtual void cleanup();
//


3、动作相关Action

    动作管理:运行、暂停、取消等操作。

//
/**
 *	动作管理Action
 *		setActionManager
 *		runAction , stopAction , getActionByTag , getNumberOfRunningActions
 */
	//设置被所有动作使用的ActionManager对象
	//如果你设置了一个新的ActionManager, 那么之前创建的动作将会被删除
	virtual void setActionManager(ActionManager* actionManager);
	virtual ActionManager* getActionManager();

	Action* runAction(Action* action);          //执行一个动作
	Action* getActionByTag(int tag);            //获取动作, 根据tag标记
	void stopAllActions();                      //暂停动作
	void stopAction(Action* action);            //暂停动作
	void stopActionByTag(int tag);              //暂停动作
	ssize_t getNumberOfRunningActions() const;  //获取正在运行的动作数量
//


4、定时器相关schedule

    定时器管理,默认定时器、自定义定时器、一次性定时器。

//
/**
 *	定时器管理schedule
 *		setScheduler
 *		scheduleUpdate : 默认定时器
 *		schedule       : 自定义定时器
 *		scheduleOnce   : 一次性定时器
 */
	//设置一个调度器对象,来调度所有的“update”和定时器
	//如果你设置了一个新的调度器,那么之前创建的timers/update将会被删除。
	virtual void setScheduler(Scheduler* scheduler);
	virtual Scheduler* getScheduler(); //得到调度器对象

	//开启默认定时器.刷新次数为60次/秒.即每秒60帧.
	//与update(float delta)回调函数相对应.
	//给予定时器优先级priority.其中priority越小,优先级越高
	void scheduleUpdate(void);
	void scheduleUpdateWithPriority(int priority);
	void unscheduleUpdate(void);      //取消默认定时器
	virtual void update(float delta); //update为scheduleUpdate定时器的回调函数.

	//设置自定义定时器.默认为每秒60帧.
	//interval  :   每隔interval秒,执行一次.
	//repeat    :   重复次数.
	//delay     :   延迟时间,即创建定时器delay后开始执行.
	void schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay);
	void schedule(SEL_SCHEDULE selector, float interval);
	void schedule(SEL_SCHEDULE selector);                  //默认为每秒60帧
	void scheduleOnce(SEL_SCHEDULE selector, float delay); //只执行一次,delay秒后执行

	void unschedule(SEL_SCHEDULE selector);                //取消一个自定义定时器
	void unscheduleAllSelectors(void);                     //取消所有定时器
	void resume(void);                                     //恢复所有定时器和动作
	void pause(void);                                      //暂停所有定时器和动作
//


5、整合NodeRGBA类

    整合NodeRGBA类,增加颜色、透明度的设置。

//
/**
 *	整合NodeRGBA类
 *		setOpacity : 透明度
 *		setColor   : 颜色
 */
	virtual GLubyte getOpacity() const;
	virtual GLubyte getDisplayedOpacity() const;
	virtual void setOpacity(GLubyte opacity);

	virtual const Color3B& getColor() const;
	virtual const Color3B& getDisplayedColor() const;
	virtual void setColor(const Color3B& color);
//


6、enumerateChildren

    新增的Node::enumerateChildren方法,且支持C++ 11的正则表达式

    用于枚举某个Node节点的子节点,并让名字符合"name通配符"的子节点执行callback函数。

    且callback函数返回类型应该为一个bool值,并且返回为true时,结束查找。

//
    virtual void enumerateChildren(const std::string &name, std::function callback) const;
//

    使用举例:

//
	//Find nodes whose name is 'nameToFind' and end with digits. 
	node->enumerateChildren("nameToFind[[:digit:]]+", 
		[](Node* node) -> bool { ... return false; // return true to stop at first match });

	//Find nodes whose name is 'nameToFind' and end with digits recursively. 
	node->enumerateChildren("nameToFind[[:digit:]]+", 
		[](Node* node) -> bool { ... return false; // return true to stop at first match });
//

    通配符匹配举例:

//
	//搜索语法选项
	'//' : 递归访问所有子节点,   只能放在搜索串的开头位置
	'..' : 搜索移至node的父节点, 只能放在某个字符串的结束位置
	'/'  : 搜索移至node的子节点, 可以放在任何位置,除了搜索串的开头位置
	 
	//代码举例
	enumerateChildren("//MyName", ...)     : 递归访问Node的所有子节点。查找匹配 "MyName" 的子节点
	enumerateChildren("[[:alnum:]]+", ...) : 在Node的儿子节点中查找。  所有项
	enumerateChildren("A[[:digit:]]", ...) : 在Node的儿子节点中查找。  名字为 "A0","A1",...,"A9" 的子节点
	enumerateChildren("Abby/Normal", ...)  : 在Node的孙子节点中查找。  其节点为"Normal",且父节点为"Abby"
	enumerateChildren("//Abby/Normal", ...): 递归访问Node的所有子节点。其节点为"Normal",且父节点为"Abby"
//


    注意:使用gccV4.8或更低版本构建的程序运行时会崩溃。由于OTHER_LDFLAGS不能在Xcode6 beta3中使用,在iOS上我们使用包括64位库文件fat library。但Xcode 5.0或更低版本不支持这种方式。

    所以:

        * Android编译需要NDK R9D以上版本

        * Linux编译需要GCC4.9以上版本

        * iOS编译需要 Xcode5.1以上版本


7、渲染顺序

    在2.x中是使用Zorder来控制节点渲染的先后顺序的。而在3.x中渲染的顺序则是由两个因素决定。

        > 全局Z顺序:GlobalZOrder。所有节点之间对比。

        > 局部Z顺序:LocalZOrder。 兄弟节点之间对比。

    且Z顺序越小,最先渲染。


  7.1、全局Z顺序

    > 定义渲染节点的顺序,拥有全局Z顺序越小的节点,最先渲染。

    > 假设:两个或者更多的节点拥有相同的全局Z顺序,那么渲染顺序无法保证。唯一的例外是如果节点的全局Z顺序为零,那么场景图顺序是可以使用的。

    > 默认的,所有的节点全局Z顺序都是零。这就是说,默认使用场景图顺序来渲染节点。

    > 全局Z顺序是非常有用的当你需要渲染节点按照不同的顺序而不是场景图顺序。

    > 局限性: 全局Z顺序不能够被拥有继承“SpriteBatchNode”的节点使用。

//
	virtual void setGlobalZOrder(float globalZOrder);
	virtual float getGlobalZOrder() const;
//


  7.2、局部Z顺序

    > LocalZOrder是“key”(关键)来分辨节点和它兄弟节点的相关性。

    > 父节点将会通过LocalZOrder的值来分辨所有的子节点。如果两个节点有同样的LocalZOrder,那么先加入子节点数组的节点将会比后加入的节点先得到渲染,那么先加入的节点会被后加入的节点遮盖[update 20140927]。

    > 同样的,场景图使用“In-Order(按顺序)”遍历数算法来遍历。并且拥有小于0的LocalZOrder的值的节点是“left”子树(左子树) 所以拥有大于0的LocalZOrder的值得节点是“right”子树(右子树)。

//
	//设置这个节点的局部Z顺序((相对于兄弟节点))
	virtual void setLocalZOrder(int localZOrder);
	virtual int getLocalZOrder() const;
//


8、坐标转换

    参考:http://www.tairan.com/archives/5739

   

  8.1、坐标转换函数

    坐标分为两种坐标:

        > 世界坐标:就是相对节点的世界坐标。

        > 局部坐标:就是相对节点的坐标。

//
/**
 *	坐标转换convertTo
 *		WorldSpace :	世界坐标
 *		NodeSpace  :	局部坐标
 *
 */
	//把世界坐标, 转换到当前节点的本地坐标系中
	//基于Anchor Point, 把基于当前节点的本地坐标系下的坐标, 转换到世界坐标系中
	Vec2 convertToNodeSpace(const Vec2& worldPoint) const;
	Vec2 convertToNodeSpaceAR(const Vec2& worldPoint) const;


	//把基于当前节点的本地坐标系下的坐标, 转换到世界坐标系中
	//基于Anchor Point. 把世界坐标, 转换到当前节点的本地坐标系中
	Vec2 convertToWorldSpace(const Vec2& nodePoint) const;
	Vec2 convertToWorldSpaceAR(const Vec2& nodePoint) const;
	
	
	//把触点坐标, 转换到当前节点的本地坐标系中
	Vec2 convertTouchToNodeSpace(Touch * touch) const;
	Vec2 convertTouchToNodeSpaceAR(Touch * touch) const;
//


  8.2、举例

cocos2dx[3.2](7) ——节点类Node_第1张图片        cocos2dx[3.2](7) ——节点类Node_第2张图片

    > node1坐标为:(20,40), 锚点(0,0)。

    > node2坐标为:(-5,-20),锚点(1,1)。

    > Vec2 point1 = node1->convertToNodeSpace(node2->getPosition());

        结果为:(-25,-60)。(即:相对node1节点坐标位置,的,相对坐标)

        分析  :node2相对node1节点的坐标为:(-5,-20) - (20,40) 。

                也就是说:node2相对node1的坐标位置。

    > Vec2 point2 = node1->convertToWorldSpace(node2->getPosition());

        结果为:(15,20)。(即:相对node1的世界坐标系统下,的,世界坐标)

        分析  :将node1作为参照,转换到世界坐标为:(20,40) + (-5,-20) 。

                也就是说:node2的坐标现在被看做是相对node1的坐标位置,然后转换到世界坐标。


  8.3、在屏幕触控中的应用

    判断触摸点是否触摸到某个精灵图片sp的内部区域。

//
	bool HelloWorld::onTouchBegan(Touch *touch, Event *unused_event)
	{
		//将触点坐标, 转换为相对节点sp的, 相对坐标
		Vec2 point = sp->convertTouchToNodeSpace(touch);

		//构造sp的尺寸矩形
		Size size = sp->getContentSize();
		Rect rect = Rect(0, 0, size.width, size.height);

		//判断触点是否触摸到sp内部
		if (rect.containsPoint(point)) {
			CCLog("点中");
			return true;
		}

		return false;
	}
//