节点 Node

文章目录

前言

建议看完后面章节的知识后,多多回顾节点的代码,有助于加深理解。

节点是场景图中的基本元素。

最常见的节点对象有:场景、图层、精灵、菜单、标签、按钮等。

节点的主要特征是:

-它们可以包含其他节点对象(’ addChild ‘、’ removeChild ‘等)

-他们可以安排定期回调(’ schedule ', ’ unschedule ‘等)

-他们可以执行动作(’ runAction ', ’ stopAction '等)

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

-覆盖init来初始化资源和调度回调

-创建回调函数来处理时间的推移

-覆盖“绘制”以呈现节点

节点的属性:

-位置(默认:x=0, y=0)

-缩放(默认值:x=1, y=1)

-旋转(角度,顺时针方向)(默认为0)

-锚点(默认:x=0, y=0)

-contentSize(默认:宽度=0,高度=0)

-可见性(默认为真)

变量初始化

static const int INVALID_TAG = -1;  //设置标签初始值

enum {
FLAGS_TRANSFORM_DIRTY = (1 << 0), // 1
FLAGS_CONTENT_SIZE_DIRTY = (1 << 1), // 2
FLAGS_RENDER_AS_3D = (1 << 3), // 8 FLAGS_DIRTY_MASK = (FLAGS_TRANSFORM_DIRTY | FLAGS_CONTENT_SIZE_DIRTY), // 或运算
}; static Node * create(); //初始化节点,并返回一个加入到自动内存释放池中的节点 static int getAttachedNodeCount(); //获取附加在节点上的节点个数,(统计父节点上的子节点个数)

创建一个节点对象

Node * Node::create()
{
Node * ret = new (std::nothrow) Node(); //创建一个名为ret的Node类型的对象
if (ret && ret->init()) //判断ret是否创建成功并初始化对象ret
{
ret->autorelease(); //将对象ret加入内存自动回收池
}
else
{
CC_SAFE_DELETE(ret); //如果创建对象失败,删除这个对象
}
return ret; //返回创建成功的节点对象ret
}

获取节点依赖的计数器

int Node::getAttachedNodeCount()
{
return __attachedNodeCount; //返回被依赖的计数
}

获取节点的描述(获取节点的Tag)

std::string Node::getDescription() const
{
return StringUtils::format("<Node | Tag = %d", _tag); //返回节点的Tag
// _tag(Node::INVALID_TAG) 实际返回的就是INVALID_TAG
}

初始化器结束


节点的局部层顺序值(LocalZOrder)

LocalZOrder是用于对节点相对于其兄弟节点进行排序的“键”。

节点的父节点将根据LocalZOrder值对所有子节点排序。

如果两个节点具有相同的LocalZOrder,那么首先添加到子数组中的节点将位于数组中另一个节点的前面。

此外,场景图使用中序遍历,先遍历左子树,然后根节点,最后是右子树。

LocalZOrder值< 0的节点是左子树

而LocalZOrder >=0的节点是右子树。

设置节点的LocalZOrder的值

void Node::setLocalZOrder(std::int32_t z)
{
if (getLocalZOrder() == z) //如果设置前后值相等,不做操作直接返回
return; _setLocalZOrder(z); //否则初始化_setLocalZOrder变量
if (_parent) //如果存在父节点
{
_parent->reorderChild(this, z); //对父节点的子节点顺序重新排序
}
_eventDispatcher->setDirtyForNode(this); //将这个节点加入到事件调度中(加入节点监听器中,并将子节点也做相同操作)
}
//兼容旧版本
CC_DEPRECATED_ATTRIBUTE virtual void setZOrder(std::int32_t localZOrder) { setLocalZOrder(localZOrder); }

获取节点的LocalZOrder的值

virtual std::int32_t getLocalZOrder() const { return _localZOrder; }
//兼容旧版本
CC_DEPRECATED_ATTRIBUTE virtual std::int32_t getZOrder() const { return getLocalZOrder(); }

相同等级子节点具有相同ZOrder时设置绘制顺序

void updateOrderOfArrival();

节点的全局层顺序值(GlobalZOrder)

设置节点的GlobalZOrder值

定义节点渲染的顺序。

globalZOrder较低的节点首先渲染。

如果两个或多个节点具有相同的globalZOrder,则不保证渲染顺序。

当节点的globalZOrder == 0时。在这种情况下,使用场景图顺序。

默认情况下,所有节点的globalZOrder为0。这意味着默认情况下,一定会使用场景图顺序用于呈现节点。

当需要以 与场景图顺序不同的顺序 呈现节点时,GlobalZOrder非常有用。

void Node::setGlobalZOrder(float globalZOrder)
{
if (_globalZOrder != globalZOrder) //如果设置前后不相等
{
_globalZOrder = globalZOrder; //给_globalZOrder赋值
_eventDispatcher->setDirtyForNode(this); //将这个节点加入到事件调度中(加入节点监听器中,并将子节点也做相同操作)
}
}

获取节点的GlobalZOrder值

virtual float getGlobalZOrder() const { return _globalZOrder; }

节点的缩放(scale)

设置缩放大小

//设置一个比例因子,用于乘以节点及其 子节点 的宽度。
void Node::setScaleX(float scaleX)
{
if (_scaleX == scaleX)
return; _scaleX = scaleX;
_transformUpdated = _transformDirty = _inverseDirty = true;
}
//设置一个比例因子,用于乘以节点及其 子节点 的高度。
void Node::setScaleY(float scaleY)
{
if (_scaleY == scaleY)
return; _scaleY = scaleY;
_transformUpdated = _transformDirty = _inverseDirty = true;
}
//设置一个比例因子,用于乘以节点及其 子节点 的高度。
void Node::setScaleZ(float scaleZ)
{
if (_scaleZ == scaleZ)
return; _scaleZ = scaleZ;
_transformUpdated = _transformDirty = _inverseDirty = true;
}
//设置一个比例因子,用于乘以节点及其 子节点 的宽度、高度、深度。
void Node::setScale(float scale)
{
if (_scaleX == scale && _scaleY == scale && _scaleZ == scale)
return; _scaleX = _scaleY = _scaleZ = scale;
_transformUpdated = _transformDirty = _inverseDirty = true;
}
//它是一个比例因子,乘以节点及其子节点的宽度和高度。
void Node::setScale(float scaleX,float scaleY)
{
if (_scaleX == scaleX && _scaleY == scaleY)
return; _scaleX = scaleX;
_scaleY = scaleY;
_transformUpdated = _transformDirty = _inverseDirty = true; //标记脏数据
}

获取缩放大小

float Node::getScaleX() const
{
return _scaleX;
}
float Node::getScaleY() const
{
return _scaleY;
}
float Node::getScaleZ() const
{
return _scaleZ;
} //获取节点的缩放因子,当X和Y具有相同的缩放因子时
float Node::getScale(void) const
{
CCASSERT( _scaleX == _scaleY, "CCNode#scale. ScaleX != ScaleY. Don't know which one to return"); // ?
return _scaleX;
}

节点位置(pisition)

设置节点在父坐标系中的位置

//设置x,y轴的位置
void Node::setPosition(const Vec2& position)
{
setPosition(position.x, position.y); //(x, y)
}
//使用0-1设置x,y轴的位置
void Node::setPositionNormalized(const Vec2& position)
{
if (_normalizedPosition.equals(position)) //修改前后相等,不做修改直接返回
return; _normalizedPosition = position; //给_normalizedPosition赋值
_usingNormalizedPosition = true; //标记 使用规范化位置
_normalizedPositionDirty = true; //标记 使用规范化位置的Dirty // ? 标记脏数据
_transformUpdated = _transformDirty = _inverseDirty = true; // ? 标记脏数据重新计算渲染坐标矩阵 //在上面的修改之后,渲染用的坐标系矩阵未经计算,造成值的不对应,在当调用getNodeToParentTransform() 重新计算渲染用的坐标系矩阵之后会使用修改之后的值渲染,渲染之后的值对应了,不存在脏数据了,会将_transformDirty再设为false
}
//直接使用x、y设置位置
virtual void setPosition(float x, float y);
virtual void getPosition(float* x, float* y) const;
virtual void setPositionX(float x);
virtual float getPositionX(void) const;
virtual void setPositionY(float y);
virtual float getPositionY(void) const;
//设置节点的三维属性
void Node::setPosition3D(const Vec3& position)
{
setPositionZ(position.z);
setPosition(position.x, position.y);
}
//设置三维的z坐标
virtual void setPositionZ(float positionZ)
{
if (_positionZ == positionZ)
return; _transformUpdated = _transformDirty = _inverseDirty = true; _positionZ = positionZ;
}

获取节点在父坐标系中的位置

//_position
const Vec2& Node::getPosition() const
{
return _position;
}
//获取节点的规范化位置 0-1
const Vec2& Node::getPositionNormalized() const
{
return _normalizedPosition;
}
//获取节点的三维属性
Vec3 Node::getPosition3D() const
{
return Vec3(_position.x, _position.y, _positionZ);
}
//获取节点的z坐标
float Node::getPositionZ() const
{
return _positionZ;
}

兼容旧版本

virtual void setNormalizedPosition(const Vec2 &position) { setPositionNormalized(position); }

virtual const Vec2& getNormalizedPosition() const { return getPositionNormalized(); }

CC_DEPRECATED_ATTRIBUTE virtual void setVertexZ(float vertexZ) { setPositionZ(vertexZ); }

CC_DEPRECATED_ATTRIBUTE virtual float getVertexZ() const { return getPositionZ(); }

节点倾斜(Skew)

设置倾斜角

void Node::setSkewX(float skewX)
{
if (_skewX == skewX)
return; _skewX = skewX;
_transformUpdated = _transformDirty = _inverseDirty = true;
}
void Node::setSkewY(float skewY)
{
if (_skewY == skewY)
return; _skewY = skewY;
_transformUpdated = _transformDirty = _inverseDirty = true;
}

获取倾斜角

float Node::getSkewX() const
{
return _skewX;
}
float Node::getSkewY() const
{
return _skewY;
}

节点锚点(AnchorPoint)

锚点是所有转换和定位操作发生的点。

它就像节点上的一个大头针,它被“连接”到它的父节点上。

锚点是标准化的,比如百分比。(0,0)表示左下角,(1,1)表示右上角。

但是也可以使用大于(1,1)和小于(0,0)的值。

默认锚点是(0,0),因此它从节点的左下角开始。

设置锚点

//以百分比设置锚点
void Node::setAnchorPoint(const Vec2& point)
{
if (! point.equals(_anchorPoint))
{
_anchorPoint = point;
_anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
_transformUpdated = _transformDirty = _inverseDirty = true;
}
}

获取锚点

//以百分比为单位返回锚点
const Vec2& Node::getAnchorPoint() const
{
return _anchorPoint;
}
//以绝对像素点为单位返回锚点
const Vec2& Node::getAnchorPointInPoints() const
{
return _anchorPointInPoints;
}

设置节点的锚点是否为(0,0)

//默认值为false,而在图层和场景中为true
void Node::setIgnoreAnchorPointForPosition(bool newValue)
{
if (newValue != _ignoreAnchorPointForPosition)
{
_ignoreAnchorPointForPosition = newValue;
_transformUpdated = _transformDirty = _inverseDirty = true;
}
}
//兼容旧版本
CC_DEPRECATED_ATTRIBUTE virtual void ignoreAnchorPointForPosition(bool ignore) { setIgnoreAnchorPointForPosition(ignore); }

获取节点的锚点是否是(0,0)

bool Node::isIgnoreAnchorPointForPosition() const
{
return _ignoreAnchorPointForPosition;
}

节点的原始大小(ContentSize)

设置节点的原始大小

void Node::setContentSize(const Size & size)
{
if (! size.equals(_contentSize))
{
_contentSize = size; _anchorPointInPoints.set(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y);
_transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true;
}
}

获取节点的原始大小

const Size& Node::getContentSize() const
{
return _contentSize;
}

节点可见性(Visible)

设置节点可见性

//默认为可见的
void Node::setVisible(bool visible)
{
if(visible != _visible)
{
_visible = visible;
if(_visible)
_transformUpdated = _transformDirty = _inverseDirty = true;
}
}

确定节点是否可见

//可见即返回true,不可见返回false
bool Node::isVisible() const
{
return _visible;
}

##节点的旋转角度(Rotation)

设置节点的旋转角度

//设置节点的旋转角度,这里是直接旋转,和下面的RotationSkew不一样
void Node::setRotation(float rotation)
{
if (_rotationZ_X == rotation)
return; _rotationZ_X = _rotationZ_Y = rotation;
_transformUpdated = _transformDirty = _inverseDirty = true; updateRotationQuat(); //?
}

获取节点的旋转角度

float Node::getRotation() const
{
CCASSERT(_rotationZ_X == _rotationZ_Y, "CCNode#rotation. RotationX != RotationY. Don't know which one to return");
return _rotationZ_X;
}

节点的3d旋转(Rotation3D)

设置节点的3d旋转

void Node::setRotation3D(const Vec3& rotation)
{
if (_rotationX == rotation.x &&
_rotationY == rotation.y &&
_rotationZ_X == rotation.z)
return; _transformUpdated = _transformDirty = _inverseDirty = true; _rotationX = rotation.x;
_rotationY = rotation.y; // rotation Z is decomposed in 2 to simulate Skew for Flash animations
//模拟flash动画的倾斜
_rotationZ_Y = _rotationZ_X = rotation.z; updateRotationQuat();
}

获取节点的3d旋转

Vec3 Node::getRotation3D() const
{
// rotation Z is decomposed in 2 to simulate Skew for Flash animations
CCASSERT(_rotationZ_X == _rotationZ_Y, "_rotationZ_X != _rotationZ_Y"); return Vec3(_rotationX,_rotationY,_rotationZ_X);
}

节点的四元数(Quat)

对于四元数的学习可以看我的另外一篇博客

cocos2dx 四元数

设置节点的四元数

//按四元数设置旋转
void Node::setRotationQuat(const Quaternion& quat)
{
_rotationQuat = quat;
updateRotation3D();
_transformUpdated = _transformDirty = _inverseDirty = true;
}

获取节点的四元数

Quaternion Node::getRotationQuat() const
{
return _rotationQuat;
}

节点的旋转倾斜(RotationSkew)

设置旋转倾斜

//设置水平旋转倾斜,模拟Flash的歪斜功能
void Node::setRotationSkewX(float rotationX)
{
if (_rotationZ_X == rotationX)
return; _rotationZ_X = rotationX;
_transformUpdated = _transformDirty = _inverseDirty = true; updateRotationQuat();
}
//设置垂直旋转倾斜的角度,模拟Flash的歪斜功能
void Node::setRotationSkewY(float rotationY)
{
if (_rotationZ_Y == rotationY)
return; _rotationZ_Y = rotationY;
_transformUpdated = _transformDirty = _inverseDirty = true; updateRotationQuat();
}

获取旋转倾斜

float Node::getRotationSkewX() const
{
return _rotationZ_X;
}
float getRotationSkewY() const
{
return _rotationZ_Y;
}

兼容旧版本

CC_DEPRECATED_ATTRIBUTE virtual void setRotationX(float rotationX) { return setRotationSkewX(rotationX); }

CC_DEPRECATED_ATTRIBUTE virtual void setRotationY(float rotationY) { return setRotationSkewY(rotationY); }

C_DEPRECATED_ATTRIBUTE virtual float getRotationY() const { return getRotationSkewY(); }

子节点(Child)

添加子节点

void Node::addChild(Node *child)
{
CCASSERT( child != nullptr, "Argument must be non-nil"); //判断节点是否是空节点
this->addChild(child, child->getLocalZOrder(), child->_name);//
}
//添加子节点同时设置localZOrder属性
void Node::addChild(Node *child, int zOrder)
{
CCASSERT( child != nullptr, "Argument must be non-nil");
this->addChild(child, zOrder, child->_name);
}
//添加子节点同时设置localZOrder和tag属性
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);
}
//添加子节点同时设置localZOrder和name属性
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 Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
{
auto assertNotSelfChild
( [ this, child ]() -> bool
{
for ( Node* parent( getParent() ); parent != nullptr;
parent = parent->getParent() )
if ( parent == child )
return false; return true;
} );
(void)assertNotSelfChild; CCASSERT( assertNotSelfChild(),
"A node cannot be the child of his own children" ); if (_children.empty())
{
this->childrenAlloc();
} this->insertChild(child, localZOrder); if (setTag)
child->setTag(tag);
else
child->setName(name); child->setParent(this); child->updateOrderOfArrival(); if( _running )
{
child->onEnter(); //调用onEnter 增加依附计数 完成过渡
// prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
if (_isTransitionFinished)
{
child->onEnterTransitionDidFinish(); //结束过渡
}
} if (_cascadeColorEnabled)
{
updateCascadeColor();
} if (_cascadeOpacityEnabled)
{
updateCascadeOpacity();
}
}

