5.【cocos2d-x 源码分析】:Node 类体系的详细分析
对应源码位置:(1)cocos2d-x-3.3\cocos\2d\CCNode (2)cocos2d-x-3.3\cocos\2d\CCLayer(3)cocos2d-x-3.3\cocos\2d\CCScene
Node是整个游戏对象的根节点
基本后面熟知的 sprite 、 layer 以及 scene都是他的子类。
Node类的部分代码
由于Cocos2d-x采用继承的方针组织整个游戏对象,所以Node类的负担很重,很庞大。简单抽取一部分出来。
class CC_DLL Node : public Ref
{
public:
/// Default tag used for all the nodes
static const int INVALID_TAG = -1;
//老规矩 就是看 变了没 没变就不计算 这样优化
enum {
FLAGS_TRANSFORM_DIRTY = (1 << 0),
FLAGS_CONTENT_SIZE_DIRTY = (1 << 1),
FLAGS_DIRTY_MASK = (FLAGS_TRANSFORM_DIRTY | FLAGS_CONTENT_SIZE_DIRTY),
};
static Node * create();
//加入 孩子节点 用一个vector来存储
virtual void addChild(Node* child, int localZOrder, const std::string &name);
//设置 父级节点
virtual void setParent(Node* parent);
//这里主要是 设置 opengl的 shader程序 下一篇具体分析
GLProgram* getGLProgram() const;
GLProgramState *getGLProgramState() const;
virtual void setGLProgramState(GLProgramState *glProgramState);
//这里就是 具体渲染时 跟opengl有关的部分
//统一的接口 代表节点对象的绘制 方式
virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);
virtual void draw() final;
/**
* Visits this node's children and draw them recursively.
*/
//这里是 访问孩子节点 并绘制 后面具体分析
virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags);
virtual void visit() final;
//获取 Node所属于的scene
virtual Scene* getScene() const;
//这个就是 前面schedule里面注册的 每帧调用的函数 注册的位置
//这也是 最常用的 update逻辑
void scheduleUpdate(void);
virtual void update(float delta);
//下面是 node的属性 也是action动作改变的数据
float _rotationX; ///< rotation on the X-axis
float _rotationY; ///< rotation on the Y-axis
// rotation Z is decomposed in 2 to simulate Skew for Flash animations
float _rotationZ_X; ///< rotation angle on Z-axis, component X
float _rotationZ_Y; ///< rotation angle on Z-axis, component Y
float _scaleX; ///< scaling factor on x-axis
float _scaleY; ///< scaling factor on y-axis
float _scaleZ; ///< scaling factor on z-axis
Vec2 _position; ///< position of the node
float _positionZ; ///< OpenGL real Z position
Vec2 _normalizedPosition;
bool _usingNormalizedPosition;
bool _normalizedPositionDirty;
float _skewX; ///< skew angle on x-axis
float _skewY; ///< skew angle on y-axis
Vec2 _anchorPointInPoints; ///< anchor point in points
Vec2 _anchorPoint; ///< anchor point normalized (NOT in points)
Size _contentSize; ///< untransformed size of the node
bool _contentSizeDirty; ///< whether or not the contentSize is dirty
Mat4 _modelViewTransform; ///< ModelView transform of the Node.
// "cache" variables are allowed to be mutable
mutable Mat4 _transform; ///< transform
mutable bool _transformDirty; ///< transform dirty flag
mutable Mat4 _inverse; ///< inverse transform
mutable bool _inverseDirty; ///< inverse transform dirty flag
mutable Mat4 _additionalTransform; ///< transform
bool _useAdditionalTransform; ///< The flag to check whether the additional transform is dirty
bool _transformUpdated; ///< Whether or not the Transform object was updated since the last frame
//一个是local一个是全局的
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
size_t _hashOfName; ///<hash value of _name, used for speed in getChildByName
void *_userData; ///< A user assingned void pointer, Can be point to any cpp object
Ref *_userObject; ///< A user assigned Object
GLProgramState *_glProgramState; ///< OpenGL Program State
int _orderOfArrival; ///< used to preserve sequence while sorting children with the same localZOrder
Scheduler *_scheduler; ///< scheduler used to schedule timers and updates
ActionManager *_actionManager; ///< a pointer to ActionManager singleton, which is used to handle all the actions
EventDispatcher* _eventDispatcher; ///< event dispatcher used to dispatch all kinds of events
bool _running; ///< is running
bool _visible; ///< is this node visible
bool _ignoreAnchorPointForPosition; ///< true if the Anchor Vec2 will be (0,0) when you position the Node, false otherwise.
///< Used by Layer and Scene.
bool _reorderChildDirty; ///< children order dirty flag
bool _isTransitionFinished; ///< flag to indicate whether the transition was finished
ComponentContainer *_componentContainer; ///< Dictionary of components
//刚体部分 用于参与 物理世界
PhysicsBody* _physicsBody; ///< the physicsBody the node have
float _physicsScaleStartX; ///< the scale x value when setPhysicsBody
float _physicsScaleStartY; ///< the scale y value when setPhysicsBody
// opacity controls
GLubyte _displayedOpacity;
GLubyte _realOpacity;
Color3B _displayedColor;
Color3B _realColor;
bool _cascadeColorEnabled;
bool _cascadeOpacityEnabled;
static int s_globalOrderOfArrival;
//和unity类似的 标签
// camera mask, it is visible only when _cameraMask & current camera' camera flag is true
unsigned short _cameraMask;
//几个阶段的 回调函数
//If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
std::function<void()> _onEnterCallback;
std::function<void()> _onExitCallback;
std::function<void()> _onEnterTransitionDidFinishCallback;
std::function<void()> _onExitTransitionDidStartCallback;
};
下面看具体实现的几个函数:
//添加孩子节点的逻辑
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)
{
if (_children.empty())
{
this->childrenAlloc();
}
//插入到 vector中 同时 更新一下 孩子标志 脏了
//为了 重新排序
this->insertChild(child, localZOrder);
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.
//如果 是物理的scene 则加 入到 物理世界中
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();
}
}
//绘制相关 具体opengl内容 下一篇
void Node::draw()
{
auto renderer = Director::getInstance()->getRenderer();
draw(renderer, _modelViewTransform, true);
}
//留给子类实现
void Node::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags)
{
}
//访问子类 并访问
void Node::visit()
{
auto renderer = Director::getInstance()->getRenderer();
Mat4 parentTransform = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
visit(renderer, parentTransform, true);
}
//parentTransform 是用来传递 父节点的 位置朝向 矩阵
//与子类的相乘才得到最终的 世界坐标系的位置
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())
{
//先排序 似曾相识
sortAllChildren();
//关于zorder盗来 一段话
/*总结:节点的渲染顺序跟节点的三个成员变量有关(_localZOrder、_globalZOrder、_orderOfArrival)分别对应三个设置函数setLocalZOrder、 setGlobalZOrder、setOrderOfArrival。无论是_localZOrder、_globalZOrder、_orderOfArrival都是越大的越后渲染,越小的越先渲染,而且有_globalZOrder的优先级大于_localZOrder的优先级大于_orderOfArrival的优先级。所以我们判断节点间的渲染(绘制)顺序时应先对比他们的_globalZOrder值,如若相等,再对比他们的_localZOrder值,如若相等,再对比他们的_orderOfArrival值。其中,_orderOfArrival值在调用addChild函数是自动给出,一般我们不调用setOrderOfArrival函数去更改节点间的渲染(绘制)顺序。
补充:当采用LocalZOrder作为节点渲染(绘制)顺序的判断值时,父节点的LocalZOrder不与子节点的LocalZOrder值作比较。子节点中LocalZOrder值小于0的节点作为以父节点为根节点的树的左子树的根节点,大于0的作为右子树的根节点。所以在中序遍历下,先(渲染)绘制子节点中LocalZOrder值小于0的子节点,再渲染(绘制)父节点,再渲染LocalZOrder值大于0的子节点。*/
// 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;
}
// self draw
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
//注意 加了个i 所以是zorder>0 cbegin是指返回的const类型 迭代器
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;
}
简要介绍 Layer
//可以看到 layer 没有添加什么
//只是 把常用的 几个事件监听器 更方便的集成
class CC_DLL Layer : public Node
{
public:
/** creates a fullscreen black layer */
static Layer *create();
// Overrides
virtual std::string getDescription() const override;
CC_CONSTRUCTOR_ACCESS:
Layer();
virtual ~Layer();
virtual bool init() override;
bool _touchEnabled;
bool _accelerometerEnabled;
bool _keyboardEnabled;
EventListener* _touchListener;
EventListenerKeyboard* _keyboardListener;
EventListenerAcceleration* _accelerationListener;
Touch::DispatchMode _touchMode;
bool _swallowsTouches;
};
简要介绍 Scene
//这里面最重要的是 Scene对Camera和BaseLight的支持 以及物理世界的支持
class CC_DLL Scene : public Node
{
public:
/** creates a new Scene object */
static Scene *create();
/** creates a new Scene object with a predefined Size */
static Scene *createWithSize(const Size& size);
using Node::addChild;
virtual std::string getDescription() const override;
/** get all cameras */
const std::vector<Camera*>& getCameras() const { return _cameras; }
const std::vector<BaseLight*>& getLights() const { return _lights; }
/** render the scene */
void render(Renderer* renderer);
CC_CONSTRUCTOR_ACCESS:
Scene();
virtual ~Scene();
bool init();
bool initWithSize(const Size& size);
void onProjectionChanged(EventCustom* event);
protected:
std::vector<Camera*> _cameras; //weak ref to Camera
Camera* _defaultCamera; //weak ref, default camera created by scene, _cameras[0], Caution that the default camera can not be added to _cameras before onEnter is called
EventListenerCustom* _event;
std::vector<BaseLight *> _lights;
private:
CC_DISALLOW_COPY_AND_ASSIGN(Scene);
#if CC_USE_PHYSICS
public:
virtual void addChild(Node* child, int zOrder, int tag) override;
virtual void addChild(Node* child, int zOrder, const std::string &name) override;
virtual void update(float delta) override;
inline PhysicsWorld* getPhysicsWorld() { return _physicsWorld; }
static Scene *createWithPhysics();
CC_CONSTRUCTOR_ACCESS:
bool initWithPhysics();
protected:
void addChildToPhysicsWorld(Node* child);
PhysicsWorld* _physicsWorld;
#endif // CC_USE_PHYSICS
};
最后
Node 类体系 与 渲染部分 密不可分 ,本篇只能简单分析,下一篇 分析渲染部分。