I've looked at the examples regarding zOrder and vertexz I first tried vertexz, but gave up because of transparency issues. In the example for using z order each horizontal plane is on a separate layer in Tiled. How can I set the z order of each tile or horizontal plane without making dozens of layers in Tiled? CCTMXLayer doesn't support reorder child so I'm not exactly sure the best way to manually set the zOrder of each tile so that multiple objects can display correctly on the map.
CCTMXLayer Isometric zOrder
(23 posts) (5 voices)-
Posted 1 year ago #
-
I see how that solution would work for moving/swapping tiles within the CCTMXLayer, but if I want CCSprites to be a part of the CCTMXLayer how would I implement that? CCTMXLayer doesn't support add child, it seems to deal with quads directly. Should I be implementing my own addChild: that uses the addQuadFromSprite:?
Posted 1 year ago # -
Could you post a picture of your problem? I don't have any experience with isometric maps, but I do know a thing or two about sorting and vertexZ.
If you need to change the order of every tile in a layer, it would be easier to reorder the layer.
Posted 1 year ago # -
Ok, here's an example:

The bottom TMX layer is the floor, green tiles. The next layer is the grey, red and blue walls, which could be tree's or other objects on the map that have height. The orange and purple boxes are CCSprites, possibly units on the map.
In this example the orange sprite needs to be behind the red and blue walls, but in front of the grey. The purple sprite needs to be in front of the red and grey walls, but behind the blue. When they move around the map, their z position needs to change to be in front of or behind these tiles.
If this were a regular layer and the walls were also just a bunch of CCSprites then the z position wouldn't be much of a problem since I could simply reorder the sprites whenever one changes its position.
Posted 1 year ago # -
Okay I understand what you're trying to accomplish. You'll have to override addChild and reorderChild indeed. When using AddQuadFromSprite you'll have to use the textureAtlas methods directly for reordering (normal reordering based on removeChild/addChild won't work).
Posted 1 year ago # -
Thanks for the help, I appreciate it. Do you think you can briefly explain how to use AddQuadFromSprite with a CCSprite and using TextureAtlas? You seem to have a good understanding of the classes. I'm trying to keep all the functionality of CCSprite. I've been trying different ways, but I don't fully understand how CCTMXLayer or TextureAtlas work. CCTMXLayer is a subclass of CCSpriteBatchNode, which only supports children with the same texture id so I'm starting to think this might not be possible with the way CCTMXLayer is currently designed. The CCSprites I will be adding will never be a part of the same sprite sheet.
Posted 1 year ago # -
I was assuming indeed that your sprites where on the same texture.
If your sprites aren't part of the same spritesheet then it isn't going to work at all. VertexZ is your only option in that case. Though for vertexZ to work with transparent sprites, the sprites need to be sorted from back to front, which isn't currently possible if your sprites aren't on the spritesheet of the map.
You would have to break the batch draw of the batchnode/textureAtlas into pieces and fit those sprites in there to get the right draw order. That would require a rewrite of the textureAtlas to accept ranged drawings and changing the draw method of the batchNode. It'll be tricky to get this just right.
It would be far easier if you could change your design to make the sprites part of the texture. If that's not a possibility, I could elaborate more about this.
Posted 1 year ago # -
Hi enorac ,
Sorry to bother you out of sudden , do you mind to share out how did you rotate or change the camera view of the tilt map that looks exactly the same as yours?
ThanksPosted 1 year ago # -
Yeah that's what I was afraid of. The reason it's not feasible for me to put them on the same sprite sheet as the map is each character/unit could have a dozen animations. Think of it like an rts or rpg with many sprites and many animations that interact can with the map.
Maybe there is a simpler approach? This does sounds like it's getting complicated quickly :) How would I go about doing what you proposed?
@phpbug - The game is drawn in that perspective and the map was created with Tiled, which supports the isometric tiling.
Posted 1 year ago # -
I wish there would be a simpler approach, though this is less work than I expected.
The first part would be to create a method that draws ranges of quads in the textureAtlas, since it would take more time to explain than to code it myself, I've already made that method. Add a BOOL firstTime to the textureAtlas to make it work.
-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start {//draws n quads starting from index start // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY // Unneeded states: - glBindTexture(GL_TEXTURE_2D, [texture_ name]); #define kQuadSize sizeof(quads_[0].bl) #if CC_USES_VBO glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); //it's best to call bufferSubData only once for the whole buffer, that's faster if (firstTime_) { //set firstTime to YES again in the beginning of the batchnode draw method firstTime_=NO; glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quads_[0]) * n, totalQuads_); } // vertices glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices)); // colors glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors)); // tex coords glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); #if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, start * sizeof(quads_[0])); #else glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, start * sizeof(quads_[0])); #endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #else // ! CC_USES_VBO NSUInteger offset = (NSUInteger)quads_; // vertex NSUInteger diff = offsetof( ccV3F_C4B_T2F, vertices); glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) (offset + diff) ); // color diff = offsetof( ccV3F_C4B_T2F, colors); glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff)); // tex coords diff = offsetof( ccV3F_C4B_T2F, texCoords); glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff)); #if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, indices_ + sizeof(quads[0])*start); #else glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices_ + sizeof(quads[0])*start); #endif #endif // CC_USES_VBO }Step two would be subclassing CCSpriteBatchNode and adjusting the draw method to draw ranges of sprites. I'm not sure if I'm explaining this clear enough, so here is a simple example..
Suppose you have a sprite on a tile with atlasIndex 100 and one on a tile with atlasIndex 200
-(void) draw { //.... other code textureAtlas.firstTime=YES; [textureAtlas_ drawNumberOfQuads:101 fromIndex:0]; //draw sprite1 [textureAtlas_ drawNumberOfQuads:100 fromIndex:101]; //draw sprite 2 //draw remaining quads }Step 3 subclass/change CCTMXTiledLayer and CCTMXTiledMap to make use of the subclassed batchNode.
Posted 1 year ago # -
One correction, the glBufferSubData line has to be
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quads_[0]) * totalQuads_, quads_);Posted 1 year ago # -
Wow! I owe you one, you're awesome. Now is see that in CCSpriteBatchNode node you're breaking up the drawing with the ranges of tiles "surrounding" the CCSprites. Few questions. The draw method of BatchNode obviously needs to be changed like the example you gave, but is it possible to make this dynamic? With X amount of CCSprite children whom can change position and Z index, how should the draw loop be designed?
Something like:
int idx = 0; for (CCSprite *child in children_) { [textureAtlas_ drawNumberOfQuads:(child.atlasIndex - idx) fromIndex:idx]; [child draw]; idx = child.atlasIndex + 1; } [textureAtlas_ drawNumberOfQuads: (textureAtlas_.totalQuads - idx) fromIndex:idx];Also should addChild look something like this?
-(void) addChild: (CCSprite*)child { NSInteger z = child.position.x + child.position.y * layerSize_.width; [child setVertexZ: [self vertexZForPos:child.position]]; [child setOpacity:opacity_]; // get atlas index NSUInteger indexForZ = [self atlasIndexForNewZ:z]; // Optimization: add the quad without adding a child [self addQuadFromSprite:child quadIndex:indexForZ]; // insert it into the local atlasindex array ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); [super addChild:child]; }Posted 1 year ago # -
Yeah, you need to make that dynamically indeed, I didn't do all the work :)
I wouldn't add them to the children array at all (since they don't share the same texture). Add those sprites to another bathNode and create your own array in the modified batchNode to hold those sprites as well.
One more thing you can't use the atlasIndex of the sprite (since it would be the atlasIndex in the other batchNode). You could use the userData field to store the correct index.
I think organizational wise it would be best to add the map to a layer as well as the batchnode(s) containing the sprites. When you want to modify the map (zoom, rotate), use the layer instead, so all sprites on it will change as well.
Posted 1 year ago # -
OK, let me just make sure I've got the organization right with some odd pseudo code:
ModifiedBatchNode - CLASS textureAtlasRanged_; newSpriteArray_; draw { loop { [textureAtlasRanged_ drawNumberOfQuads:x fromIndex:x]; [newSpriteArray_[x] draw]; } } Modified/Subclassed TMXLayer - SUBCLASS childrenBatchNode_; <-- Problem with multiple sprite sheets? addChild { [self addQuadFromSprite:child quadIndex:indexForZ]; [newSpriteArray_ addObject:child]; [childrenBatchNode addChild:child]; [child setUserData:[self getAtlasIndexForZ:z]]; }The current way I've been loading sprites is:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"Unit.plist"]; CCSprite *unit = [CCSprite initWithSpriteFrameName:@"UnitFrameName01.png"]; [layer addChild:unit];Then loading animations with the CCAnimationCache. How does this work with CCSpriteBatchNode?
Posted 1 year ago # -
That's almost correct, the tmxlayer only needs to have the modified batch node as property, no addChild additions.
-CCLayer --CCTMXTiledMap (subclassed, accepets modified CCTMXTiledLayer) --CCTMXTiledLayer (subclassed) --inherits from a subclassed CCSpriteBatchNode --normal CCSpriteBatchNode -> contains sprites that need to be drawn on mapThere is enough information in the documentation and on this forum on how to get a batchNode working with spriteFrames.
Posted 1 year ago # -
You asked for a alternative, @Lam mentioned one in this thread. Though it would mean reimplementing the isometric map yourself.
Posted 1 year ago # -
I still can't get this working correctly. Here's my current problems:
- This can't be broken up into just a few layers or "lanes" because this is basically faking 3D. Sprites can be at any z index at any given time. I need some sort of global z index - reordering within BatchNodes doesn't work.
- BatchNodes are almost useless in this situation, because even if 50 sprites share a texture they still need to be drawn at globally different z positions and therefore drawn independently. With likely a single texture dedicated to each type of unit - trying to breakup the drawing in X amount of BatchNodes just doesn't seem feasible.
- VertexZ is also useless in this scenario because with transparency it still needs to be drawn from back to front or you get transparency artifacts. Might as well just reorder sprites.
- CCTMXTiledMap just isn't built to do this and CCTMXLayer is built upon CCSpriteBatchNode, which runs into all the problems mentioned above. The ranged version above would work well if I was only using one or two separate BatchNodes, but in this case I can't.
Looking at this image every object has a different z index, the further back in z the darker objects get. Each sprite has a different z depth.