获取子节点

//遍历子节点,通过tag找到所需的节点
Node* Node::getChildByTag(int tag) const
{
CCASSERT(tag != Node::INVALID_TAG, "Invalid tag"); for (const auto child : _children)
{
if(child && child->_tag == tag)
return child;
}
return nullptr;
}
//返回具有给定名称的节点,该节点可以转换为类型T
template <typename T>
T getChildByTag(int tag) const { return static_cast<T>(getChildByTag(tag)); }
//通过name找到所需的节点
Node* Node::getChildByName(const std::string& name) const
{
CCASSERT(!name.empty(), "Invalid name"); //需要有名字 std::hash<std::string> h;
size_t hash = h(name); for (const auto& child : _children)
{
// Different strings may have the same hash code, but can use it to compare first for speed
if(child->_hashOfName == hash && child->_name.compare(name) == 0)
return child;
}
return nullptr;
}
//返回具有给定名称的节点,该节点可以转换为类型T
template <typename T>
T getChildByName(const std::string& name) const { return static_cast<T>(getChildByName(name)); }
//查找节点
// '//':只能放在搜索字符串的开头。这表明它将递归搜索。
// '..':搜索应该向上移动到节点的父节点。只能放在字符串的末尾。
// '/' :当搜索字符串放置在除开始位置以外的任何位置时,这表示搜索应该移动到节点的子节点。
//例如:
//enumerateChildren("//MyName", ...) // 递归查找子节点
//enumerateChildren("[[:alnum:]]+", ...) // 匹配子节点的每个节点
//enumerateChildren("A[[:digit:]]", ...) // 查找A0-A9
//enumerateChildren("Abby/Normal", ...) // 搜索并返回名为Normal父节点为Abby的任何节点
//enumerateChildren("//Abby/Normal", ...) // 递归搜索并返回名为Normal父节点为Abby的任何节点
void Node::enumerateChildren(const std::string &name, std::function<bool (Node *)> callback) const
{
CCASSERT(!name.empty(), "Invalid name");
CCASSERT(callback != nullptr, "Invalid callback function"); size_t length = name.length(); size_t subStrStartPos = 0; // sub string start index
size_t subStrlength = length; // sub string length // Starts with '//'?
bool searchRecursively = false;
if (length > 2 && name[0] == '/' && name[1] == '/')
{
searchRecursively = true;
subStrStartPos = 2;
subStrlength -= 2;
} // End with '/..'?
bool searchFromParent = false;
if (length > 3 &&
name[length-3] == '/' &&
name[length-2] == '.' &&
name[length-1] == '.')
{
searchFromParent = true;
subStrlength -= 3;
} // Remove '//', '/..' if exist
std::string newName = name.substr(subStrStartPos, subStrlength); if (searchFromParent)
{
newName.insert(0, "[[:alnum:]]+/");
} if (searchRecursively)
{
// name is '//xxx'
doEnumerateRecursive(this, newName, callback);
}
else
{
// name is xxx
doEnumerate(newName, callback);
}
}
//返回节点的子节点数组
virtual Vector<Node*>& getChildren() { return _children; }
virtual const Vector<Node*>& getChildren() const { return _children; }
//获取子节点的个数
ssize_t Node::getChildrenCount() const
{
return _children.size();
}

