Skip to content

cocos2d x 3.3 007 核心概念和相关类 节点

David edited this page Jan 14, 2015 · 5 revisions

Node概述

  • 继承自Ref
  • 来自代码的详细说明
Node is the base element of the Scene Graph. Elements of the Scene Graph must be Node objects or subclasses of it.
 The most common Node objects are: Scene, Layer, Sprite, Menu, Label.

 The main features of a Node are:
 - They can contain other Node objects (`addChild`, `getChildByTag`, `removeChild`, etc)
 - They can schedule periodic callback (`schedule`, `unschedule`, etc)
 - They can execute actions (`runAction`, `stopAction`, etc)

 Subclassing a Node usually means (one/all) of:
 - overriding init to initialize resources and schedule callbacks
 - create callbacks to handle the advancement of time
 - overriding `draw` to render the node

 Properties of Node:
 - position (default: x=0, y=0)
 - scale (default: x=1, y=1)
 - rotation (in degrees, clockwise) (default: 0)
 - anchor point (default: x=0, y=0)
 - contentSize (default: width=0, height=0)
 - visible (default: true)

 Limitations:
 - A Node is a "void" object. If you want to draw something on the screen, you should use a Sprite instead. Or subclass Node and override `draw`.

主要API

setter getter for Properties

  • zOrder scale position skew rotation AnchorPoint ContentSize ...

tag and name

  • set get

user data/object

  • set get

matrix

  • get set

shader

  • get set

parent操作

  • set remove

child操作

  • add remove get sort

游戏生命周期

  • isRunning onEnter onEnterTransitionDidFinish onExit onExitTransitionDidStart cleanup

绘图

  • draw visit

actions相关

  • runAction stopAction stopAllActions stopActionByTag getActionByTag

调度器相关

  • setScheduler getScheduler scheduleUpdate scheduleUpdateWithPriority unscheduleUpdate schedule scheduleOnce unschedule unscheduleAllCallbacks update resume pause

坐标转换

  • convertToNodeSpace convertToWorldSpace convertTouchToNodeSpace

物理引擎相关

  • setPhysicsBody getPhysicsBody removeFromPhysicsWorld

走马观花看代码

* opengl/物理引擎相关的略过

1. 构造函数
Node::Node(void)
: _rotationX(0.0f)
//略过初始化列表
{
//导演的几个骨干业务员跑来兼职了
    // set default scheduler and actionManager
    Director *director = Director::getInstance();
//动作管理器
    _actionManager = director->getActionManager();
    _actionManager->retain();
//调度器
    _scheduler = director->getScheduler();
    _scheduler->retain();
//事件分发器
    _eventDispatcher = director->getEventDispatcher();
    _eventDispatcher->retain();
    
#if CC_ENABLE_SCRIPT_BINDING
    ScriptEngineProtocol* engine = ScriptEngineManager::getInstance()->getScriptEngine();
    _scriptType = engine != nullptr ? engine->getScriptType() : kScriptTypeNone;
#endif
    _transform = _inverse = _additionalTransform = Mat4::IDENTITY;
}


2. 看看cleanup是怎么进行扫尾工作的
void Node::cleanup()
{
//停止所有动作和调度器回调
    // actions
    this->stopAllActions();
    this->unscheduleAllCallbacks();

#if CC_ENABLE_SCRIPT_BINDING
    if ( _scriptType != kScriptTypeNone)
    {
        int action = kNodeOnCleanup;
        BasicScriptData data(this,(void*)&action);
        ScriptEvent scriptEvent(kNodeEvent,(void*)&data);
        ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
    }
#endif // #if CC_ENABLE_SCRIPT_BINDING

//遍历每一个child对象执行cleanup
    // timers
    for( const auto &child: _children)
        child->cleanup();
}

3.add child
/* "add" logic MUST only be on this method
* If a class want's to extend the 'addChild' behavior it only needs
* to override this method
*/
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::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
{
    if (_children.empty())
    {
//分配空间
        this->childrenAlloc();
    }
    
//插入
    this->insertChild(child, localZOrder);
    
//set tag or name
    if (setTag)
        child->setTag(tag);
    else
        child->setName(name);
    
    child->setParent(this);
    child->setOrderOfArrival(s_globalOrderOfArrival++);
    
#if CC_USE_PHYSICS
    // Recursive add children with which have physics body.
    auto scene = this->getScene();
    if (scene && scene->getPhysicsWorld())
    {
//不懂啦,反正和物理引擎相关
        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();
    }
}

