一、数据结构

  1. // SpriteBatchNode的子节点都将在一个GL draw call中完成。
  2. // SpriteBatchNode的本质可看成是TextureAtlas::drawQuad()
  3. // SpriteBatchNode的子节点都必须是Sprite或者Sprite的子类
  4. // SpriteBatchNode的子节的纹理参数都只能是一样的,Filtering(过滤)、Wrapping(封装),因为都是同一个Texture纹理。
  5. class CC_DLL SpriteBatchNode : public Node, public TextureProtocol
  6. {
  7. // 纹理集,包含以下关键数据:
  8. // 一张纹理
  9. // n个矩形的顶点数据、顶点索引,一个矩形就对应一个Sprite
  10. // 因此,TextureAtlas可以在一个GL draw call中绘制多个矩形,也即多个Sprite,这就是SpriteBatchNode的基础。
  11. TextureAtlas* _textureAtlas;
  12. BlendFunc _blendFunc; // 混合函数
  13. BatchCommand _batchCommand; // SpriteBatchNode是通过batchCommand绘制
  14. // batchCommand就是封装TextureAtlas::drawQuad
  15. // all 后代: children, grand children, etc...
  16. // 注意这里是std::vector保存,是weak reference。
  17. std::vector<Sprite*> _descendants;
  18. }

二、创建

  1. class CC_DLL SpriteBatchNode : public Node, public TextureProtocol
  2. {
  3. static const int DEFAULT_CAPACITY = 29;
  4. // tex: 使用的纹理
  5. // capacity: 预计子节点的最大数量,超出将触发内存重构,最好不要超过这个数量。
  6. static SpriteBatchNode* createWithTexture(Texture2D* tex,
  7. ssize_t capacity = DEFAULT_CAPACITY);
  8. static SpriteBatchNode* create(const std::string& fileImage, ssize_t capacity = DEFAULT_CAPACITY);
  9. bool initWithTexture(Texture2D* tex, ssize_t capacity = DEFAULT_CAPACITY);
  10. bool initWithFile(const std::string& fileImage, ssize_t capacity = DEFAULT_CAPACITY);
  11. }

三、绘制

visit:遍历

  1. // 重写了visit方法
  2. // 遍历逻辑和Node::visit几乎一样,不同的地方是没有调用子节点的draw
  3. // 而是在SpriteBatchNode::draw中完成子节点的全部绘制
  4. void SpriteBatchNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
  5. {
  6. CC_PROFILER_START_CATEGORY(kProfilerCategoryBatchSprite, "CCSpriteBatchNode - visit");
  7. if (! _visible)
  8. {
  9. return;
  10. }
  11. // 排序子节点,注意这是自己实现的方法,和Node::sortAllChildren不同,主要工作如下:
  12. // 工作一:生成UI树的绘制顺序,具体做法:执行Node::sortAllChildren逻辑,按localZOrder从小到大排序
  13. // 工作二:将上面的绘制顺序映射到TextureAtlas中的_quads数组的索引上,也即索引更小的_quad对应的就是先绘制的Sprite
  14. sortAllChildren();
  15. uint32_t flags = processParentFlags(parentTransform, parentFlags);
  16. if (isVisitableByVisitingCamera())
  17. {
  18. // IMPORTANT:
  19. // To ease the migration to v3.0, we still support the Mat4 stack,
  20. // but it is deprecated and your code should not rely on it
  21. _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
  22. _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
  23. // 自己实现了绘制逻辑
  24. draw(renderer, _modelViewTransform, flags);
  25. _director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
  26. // FIX ME: Why need to set _orderOfArrival to 0??
  27. // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
  28. // setOrderOfArrival(0);
  29. CC_PROFILER_STOP_CATEGORY(kProfilerCategoryBatchSprite, "CCSpriteBatchNode - visit");
  30. }
  31. }

draw:绘制

  1. void SpriteBatchNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
  2. {
  3. // Optimization: Fast Dispatch
  4. if( _textureAtlas->getTotalQuads() == 0 ) return;
  5. for (const auto &child : _children) {
  6. child->updateTransform(); // 更新变换矩阵
  7. }
  8. _batchCommand.init( // batchCommand绘制,其实就是对TextureAtlas::drawQuads()的封装。
  9. _globalZOrder,
  10. getGLProgram(),
  11. _blendFunc,
  12. _textureAtlas, // 绘制的纹理、顶点数据和索引都在这里。
  13. transform, flags);
  14. renderer->addCommand(&_batchCommand);
  15. }