添加父节点

void Node::setParent(Node * parent)
{
_parent = parent;
_transformUpdated = _transformDirty = _inverseDirty = true;
}

获取父节点

virtual Node* getParent() { return _parent; }
virtual const Node* getParent() const { return _parent; }

移除(remove)

//将这个节点从父节点中移除,如果没有父节点则不操作
void Node::removeFromParent()
{
this->removeFromParentAndCleanup(true);
}
void Node::removeFromParentAndCleanup(bool cleanup)
{
if (_parent != nullptr)
{
_parent->removeChild(this,cleanup);
}
}
void ParallaxNode::removeChild(Node* child, bool cleanup)
{
for( int i=0;i < _parallaxArray->num;i++)
{
PointObject *point = (PointObject*)_parallaxArray->arr[i];
if (point->getChild() == child)
{
ccArrayRemoveObjectAtIndex(_parallaxArray, i, true);
break;
}
}
Node::removeChild(child, cleanup);
}
//移除子节点
//如果所有正在运行的动作和回调函数都在子节点上,cleanup为true,否则为false
void Node::removeChild(Node* child, bool cleanup /* = true */)
{
if (_children.empty())
{
return;
} ssize_t index = _children.getIndex(child);
if( index != CC_INVALID_INDEX )
this->detachChild( child, index, cleanup );
}
//通过tag移除子节点
void Node::removeChildByTag(int tag, bool cleanup/* = true */)
{
CCASSERT( tag != Node::INVALID_TAG, "Invalid tag"); Node *child = this->getChildByTag(tag); if (child == nullptr)
{
CCLOG("cocos2d: removeChildByTag(tag = %d): child not found!", tag);
}
else
{
this->removeChild(child, cleanup);
}
}
//通过Name移除子节点
void Node::removeChildByName(const std::string &name, bool cleanup)
{
CCASSERT(!name.empty(), "Invalid name"); Node *child = this->getChildByName(name); if (child == nullptr)
{
CCLOG("cocos2d: removeChildByName(name = %s): child not found!", name.c_str());
}
else
{
this->removeChild(child, cleanup);
}
}
//移除所有子节点
void Node::removeAllChildren()
{
this->removeAllChildrenWithCleanup(true);
}
//删除所有子节点,并根据cleanup参数对所有正在运行的操作执行清理(cleanup)
void Node::removeAllChildrenWithCleanup(bool cleanup)
{
// not using detachChild improves speed here
for (const auto& child : _children)
{
// IMPORTANT:
// -1st do onExit
// -2nd cleanup
if(_running)
{
child->onExitTransitionDidStart();
child->onExit();
} if (cleanup)
{
child->cleanup();
}
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
auto sEngine = ScriptEngineManager::getInstance()->getScriptEngine();
if (sEngine)
{
sEngine->releaseScriptObject(this, child);
}
#endif // CC_ENABLE_GC_FOR_NATIVE_OBJECTS
// set parent nil at the end
child->setParent(nullptr);
} _children.clear();
}

