Cocos2d:6对CCSpriteBatchNode的使用怀疑 [英] Cocos2d: 6 doubts on usage of CCSpriteBatchNode

查看:176
本文介绍了Cocos2d:6对CCSpriteBatchNode的使用怀疑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道如何优化CCSpriteBatchNode的使用。换句话说,我理解:




  • 1)每个CCSpriteBatchNode实例对draw方法执行一次调用,导致OpenGL调用减少,因此显着的性能改进

  • 2)每个CCSpriteBatchNode可以引用一个且只有纹理atlas



<我不是100%肯定,我想你的答案是:




  • 3)如果我有一个纹理atlas,例如game-art-hd.png,并在各种类中创建几个
    CCSpriteBatchNode我会得到多个绘制调用吗?
    换句话说,我假设每个CCSpriteBatchNode实例将
    调用自己的draw方法,导致多个GL绘图调用和
    的性能比具有一个共享批处理节点少。我是对吗?



    - 4)如果我在Sprite上使用多个框架制作的动画,我应该添加动画帧到Sprite批处理节点。如何
    我可以这样做?



    下面有一个代码片段如何通常动画sprite。可以注意到,sprite框架没有添加到
    sprite批处理节点。 为了更好的性能,我应该在初始化时执行
    。这是正确的吗?

      NSMutableArray * frames = [[NSMutableArray alloc] initWithCapacity:2]; 

    for(int i = 0; i <4; i ++)
    {
    NSString * bulletFrame = [NSString stringWithFormat:@animation-%i.png,i ];
    CCSpriteFrame * frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:bulletFrame];
    [frames addObject:frame];
    }
    CCAnimation * anim = [CCAnimation animationWithFrames:frames delay:0.1f];
    CCAnimate * animate = [CCAnimate actionWithAnimation:anim];
    CCRepeatForever * repeat = [CCRepeatForever actionWithAction:animate];
    [self runAction:repeat];

    }


  • 5)确认更喜欢避免
    在运行时向sprite批处理节点添加和删除精灵?


  • 6)CCSpriteBatchNode只考虑可见设置为true或实际位于屏幕区域外的位置的精灵?







3上的其他注意事项



为了解决我在3.中的假设,并减少CCSpriteBatchNode实例的数量,解决方案将遵循@Suboptimus在此中提出的建议回答。我喜欢建议的方法,初始化类,希望与MainScene类共享相同的批处理节点,而不是让他们通过self.parent.parent。(...)访问MainScene。parent.finallysharedbatchNode



其他人会建议通过使用self.parent .....父母并正确地引用MainScene。



这是软件工程术语中最好的方法吗?



我更喜欢清楚地显示sprite是通过明确引用MainScene类来添加的。如果我在团队中工作,或者如果我改变类层次结构。但这样做的缺点是,如果我想向批处理节点添加后续的sprite,我需要存储一个引用,导致更多的代码维护。



我问这个问题的原因是,如果发现我的传统的软件工程心态和Cocos2d父节点层次结构方法之间的轻微冲突。我是新的游戏编程,我想知道哪个方法是经验丰富的游戏开发人员在大型团队使用:)。 H

解决方案


  1. 正确,但绘图调用不等同于执行 draw 方法。绘制调用是OpenGL状态的变化,需要执行昂贵的操作来复位状态机。

  2. 正确。

  3. 正确。

  4. 不需要要做到这一点。动画运行在精灵上。所以只有sprite需要分批。如果一个动画帧不是来自相同的纹理地图集,当动画试图使用这样的帧时,CCSpriteBatchNode将会抱怨。

  5. 这是更可取的。在CCSpriteBatchNode中添加/删除精灵比添加/删除任何其他节点更加昂贵。因为sprite批处理节点的四边形需要更新。虽然如果你有很多子节点并且经常添加/删除,它可能只会有所不同。

  6. 不和没有。请参阅我的回答

3上的其他注意事项:



如果您的场景层次结构不够深,可以使用self.parent或者self.parent偶尔,但只有父 - 父关系几乎是固定的(即从一个精灵批处理精灵绕过精灵批处理节点,以获得底层的真正的父)。但我不建议去更深。请参阅我的回答了解self.parent和避免保留周期的技巧。



self.parent.parent。(...).parent 的问题是,如果您需要更改父子关系,则完全中断,例如添加或删除层次结构中的父节点。这将然后崩溃与EXC_BAD_ACCESS严重,它很难调试,因为你将必须检查每个父和父的父以查看它是什么样的对象。访问父级超过3级或以上的层次我不会考虑坏习惯。 这是一个可怕的做法



对于访问像共享sprite批次这样常用的节点,我更喜欢MainScene一个临时的Singleton类,当它处于活动状态时。然后,您可以从任何子节点执行以下操作:

  CCSpriteBatchNode * mainBatch = [MainScene sharedMainScene] .spriteBatchNode; 

要创建此临时单身人士:

  static MainScene * instance; 
- (id)init
{
self = [super init];
if(self)
{
instance = self;
}
return self;
}
- (void)dealloc
{
instance = nil;
}
- (MainScene *)sharedMainScene
{
NSAssert(instance,@MainScene is not initialized!
return instance;
}

与真正单例的区别在于它不初始化实例if它还不存在。因此,在sharedMainScene中的NSAssert。你应该只在场景实例已经运行的情况下访问场景实例,即它只能被特定场景的子节点使用。



这个半单例允许访问所有的场景属性。您还可以发送场景可以中继到其他节点的消息。



如果这个单例让你感到烦恼,那么总是有可能出现这种情况从主管获取运行场景:

  MainScene * mainScene =(MainScene *)[CCDirector sharedDirector] .runningScene; 

只有当runningScene是一个MainScene对象时,你才需要小心地转换到MainScene。 isKindOfClass:check或assert是有序的。


I am wondering how to optimize the usage of CCSpriteBatchNode. In other words I understand that:

  • 1) Each CCSpriteBatchNode instance performs one call to the draw method, resulting in a reduction of OpenGL calls and hence significant performance improvement
  • 2) Each CCSpriteBatchNode can refer to one and only texture atlas

