Cocos2d之Node类详解之节点树(一)

一、声明

笔者分析的是用C++语言实现、版本号为cocos2d-x-3.3rc0的cocos2d框架的源代码。本文为笔者原创,允许读者分享和转载,只要读者注明文章来源即可。

二、简介

Node对象时场景图的基本元素,并且场景图的基本元素必须是Node对象和Node的子类对象。常见的Node类的子类有:Scene、Layer、Sprite、Menu和Label类。

Node类主要实现几个特性:

  • Node对象的 addChild(Node *child)、getChildByTag(int tag)、removeChild(Node *child, bool cleanup=true) 能够使其持有别的Node对象作为其子节点。

  • Node对象的调度器能够定时的调用毁掉函数。
  • Node对象能够执行动作(动作由Action对象表示)。

Node子类一般实现下面几点:

  • 重写Node类的init函数,使子类能够初始化资源和回调函数。

  • 为Node子类编写回调函数,并交由调度器定时调用。
  • 重写draw函数来渲染Node子类。

Node类有下面几个常用属性:

  • position(位置)。此属性表示Node对象的中心点在坐标系中渲染的位置,默认初始化成(x = 0, y = 0)。

  • anchor point(锚点)。默认为(x = 0, y = 0),但是Node的子类的初始值可能会有差异。
  • scale(缩放)。默认宽和高的缩放比例都为1.
  • rotation(旋转)。此属性表示顺时针旋转的角度,默认是0度。
  • contentSize(内容大小)。默认长和宽都为0.
  • visible(可见性)。默认为true。

这些属性会在后续的源码分析中做具体介绍。

三、源码详解

Node比较庞大,笔者打算在多篇博客中分别详细介绍Node节点的不同模块。前面说到Node对象能够持有其他Node对象作为其子节点,也就是说一个Node对象其实能够扩展出一个节点树。所以笔者先介绍节点树模块。

节点树实现

添加子节点

添加子节点的过程需要到下面的属性。

int _localZOrder;               ///< Local order (relative to its siblings) used to sort the node
float _globalZOrder;            ///< Global order used to sort the node
Vector<Node*> _children;        ///< array of children nodes
Node *_parent;                  ///< weak reference to parent node
int _tag;                         ///< a tag. Can be any number you assigned just to identify this node
std::string _name;               ///<a string label, an user defined string to identify this node
int _orderOfArrival;            ///< used to preserve sequence while sorting children with the same localZOrder
bool _running;                  ///< is running

下面看此addChild函数的声明。

/**
     * Adds a child to the container with z order and tag
     *
     * If the child is added to a ‘running‘ node, then ‘onEnter‘ and ‘onEnterTransitionDidFinish‘ will be called immediately.
     *
     * @param child     A child node
     * @param zOrder    Z order for drawing priority. Please refer to `setLocalZOrder(int)`
     * @param tag       An integer to identify the node easily. Please refer to `setTag(int)`
     *
     * Please use `addChild(Node* child, int localZOrder, const std::string &name)` instead.
     */
     virtual void addChild(Node* child, int localZOrder, int tag);
    /**
     * Adds a child to the container with z order and tag
     *
     * If the child is added to a ‘running‘ node, then ‘onEnter‘ and ‘onEnterTransitionDidFinish‘ will be called immediately.
     *
     * @param child     A child node
     * @param zOrder    Z order for drawing priority. Please refer to `setLocalZOrder(int)`
     * @param name      A string to identify the node easily. Please refer to `setName(int)`
     *
     */
    virtual void addChild(Node* child, int localZOrder, const std::string &name);

LocalZOrder参数决定了子节点被添加到节点树的位置,子节点在节点树中的位置决定了节点显示的顺序。关于节点树的遍历会在后续的博客中介绍,读者现在只需要知道LocalZOrder取值从负轴到正轴,显示顺序递减。

函数声明还提到,如果当前父节点处于running状态,那么被添加的子节点会被立刻调用onEnter和onEnterTransitionDidFinish函数。下面看此函数的具体实现。

void Node::addChild(Node *child, int localZOrder, int tag)
{
    CCASSERT( child != nullptr, "Argument must be non-nil");
    CCASSERT( child->_parent == nullptr, "child already added. It can‘t be added again");

    addChildHelper(child, localZOrder, tag, "", true);
}

void Node::addChild(Node* child, int localZOrder, const std::string &name)
{
    CCASSERT(child != nullptr, "Argument must be non-nil");
    CCASSERT(child->_parent == nullptr, "child already added. It can‘t be added again");

    addChildHelper(child, localZOrder, INVALID_TAG, name, false);
}

这两个函数都调用了一个私有函数 void addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)。下面看该函数的实现。