排序(Sort) !

void Node::reorderChild(Node *child, int zOrder)
{
CCASSERT( child != nullptr, "Child must be non-nil");
_reorderChildDirty = true;
child->updateOrderOfArrival();
child->_setLocalZOrder(zOrder);
}
//对所有的子节点排序,不需要手动调用,只会在绘制之前执行一次,以提高性能
void Node::sortAllChildren()
{
if (_reorderChildDirty)
{
sortNodes(_children);
_reorderChildDirty = false;
_eventDispatcher->setDirtyForNode(this);
}
}
//
template<typename _T> inline
static void sortNodes(cocos2d::Vector<_T*>& nodes)
{
static_assert(std::is_base_of<Node, _T>::value, "Node::sortNodes: Only accept derived of Node!");
#if CC_64BITS
std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
return (n1->_localZOrder$Arrival < n2->_localZOrder$Arrival);
});
#else
std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
return (n1->_localZOrder == n2->_localZOrder && n1->_orderOfArrival < n2->_orderOfArrival) || n1->_localZOrder < n2->_localZOrder;
});
#endif
}

数据和用户标签

设置

UserData基本不用,一般使用UserDefault来记录数据,生成类似xml的文件。

    virtual void setTag(int tag);
virtual void setName(const std::string& name);
virtual void setUserData(void *userData);//可以看到这里的UserData是void类型的,也就是说可以记录任意数据
virtual void setUserObject(Ref *userObject);