// helper used by reorderChild & add
void Node::insertChild(Node* child, int z)
{
    _transformUpdated = true;
    _reorderChildDirty = true;
//加入vcetor并设置zorder
    _children.pushBack(child);
    child->_localZOrder = z;
}


4.remove child
/* "remove" logic MUST only be on this method
* If a class want's to extend the 'removeChild' behavior it only needs
* to override this method
*/
void Node::removeChild(Node* child, bool cleanup /* = true */)
{
    // explicit nil handling
    if (_children.empty())
    {
        return;
    }

    ssize_t index = _children.getIndex(child);
    if( index != CC_INVALID_INDEX )
        this->detachChild( child, index, cleanup );
}

void Node::detachChild(Node *child, ssize_t childIndex, bool doCleanup)
{
    // IMPORTANT:
    //  -1st do onExit
    //  -2nd cleanup
    if (_running)
    {
//跟add child时的onEnter onEnterTransitionDidFinish 遥相呼应
        child->onExitTransitionDidStart();
        child->onExit();
    }
    
#if CC_USE_PHYSICS
    child->removeFromPhysicsWorld();
#endif

    // If you don't do cleanup, the child's actions will not get removed and the
    // its scheduledSelectors_ dict will not get released!
    if (doCleanup)
    {
//清场,停止动作和timer调度
        child->cleanup();
    }

    // set parent nil at the end
    child->setParent(nullptr);
//erase
    _children.erase(childIndex);
}

5. 
// MARK: draw / visit

void Node::draw()
{
    auto renderer = Director::getInstance()->getRenderer();
    draw(renderer, _modelViewTransform, true);
}

void Node::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags)
{
//为什么node头文件说node仅是个'void'对象,这就是原因,draw是空实现
}

void Node::visit()
{
    auto renderer = Director::getInstance()->getRenderer();
    Mat4 parentTransform = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    visit(renderer, parentTransform, true);
}
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
    // quick return if not visible. children won't be drawn.
    if (!_visible)
    {
        return;
    }

    uint32_t flags = processParentFlags(parentTransform, parentFlags);

    // IMPORTANT:
    // To ease the migration to v3.0, we still support the Mat4 stack,
    // but it is deprecated and your code should not rely on it
    Director* director = Director::getInstance();
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
    
    bool visibleByCamera = isVisitableByVisitingCamera();

    int i = 0;

    if(!_children.empty())
    {
//根据zorder排序,若zorder相同则按插入的先后顺序排
        sortAllChildren();

//zorder<0就draw,否则break。也就是仅仅draw zorder小于0的node了
        // draw children zOrder < 0
        for( ; i < _children.size(); i++ )
        {
            auto node = _children.at(i);

            if ( node && node->_localZOrder < 0 )
                node->visit(renderer, _modelViewTransform, flags);
            else
                break;
        }
//draw自己
        // self draw
        if (visibleByCamera)
            this->draw(renderer, _modelViewTransform, flags);
//draw zorder>=0的了
//难道这就是官方文档http://cn.cocos2d-x.org/tutorial/show?id=1926
//_in-order walk_算法
        for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
            (*it)->visit(renderer, _modelViewTransform, flags);
    }
    else if (visibleByCamera)
    {
        this->draw(renderer, _modelViewTransform, flags);
    }

    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    
    // FIX ME: Why need to set _orderOfArrival to 0??
    // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
    // reset for next frame
    // _orderOfArrival = 0;
}


6. run action
Action * Node::runAction(Action* action)
{
    CCASSERT( action != nullptr, "Argument must be non-nil");
//在后续的action的介绍中将重点介绍
//actionmanager会将action和node关联并驱动action
    _actionManager->addAction(action, this, !_running);
    return action;
}

7. 对_scheduler的包装
//略,见后面关于调度器的介绍

8. 其他关于物理引擎,opengl,坐标转化和属性的setter getter就先略了

小结

  • node节点主要是做为基类存在,定义各种属性
  • node中可以继续添加子节点,最终形成树形结构,提供了_in-order walk_算法实现绘制顺序,但他自己的draw方法是空实现
  • 包装了timer调度器的一些方法便于使用
  • 提供了runAction等方法用于执行action
  • 提供了坐标转化,物理引擎相关的辅助方法

扩展阅读

Clone this wiki locally