Cocos2d iphone 了解何时使用CCSpriteBatchNodes?

Cocos2d iphone 了解何时使用CCSpriteBatchNodes?,cocos2d-iphone,ccspritebatchnode,Cocos2d Iphone,Ccspritebatchnode,我在几个地方看到过,包括CCSpriteBatchNode的源代码,从中添加/删除child是“昂贵的”。我的理解是,使用批处理节点的全部目的是防止当来自同一个精灵表的许多精灵被添加到同一个容器中时,昂贵的OpenGL调用一次又一次地发生 我想知道的是1)在sprite批处理节点中添加/删除child的“成本”有多高,2)什么时候使用一个合适 例如,我有一个激光物体,它可以产生十个精灵。。。当它在屏幕上移动时,它显示/隐藏给定屏幕位置的当前精灵。当它到达屏幕最右边缘时,激光物体被丢弃,十个精灵也

我在几个地方看到过,包括CCSpriteBatchNode的源代码,从中添加/删除child是“昂贵的”。我的理解是,使用批处理节点的全部目的是防止当来自同一个精灵表的许多精灵被添加到同一个容器中时,昂贵的OpenGL调用一次又一次地发生

我想知道的是1)在sprite批处理节点中添加/删除child的“成本”有多高,2)什么时候使用一个合适

例如,我有一个激光物体,它可以产生十个精灵。。。当它在屏幕上移动时,它显示/隐藏给定屏幕位置的当前精灵。当它到达屏幕最右边缘时,激光物体被丢弃,十个精灵也被丢弃。所以,我想知道,在这种情况下,精灵批处理节点是否不适合使用,因为它只有10个精灵,而且发生得很快——移动动画为0.2秒,因此如果播放器要快速启动,这意味着要反复向批处理节点添加/删除10个精灵


在其他情况下,我已经为各种对象设置了一个SpriteBatchNode,偶尔我会遇到一个需要添加的一次性sprite,它恰好是同一个sprite工作表的一部分,因此我很想将它添加到该批处理节点,因为它在那里,并且它已经被指定到特定的sprite工作表中。。。。。。无论如何,我想澄清一下这个话题。

一个
CCSpriteBatchNode
和一个正常的
CCSprite
之间的主要区别是
CCSpriteBatchNode
一次将所有精灵的所有数据发送到GPU,而不是为每个精灵发送数据

CCSprite
draw调用的工作方式如下:

glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
基本上需要进行3次调用来设置精灵的数据,然后调用
glDrawArrays
。如果你有100个精灵,这个代码会执行100次

现在让我们看一下
CCSpriteBatchNode
(我选择了没有VAO的实现,这是另一种可能的优化):

现在这段代码一次设置所有精灵的所有数据,因为它存储在连续内存中。无论精灵的数量是多少,这个呼叫对于1、10、100都是一样的

这就是为什么它更高效,但同时,由于数据是连续存储在内存中的,因此当移除、添加或修改子对象时,阵列必须相应地更改并在GPU中更新。这就是添加和删除成本的来源(甚至是隐藏的CCSprite跳过了渲染阶段,而批处理节点中的隐藏CCSprite却没有)

根据个人经验,我可以告诉你,成本通常可以忽略不计,如果可以的话(因为它们有其局限性,比如在整个节点上混合,而不是在每个精灵的基础上以及类似的事情上),当你绘制的精灵多于一组时,你应该始终使用
CCSpriteBatchNode

不过,为自己设定基准应该很容易

1) 向sprite批处理节点添加/删除child的“成本”有多高

我所知道的唯一一种可能“昂贵”的情况是,您必须增加atlas的容量。你看,批处理节点有一个容量,如果你添加一个子节点超过它,该节点将不得不增加其容量并重新计算所有精灵的纹理坐标

要解决这个问题,您只需首先为批处理节点提供一个合理的容量—既不能太少也不能太多。根据您的需要,您可以确定此类号码

2) 什么时候使用一个被认为是合适的

只要有几个精灵可以使用同一纹理源。对于马里奥游戏,很明显,你需要在屏幕上显示几个硬币。这将是一个很好的批处理节点用例:为硬币图像创建一个批处理节点,然后所有硬币精灵都将使用该批处理节点

有时,可以将多个元素组合到同一纹理中。比如说,你可以将硬币图像、怪物图像和蘑菇图像放在同一个纹理中。这样,您的所有硬币、怪物和蘑菇都可以使用同一批节点

对于背景纹理之类的东西,您不应该需要批处理节点,因为您可能只需要一个背景精灵

所以,我想知道,在这种情况下,sprite批处理节点会是 不适合使用,因为它只有10个精灵,而且碰巧如此 快速——移动动画为0.2秒,因此如果播放器 要快速启动,这意味着在一批中添加/删除10个精灵 节点一遍又一遍

这是批处理节点的有效用例。毕竟,10个精灵是模拟绘制的。而且,如果您知道将不再使用激光对象,则始终可以卸载相应的批处理节点。我想游戏中可能有几个激光对象,所以批处理节点是个好主意

坦率地说,不要太担心性能。在我的游戏中,我总是用几十个来处理各种各样的事情(角色、天气粒子、地图对象、收藏品、界面等等),多亏了它们,我很少看到它低于55fps


事实上,我发现很难反对使用批处理节点。它们很少造成任何伤害。

如前所述,sprite批处理节点为其所有子节点批处理对GPU的调用(因为它们使用相同的纹理)。然而,为了对性能产生影响,必须加入大量的精灵。对于10个精灵,我不认为这会有什么不同

也就是说,请注意,如果您使用的是新版本的Cocos2d(如3.0),目前处于测试阶段的3.1提供了自动批处理功能,因此您无需浪费时间玩CCSpriteBatchNode。Cocos2d将自动批处理发送到GPU的数据。

add
glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]);
glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) );