获取

    virtual int getTag() const;
virtual const std::string& getName() const;
virtual void* getUserData() { return _userData; }
virtual const void* getUserData() const { return _userData; }
virtual Ref* getUserObject() { return _userObject; }
virtual const Ref* getUserObject() const { return _userObject; }
    virtual int getTag() const;
virtual const std::string& getName() const;
virtual void* getUserData() { return _userData; }
virtual const void* getUserData() const { return _userData; }
virtual Ref* getUserObject() { return _userObject; }
virtual const Ref* getUserObject() const { return _userObject; }

OPenGL

获取OPenGL程序的状态

//每个node私有的状态
GLProgram * Node::getGLProgram() const
{
return _glProgramState ? _glProgramState->getGLProgram() : nullptr;
}
//兼容旧版本
CC_DEPRECATED_ATTRIBUTE GLProgram* getShaderProgram() const { return getGLProgram(); }
//通用的状态
GLProgramState* Node::getGLProgramState() const
{
return _glProgramState;
}

设置OPenGL程序的状态

//设置私有的程序状态
void Node::setGLProgram(GLProgram* glProgram)
{
if (_glProgramState == nullptr || (_glProgramState && _glProgramState->getGLProgram() != glProgram))
{
CC_SAFE_RELEASE(_glProgramState);
_glProgramState = GLProgramState::getOrCreateWithGLProgram(glProgram);
_glProgramState->retain(); _glProgramState->setNodeBinding(this);
}
}
//兼容旧版本
CC_DEPRECATED_ATTRIBUTE void setShaderProgram(GLProgram *glprogram) { setGLProgram(glprogram); }
//设置通用的程序状态
void Node::setGLProgramState(cocos2d::GLProgramState* glProgramState)
{
if (glProgramState != _glProgramState)
{
CC_SAFE_RELEASE(_glProgramState);
_glProgramState = glProgramState;
CC_SAFE_RETAIN(_glProgramState); if (_glProgramState)
_glProgramState->setNodeBinding(this);
}
}