四、addChild

  1. void SpriteBatchNode::addChild(Node *child, int zOrder, int tag)
  2. {
  3. CCASSERT(child != nullptr, "child should not be null");
  4. // child必须是Sprite或者Sprite子类。
  5. CCASSERT(dynamic_cast<Sprite*>(child) != nullptr, "CCSpriteBatchNode only supports Sprites as children");
  6. Sprite *sprite = static_cast<Sprite*>(child);
  7. // child必须都是用相同的Texture
  8. CCASSERT(sprite->getTexture()->getName() == _textureAtlas->getTexture()->getName(), "CCSprite is not using the same texture id");
  9. Node::addChild(child, zOrder, tag); // 添加到UI树,visit遍历时,按localZOrder生成绘制顺序
  10. appendChild(sprite);
  11. }
  12. // addChild helper, faster than insertChild
  13. void SpriteBatchNode::appendChild(Sprite* sprite)
  14. {
  15. _reorderChildDirty = true;
  16. sprite->setBatchNode(this);
  17. sprite->setDirty(true);
  18. if(_textureAtlas->getTotalQuads() == _textureAtlas->getCapacity()) {
  19. increaseAtlasCapacity();
  20. }
  21. // 加入到后代容器_descendants的默认。
  22. _descendants.push_back(sprite);
  23. // atlasIndex和在_descendants的索引一样。
  24. int index = static_cast<int>(_descendants.size() - 1);
  25. sprite->setAtlasIndex(index);
  26. // 将sprite的顶点数据插入到atlasIndex = index位置处。
  27. // atlasIndex即表示_textureAtlas._quads中的第几个quad,_textureAtlas.drawQuads是按atlasIndex从小到大绘制的。
  28. // atlasIndex决定了每个后代的绘制顺序。
  29. V3F_C4B_T2F_Quad quad = sprite->getQuad();
  30. _textureAtlas->insertQuad(&quad, index);
  31. // add children recursively
  32. auto& children = sprite->getChildren();
  33. #if CC_SPRITE_DEBUG_DRAW // 绘制出三角形
  34. ......
  35. #endif
  36. }

五、使用示例

  1. // ********************************************
  2. // ************* 例 子 一:单张纹理
  3. // ********************************************
  4. auto batchNum = 50;
  5. auto batchNode = SpriteBatchNode::create(filename, batchNum);
  6. for(int i = 0; i < batchNum; i++)
  7. batchNode->addChild(Sprite::createWithTexture(batchNode->getTexture()));
  8. // ********************************************
  9. // ************* 例 子 二:SpriteSheet
  10. // ********************************************
  11. auto plist = "animations/ghosts.plist";
  12. auto plist_png = "animations/ghosts.png";
  13. SpriteFrameCache::getInstance()->addSpriteFramesWithFile( plist , plist_png );
  14. auto batchNode = SpriteBatchNode::create( "animations/ghosts.png" );
  15. batchNode->addChild(Sprite::createWithSpriteFrameName( "father.gif" ));
  16. batchNode->addChild(Sprite::createWithSpriteFrameName( "father1.gif" ));
  17. batchNode->addChild(Sprite::createWithSpriteFrameName( "father2.gif" ));
  18. // ********************************************
  19. // ************* 例 子 三:Animation动画
  20. // ********************************************
  21. auto cache = SpriteFrameCache::getInstance();
  22. auto plist = "animations/grossini-aliases.plist";
  23. auto plist_png = "animations/grossini-aliases.png";
  24. cache->addSpriteFramesWithFile( plist, plist_png );
  25. // 动画第一帧,animation动画的精灵
  26. auto sprite = Sprite::createWithSpriteFrameName( "grossini_dance_01.png" );
  27. addChild(sprite);
  28. auto batchNode = SpriteBatchNode::create( plist_png );
  29. addChild(batchNode);
  30. // 创建SpriteFrame
  31. Vector<SpriteFrame *> animFrames( 15 );
  32. for(......) animFrames->pushBack(cache->getSpriteFrameByName(...));
  33. sprite->runAction(Animate::create( Animation::createWithSpriteFrames( animFrames, 0.3f ) ));