What I am not 100% sure and I would like your answer is:

  • 3) If I have one texture atlas, e.g. game-art-hd.png, and create several CCSpriteBatchNode in various classes will I get mutliple draw calls? In other words, I assume that each instance of CCSpriteBatchNode will call its own draw method, resulting in multiple GL drawing calls and less performance than having one shared batch node. Am I right?

    - 4) If I am using an animation made of multiple frames on a Sprite, I guess I should add the animation frames to the Sprite batch node. How can I do so?

    Following there is a code snippet on how I normally animate a sprite. As can be noticed the sprite frames are not added to the sprite batch node. For better performance I should probably do this at initialization time. Is this correct?

        NSMutableArray* frames = [[NSMutableArray alloc]initWithCapacity:2];
    
        for (int i = 0; i < 4; i++)
        {
            NSString*bulletFrame = [NSString stringWithFormat:@"animation-%i.png", i];            
            CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:bulletFrame];
            [frames addObject:frame];
        }
        CCAnimation* anim = [CCAnimation animationWithFrames:frames delay:0.1f];
        CCAnimate* animate = [CCAnimate actionWithAnimation:anim];
        CCRepeatForever* repeat = [CCRepeatForever actionWithAction:animate];
        [self runAction:repeat];
    
    } 
    

  • 5) Partially refering to 4. do you confirm that is prefer to avoid adding and removing sprites to a sprite batch node at runtime?

  • 6) Will CCSpriteBatchNode consider only sprites that have visible set to true or sprites that have a position that is actually outside the screen area?


Other considerations on 3

In order to address my assumption in 3., and reduce the number of CCSpriteBatchNode instances, my solution would follow what suggested by @Suboptimus in this answer. I like the suggested approach of initializing classes that want to share the same batch node with the MainScene class, rather than having them to access the MainScene via self.parent.parent.(...).parent.finallysharedbatchNode

Other people would instead suggest to refer to the MainScene via using self.parent.....parent and casting it correctly.

Is this the best approach also in Software Engineering terms?

I prefer making it clear where the sprites are added by using an explicit reference to the MainScene class.. this should help if I am working in team or if I change the class hierarchy. But the downside of this is that I "need" to store a reference to it if I want to add subsequently sprites to the batch node, resulting in more code to maintain.

The reason I am asking this question is that if find a slight clash between my traditional "Software Engineering" mind and the "Cocos2d parent-node" hierarchy approach. I am new to game programming and I'd like to understand which approach is the one that experienced game developers that work in large teams use :). H

解决方案

  1. Correct, but a "draw call" is not equivalent to executing the draw method. A draw call is a change in the OpenGL state that requires performing an expensive operation to reset the statemachine. Binding a new texture or changing transform fit the bill.
  2. Correct.
  3. Correct.
  4. No need to do that. Animations run on sprites. So only the sprite needs to be batched. If one animation frame is not from the same texture atlas, CCSpriteBatchNode will complain when the animation tries to use such a frame.
  5. It's preferable. Add/Remove of sprites in a CCSpriteBatchNode is more expensive than add/remove of any other node. Because the sprite batch node's quads need to be updated. Though it probably only makes any difference if you have many child nodes and add/remove frequently.
  6. No and no. See my answer here.

Other considerations on 3:

If your scene hierarchy is not too deep, you can go with self.parent or maybe self.parent.parent on occasion but only where the parent-parent relationship is practically fixed (ie from a sprite batched sprite bypassing the sprite batch node in order to get to the underlying "true" parent). But I wouldn't recommend going any deeper. See my answer here for techniques for both self.parent and avoiding retain cycles.

The problem with self.parent.parent.(…).parent is that this completely breaks if you need to change the parent child relationship, for example by adding or removing a parent node in the hierarchy. This will then crash badly with EXC_BAD_ACCESS and it's hard to debug because you will have to check each parent and parent's parent to see what kind of object it really is. Accessing parents over 3 or more levels of the hierarchy I wouldn't consider bad practice. It's a terrible practice.

Personally, for access to commonly used nodes like shared sprite batches, I prefer the solution where the "MainScene" becomes a temporary Singleton class for the time while it is active. Then you can do the following from any child node:

CCSpriteBatchNode* mainBatch = [MainScene sharedMainScene].spriteBatchNode;

To create this temporary singleton:

static MainScene* instance;
-(id) init
{
    self = [super init];
    if (self)
    {
        instance = self;
    }
    return self;
}
-(void) dealloc
{
    instance = nil;
}
-(MainScene*) sharedMainScene
{
    NSAssert(instance, @"MainScene is not initialized!");
    return instance;
}

The difference to a real singleton is that it doesn't initialize the instance if it doesn't exist yet. Hence the NSAssert in sharedMainScene. You should only access the scene instance while it is already running, ie it's only to be used by child nodes of that particular scene.

This semi-singleton allows access to all of the scene's properties. You can also send messages that the scene could relay to other nodes. Or enqueue physics objects in the scene that need to be removed, but can't be removed during collision handling itself.

And if that singleton bothers you, there's always the possibility to get the running scene from the director:

MainScene* mainScene = (MainScene*)[CCDirector sharedDirector].runningScene;

You should be careful casting to the MainScene only if the runningScene is really a MainScene object. An isKindOfClass: check or assert is in order.

这篇关于Cocos2d:6对CCSpriteBatchNode的使用怀疑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