isRunning()

//返回节点是否正在运行
//如果节点正在运行,它将接受onEnter()、onExit()、update()等事件回调
bool Node::isRunning() const
{
return _running;
}

onEnter()

//过渡效果开始之前调用,预处理一些动作
void Node::onEnter()
{
if (!_running) //没有运行说明还未依附,之后依附,给依附计数器+1
{
++__attachedNodeCount;
}
#if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeJavascript)
{
if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnter))
return;
}
#endif if (_onEnterCallback)
_onEnterCallback(); //调用自己的回调函数 if (_componentContainer && !_componentContainer->isEmpty())
{
_componentContainer->onEnter();
} _isTransitionFinished = false; for( const auto &child: _children) //子节点也会进入场景
child->onEnter(); this->resume(); _running = true; //已经依附 #if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeLua)
{
ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnter);
}
#endif
}
//完全进入场景时调用,添加背景乐等动作
void Node::onEnterTransitionDidFinish()
{
#if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeJavascript)
{
if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnterTransitionDidFinish))
return;
}
#endif if (_onEnterTransitionDidFinishCallback)
_onEnterTransitionDidFinishCallback(); //调用结束过渡的回调函数 _isTransitionFinished = true;
for( const auto &child: _children) //子节点同步
child->onEnterTransitionDidFinish(); #if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeLua)
{
ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnterTransitionDidFinish);
}
#endif
}