void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
{
    if (_children.empty())
    {
        this->childrenAlloc();
    }

    this->insertChild(child, localZOrder);

    if (setTag)
        child->setTag(tag);
    else
        child->setName(name);

    child->setParent(this);

    /* 笔者注
     * 设置节点到达顺序,如果节点树中不同节点有相同的LocalZOrder时,
     * 到达顺序小的节点先画
     */
    child->setOrderOfArrival(s_globalOrderOfArrival++);

    /* 笔者注
     * 如果使用了物理引擎,需要为节点添加物理世界的性质
     */
#if CC_USE_PHYSICS
    // Recursive add children with which have physics body.
    Scene* scene = this->getScene();
    if (scene != nullptr && scene->getPhysicsWorld() != nullptr)
    {
        child->updatePhysicsBodyTransform(scene);
        scene->addChildToPhysicsWorld(child);
    }
#endif

    if( _running )
    {
        child->onEnter();
        // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
        if (_isTransitionFinished) {
            child->onEnterTransitionDidFinish();
        }
    }

    if (_cascadeColorEnabled)
    {
        updateCascadeColor();
    }

    if (_cascadeOpacityEnabled)
    {
        updateCascadeOpacity();
    }
}

从实现源码不难看出,所有的子节点都被保存到 _children 数组中。如果父节点不处于 _running 状态,那么子节点在添加时就不会被调用 onEnter和onEnterTransitionDidFinished函数,这会产生什么影响笔者今后再做补充。

四、结束

本文就先介绍Node类实现往父节点的节点树添加子节点的过程。下一篇博客会继续介绍Node类节点树的实现。

时间: 2024-11-25 00:11:32

Cocos2d之Node类详解之节点树(一)的相关文章

Cocos2d之Node类详解之节点树(二)

一.声明 本文属于笔者原创,允许读者转载和分享,只要注明文章来源即可. 笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析.这篇文章承接上篇<Cocos2d之Node类详解之节点树(一)>. 二.简介 节点 一个Node对象. 节点树 上篇文章介绍到,Node类有一个成员变量 Vector<Node*> _children,这是一个保存所有子节点的数组,因为Node类采用遍历树的方式获取子节点进行渲染,所以我管这两个东西的结合叫节点树. 三.源码详解 &

Cocos2d之Node类详解之ZOrder详解

一.声明 笔者以cocos2d框架的cocos2d-x-3.3rc0版本源码做分析.本文属于笔者原创,允许转载和分享,但请注明文章出处. 二.简介 ZOrder ZOrder顾名思义就是节点(Node对象)在Z轴上的排序,这样一来ZOrder越小就越优先显示.每个节点(Node对象)可以持有多个子节点,组成节点树(关于节点树的介绍查看<Cocos2d之Node类详解之节点树>一文).ZOder表示了节点树中每个子节点显示的优先级.值得注意的是,节点树中子节点的ZOder可能会一样,这种情况下父

Cocos2d之Texture2D类详解之将文件加载成Texture2D对象

一.声明 笔者以cocos2d框架cocos2d-x-3.3rc0版本的源码做分析.本文为笔者原创,允许转载和分享,只要注明文章出处即可. 二.简介 Texture2D类简介 Texture2D类允许开发者用图像.文本信息和简单的数据来创建OpenGL2D纹理.被创建的纹理拥有两个维度.根据开发者创建Texture2D对象方式的不同,实际图像的尺寸可能比生成的纹理的尺寸要小,而且纹理的内容是倒置的. 像素格式 在计算机图形学中,人们用每个像素在内存中的总位数以及分别存储红.蓝.绿和alpha(阿

Cocos2d之Action类详解

一.声明 文章中使用到的cocos2d的源代码的版本是cocos2d-x-3.3rc0. 二.主要内容 [Action类简介] 在cocos2d中,Action类是所有动作的基类.Action类继承了Ref类和Clonable类. [Action类的声明源码] 声明的源码在 CCAction.h 文件中,声明如下: /** @brief Base class for Action objects. */ class CC_DLL Action : public Ref, public Clona

QAction类详解:

先贴一段描述:Qt文档原文: Detailed Description The QAction class provides an abstract user interface action that can be inserted into widgets. In applications many common commands can be invoked via menus, toolbar buttons, and keyboard shortcuts. Since the user

Android技术18:Android中Adapter类详解

1.Adapter设计模式 Android中adapter接口有很多种实现,例如,ArrayAdapter,BaseAdapter,CursorAdapter,SimpleAdapter,SimpleCursorAdapter等,他们分别对应不同的数据源.例如,ArrayAdater对应List和数组数据源,而CursorAdapter对应Cursor对象(一般从数据库中获取的记录集).这些Adapter都需要getView方法返回当前列表项显示的View对象.当Model发生改变时,会调用Ba

C++虚基类详解

1.虚基类的作用从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员.在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如    c1.A::display( ).在一个类中保留间接共同基类的多份同名成员,这种现象是人们不希望出现的.C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员.现在,将类A声明为

URLConnection类详解

为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3753224.html URLConnection概述 URLConnection是一个抽象类,表示指向URL指定资源的活动连接. URLConnection类本身依赖于Socket类实现网络连接.一般认为,URLConnection类提供了比Socket类更易于使用.更高级的网络连接抽象.但实际上,大多数程序员都会忽略它

ThreadLocal类详解

众所周知,ThreadLocal对象可以每一个线程保存一份值,可以避免因线程间共享数据带来的问题. 其实现的原理,大致如下,具体的可以参考JDK里的源码. Thread类中,有一个threadLocals字段,它是ThreadLocalMap类型(ThreadLocal里的一个静态内部类).该字段存放当前线程下,所有与ThreadLocal相关的值.该对象是一个Map,key为ThreadLocal对象,value为所存放的值. 在ThreadLocal类里,有两个重要的方法:set()和get