I'm now thinking the only way to go about this is to either ditch or rewrite CCTMXTiledMap/Layer completely and put all CCSprites on one layer and just reorder them every frame. Hopefully not using BatchNodes won't be a huge performance hit.
If anyone has done this I'd be curious how they approached it. I know there a very few 2D isometric games out there.
Posted 1 year ago # -
Hi @enorac, I've done an isometric engine, I did it even before cocos2d had the isometric tile maps, so I did my own solution from scratch. On my game because I can have all the sprites on one CCSpriteBatchNode (used really the old AtlasManager as I used an old version of cocos2d), I can really simple re-order the sprites, and works fine. But because I wanted to include particles, I couldn't use a batchnode, not for the re-ordering objects (floor tiles and other fixed objects are on a batchnode), so my ordering objects where simple sprites on a layer, obviously with the penalty that if you include too much of them, your fps drops, so I had to compromise the performance and run only at 30fps, but see:

This is an extreme situation (I never have so much sprites on screen), it drops to 15fps on an old iPod 1G, about 20fps on iPhone 3G, but gets 30fps stable on iPhone 4. Fortunately I never have that much sprites so I can always run 30fps in most situations.
Or maybe you should just really try to fit all your sprites on same spreatsheet texture, for all devices you can use 1024x1024, or 2024x2024 for 3GS and up, and that's really a lot. Maybe you need to reduce the number of animations or different characters.
Hope it helps.
Posted 1 year ago # -
@araker: Thanks. I'll add your new method in
CCTextureAtlas. I'm not sure aboutfirstTimethough. Perhaps it is better to have attributes likeSTATIC / DYNAMICbuffer. It is isSTATIC, then the buffer won't be updated. Another optimization would be not to send the colors. ThetextureAtlascan have a list of attributes to be send. eg:vertex, color, texCoord.For cocos2d 1.0 I'll add this method
-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) startwithoutfirstTimeand for 1.1 / 2.0 I'll add the rest of the functionality: static/dynamic, attributes to send.UPDATE: Ah... Now I understood
firstTime... it is not aboutstatic / dynamicbut about updating the whole buffer once before rendering it.Dynamic / staticis still useful, but won't be part of 1.0.Posted 1 year ago # -
@riq, I couldn't think of a nice solution for firstTime where the textureAtlas sets the variable. It needs to be set to YES at the start of every frame. Now the container (batchNode) is responsible for setting it.
Posted 1 year ago # -
Added
-(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start
https://github.com/cocos2d/cocos2d-iphone/commit/aad266acd2b9854a8a2722244147d57368b89d79@araker: One way to do it would be to schedule an
update, and update the buffer there. I'll add it in v1.1.The committed version updates the buffer every time it is called (not as optimal as @araker's version). It fixed some out-of-bound bugs in @araker's version.
Posted 1 year ago # -
great post!! luckily there is an answer for this :)
Posted 1 year ago #
Reply
You must log in to post.