onExit()

/* if(_running)
{
child->onExitTransitionDidStart();
child->onExit();
}
*/ //belong to void Node::removeAllChildren() //在退出场景之前调用
void Node::onExitTransitionDidStart()
{
#if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeJavascript)
{
if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnExitTransitionDidStart))
return;
}
#endif if (_onExitTransitionDidStartCallback)
_onExitTransitionDidStartCallback(); //调用回调函数 for( const auto &child: _children) //子节点同步
child->onExitTransitionDidStart(); #if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeLua)
{
ScriptEngineManager::sendNodeEventToLua(this, kNodeOnExitTransitionDidStart);
}
#endif
} //退出场景时调用
void Node::onExit()
{
if (_running)
{
--__attachedNodeCount; //依附计数器-1
}
#if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeJavascript)
{
if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnExit))
return;
}
#endif if (_onExitCallback)
_onExitCallback(); //调用自己的回调函数 if (_componentContainer && !_componentContainer->isEmpty())
{
_componentContainer->onExit();
} this->pause(); //暂停事务 _running = false; //不在运行中 for( const auto &child: _children) //子节点也会被退出
child->onExit(); #if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeLua)
{
ScriptEngineManager::sendNodeEventToLua(this, kNodeOnExit);
}
#endif
}

cleanup()

//停止所有的操作和调度器
void Node::cleanup()
{
#if CC_ENABLE_SCRIPT_BINDING
if (_scriptType == kScriptTypeJavascript)
{
if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnCleanup))
return;
}
else if (_scriptType == kScriptTypeLua)
{
ScriptEngineManager::sendNodeEventToLua(this, kNodeOnCleanup);
}
#endif // #if CC_ENABLE_SCRIPT_BINDING // actions
this->stopAllActions();
// timers
this->unscheduleAllCallbacks(); for( const auto &child: _children) //子节点同步
child->cleanup();
}

补充:

1、GlobalZorder、LocalZorder和orderOfArrival的区别

//渲染的优先级是Globalzorder > LocalZorder > orderOfArrival,类似于权重

值越大的越迟渲染,因为节点是使用二叉树的中序遍历来渲染的。

会将所有Globalzorder < 0,Globalzorder > 0,Globalzorder > 0,的节点都放在数组中。

localZorder区别节点本身与兄弟节点的渲染顺序,当两个或多个节点的localZorder值相等时会比较orderOfArrival的值。

orderOfArrival的值不需要人为的特别设置,在给节点添加子节点时会自动的自增, orderOfArrival的值默认为0。

2、setIgnoreAnchorPointForPosition();//一般Node的锚点默认为(0.5, 0.5),而Layer的锚点则在左下角(0,0),这个函数用来给Layer使用,layer的_ignoreAnchorPointForPosition值默认为true,忽略了锚点设置,如果自己设置了锚点也不会生效getChildren()

3、getChildren()有const修饰的问题 如果节点被const修饰,则只能调用被const修饰的getChildren函数。

4、_componentContainer的问题 相当于级联的操作

从零开始のcocos2dx生活(二)Node的更多相关文章

  1. 从零开始のcocos2dx生活(九)CCBReader

    NodeLoaderLibrary是用来存储节点加载器类型的类,通过registerDefaultNodeLoaders()可以注册所有默认类型的加载器 在CocosBuilder的使用手册中: 1. ...

  2. 从零开始のcocos2dx生活(七)ParticleSystem

    CCParticleSystem是用来设置粒子效果的类 1.粒子分为两种模式:重力模式 和 半径模式 重力模式独占属性: gravity 重力方向,Vec2类型,可以分别指定不同方向的重力大小 spe ...

  3. 从零开始のcocos2dx生活(六)EventDispatcher

    EventDispatcher可能是所有的里面比较不容易理解也不容易看的 我说自己的理解可能会误导到你们-[索了你们看不下去>< 我写了几乎所有的代码的注释,有的是废话跳过就好 主要的代码 ...

  4. 从零开始のcocos2dx生活(一)内存管理

    cocos中所有的对象都是继承自Ref基类,Ref的职责就是对对象进行引用计数管理 内存管理中最重要的是三个方法retain().release().autorelease() 在cocos中创建对象 ...

  5. 从零开始のcocos2dx生活(四)ActionManager

    文章目录 初始化构造函数 析构函数 删除哈希元素 分配存放动作对象的空间 通过索引移除动作 暂停动作 恢复动作 暂停所有的动作 恢复所有的动作 添加动作 移除所有的动作 移除target中的所有动作 ...

  6. 从零开始のcocos2dx生活(三)Scheduler

    文章目录 取模 Timer() 变量 设置定时器Timer() 一些成员函数 Scheduler() 变量 初始化 哈希表 构造函数schedule() 开启定时器Update() 析构函数~Upda ...

  7. 从零开始のcocos2dx生活(十一)TableView

    目录 简述 主要变量 主要方法 setVerticalFillOrder reloadData cellAtIndex updateCellAtIndex insertCellAtIndex remo ...

  8. 从零开始のcocos2dx生活(十)ScrollView

    目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...

  9. 从零开始のcocos2dx生活(八)ParticleSystemQuad

    https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_1 写的真的非常好-最近没时间拜读,只看 ...

随机推荐

  1. 像Google一样构建机器学习系统3 - 利用MPIJob运行ResNet101

    本系列将利用阿里云容器服务,帮助您上手Kubeflow Pipelines. 第一篇:在阿里云上搭建Kubeflow Pipelines 第二篇:开发你的机器学习工作流 第三篇:利用MPIJob运行R ...

  2. MySQL 8.0 技术详解

    MySQL 8.0 简介 MySQL 5.7 到 8.0,Oracle 官方跳跃了 Major Version 版本号,随之而来的就是在 MySQL 8.0 上做了许多重大更新,在往企业级数据库的路上 ...

  3. :after选择器-----分割线

    分割线: 让span中的文字覆盖分割线,需要:给div和span设置同样的background-color,并且给span设置z-index. 接下来就是margin和padding的调整了. 效果: ...

  4. 小程序中使用threejs

    webgl调试 起初使用threejs 在小程序里面调试,明明是按着官方的文档来,但是会发现开发者工具上面会提示getContext,经过一翻摸索,发现webgl调试只能在手机端调试. 总结:webg ...

  5. Java多线程遍历文件夹,广度遍历加多线程加深度遍历结合

    复习IO操作,突然想写一个小工具,统计一下电脑里面的Java代码量还有注释率,最开始随手写了一个递归算法,遍历文件夹,比较简单,而且代码层次清晰,相对易于理解,代码如下:(完整代码贴在最后面,前面是功 ...

  6. 运行项目npm run dev时报错: ~Error: Cannot find module 'webpack-cli/bin/config-yargs', 原因是

    webpack@3.X运行项目npm run dev时报错: ~Error: Cannot find module 'webpack-cli/bin/config-yargs' 我的原因是:  web ...

  7. excel中如何筛选功能的使用

    excel中如何筛选功能的使用 excel是一款数据处理工具,可以在众多的数据中找到想要的经过处理之后的数据,而最直接方便的功能就是筛选.请阅读下文,了解如何对数据进行筛选. 如下图所示的学生成绩中, ...

  8. 洛谷P2719 搞笑世界杯 题解 概率DP入门

    作者:zifeiy 标签:概率DP 题目链接:https://www.luogu.org/problem/P2719 我们设 f[n][m] 用于表示还剩下n张A类票m张B类票时最后两张票相同的概率, ...

  9. python编程之进程

    进程:运行中的程序 进程和操作系统的关系:进程是操作系统调度和资源分配的最小单位,是操作系统的结构基础. 那么为什么要有进程呢? 程序在运行时,会使用各种硬件资源,如果他们之间没有界限,那么程序之间的 ...

  10. Vue 循环为选中的li列表添加效果

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Vu ...