Hey, I found this topics in numerous threads, but never answered. For example here:
The question is simple: When I load .tmx iso map, how do I put a player character dynamically into the map, with correct sorting?
Lets say I have a “walls” layer and I want the player to appear correctly behind / in front of walls as he is walking around.
The layers are one big object, how can I insert something “inbetween” there. I know how its done in theory, I’ve done my own isometric engine in the past. But I don’t feel like writing one from scratch just to do this basic thing, with existing tmx support. Surely the developers didn’t create just a “tmx viewer” which is currently more like background image, but actually something you can insert stuff into? I need some way to affect the sorting of the individual tiles and player.
For the C++ engine there appeared to be a solution using some vertexZ thing, which is not available in Cocos Creator (or am I wrong?). So is there any other solution to insert sprites into layers with correct ordering, or do we just have just “tmx viewer” for iso maps, which is rather useless unfortunately for any game which needs like…a player, npc’s etc?
For this to work, the player would be added to the walls TMXLayer, not the TMXTileMap… note the TMXLayer is a SpriteBatchNode, so the player has to share texture with the layer. You can add nodes to the TMXLayers or the TMXTileMap, just make sure you follow sprite batch texture rules.
You can also break the walls into 2 layers (infront/behind), then have the players texture as a layer in-between those.
@tdebock Thanks for your reply! I should probably say that I’m using Cocos Creator scripting (TypeScript), not the C++ flavor.
I have a feeling that you mention the C++ classes (TMXLayer, TMXTileMap)? My question was, if I can achieve this with the TypeScript / JS bindings. They seem somehow limited when I compared the docs with the original C++ classes.
Breaking that layer wouldn’t help I think, you might have the same wall as both back and front, depending where you stand (and its not just walls, regular objects like a vase etc.).
Regarding the child nodes approach, when I tried to query number of children nodes of a cc.TiledLayer, it says “0” (also in the IDE its pretty clear there are no children for the layer nodes). That’s why I’m confused and mentioned it appears as one big homogenous layer.
I’m familiar with batching otherwise, that wouldn’t be a showstopper. I just don’t know where I should insert that sprite in terms of the cc.TiledLayer structure.
I have never tried in Creator. However, I don’t see why this can’t be done. Let me ask an engineer to take a look at this. Please remember that there is a national holiday going on and also mandatory shutdown due to the Corona virus.
@slackmoehrle Ok great, maybe I’m just overlooking something, as I’m still new with Cocos. It’s a great environment and something I’d love to develop games in instead of Unity, it feels more natural to transit from AS3 to TypeScript.
Definitely can wait a few days due to the holidays… and the virus ugh Just don’t want this to die away. I saw it was an issue in the C++ engine and hints that some classes solving it weren’t binded in TypeScript or whatever. But maybe thats not the case anymore, most of the threads I’ve read were several years old…
I am a C++ Engine user, I do not have much additional knowledge to add from a Cocos Creator perspective. But, I would break the layer into 2 parts, front/back… then, when you are drawing the TileMap, set the localZOrder of the layers to say 0 and 2, then you can add the character as a Node to the TileMap at localZOrder 1. That is at least something I would try first… If you add the character into a tile layer itself, you will need to track tile GIDs, and knowing which is where as far as Zorder, and will likely get a lot more complicated quickly.
I’m sure that hardcoding part of the walls as “back”, and another as “front” and inserting the player inbetween would work as long as you stay inside a rectangular room. But what if you walk outside the room and step in front of the “front” wall? You’d get the character hidden under the wall. What I’m saying is the walls need to sort dynamically with the player depending on where the player stands. Hardcoding some layers as front / back doesn’t solve the situation in my opinion.
The same for any objects (trees etc.), you can’t say which tree is “front” or “back”. As you walk around the tree, that aspect changes, if you step in front of the three, it must be behind you, if you step behind the tree, it must be in front.
When I was coding my own C++ iso engine years ago, the solution was pretty straightforward and much simpler. I made the player part of the “objects” layer (where I had walls / trees etc.). Then I just sorted everything to draw from back to front. If the player moved to a different tile I would resort. That is the only sane approach as far as I’m aware. With the C++ Cocos I saw people were using vertexZ to achieve this kind of thing. They’d set the layer to vertexZ “auto”, then set the proper Z for the player to “fit in”. That made sense at least. But there’s no access to “vertexZ” from TypeScript, based on what I saw at least.
Yeah, I was trying to do that initially, because it’s what I would do in my engine. But unfortunately - (like I mentioned earlier), it seems you can’t even do that All tile layer nodes have 0 child nodes. It’s just a single node in the Creator IDE rendering itself as one big thing. When I query # of child nodes of the layer node from code, it says 0. So I’m not even sure how to “add character into the layer”, however problematic that might be.
The only solution how to get anything into a layer seems with this:
So I’d need to have a special tile with player sprite, which I would then erase (if at all possible) by setting a tile to null or some kind of empty tile, and then set the player tile at a different position lol. But that is incredibly messy even if it works, it would appear that the player sprite is “skipping” and I don’t think I could animate that sprite.
The C++ solution with setting Z (or vertexZ) seemed good, but there’s no way I found to affect vertexZ from TypeScript. Here’s one of the many threads describing the solution:
There was additional transparency issue, lol…but lets ignore that. I’m hoping I’m really missing something fundamental and I don’t need to write my own TMX parser, when there’s one already available I’d need one of the following:
Have tiles “exposed” as nodes so I can insert sprites “inbetween” (affecting the graph draw order?)
Or, have the ability to at least change the “vertexZ” of nodes like people could in C++, or some alternative to that. Of course, the tiles would need to use that as well. So then when I say “player.vertexZ=10”, I’ll get part of the tiles rendering “behind”,and part in front. When I change players position, I’d change simply players Z and everything would render correctly.
In our project, Fleets of Heroes, I solve a similar issue by dynamically changing zOrders of objects based on location on the map… If lower on map, increase order (and some additional logic for left right etc)…
on update loop the objects get positioned in z based on position in x-y…
Yeah I tried something like, adding children and trying to change z to different numbers, didn’t seem to work - but I’ll retry, I might have done it differently. If this worked then it would certainly be one of the ways I was looking for!
That game of yours seems cool! What you describe is what I was doing as well in my old engine, though a subtle difference might be you’re sorting dynamically added objects with each other (if thats the case, it looks like a base the player built up). I was talking more of a situation with .tmx tile(like a tree) sorted with dynamically added player. Nevertheless, the z sorting should still work if I can affect the players z. I’ll try to focus on that now and report back
I’ve been sick for couple days - anyway, now I’m trying again. I just can’t seem to do it. I’m using the walls texture itself when inserting a new node to make sure it won’t mess up batching or whatever…
// Trying to add dynamic sprite into Tiled layer
// objectsNode is the .tmx layer called "Objects", where I'm trying to insert something (it contains walls)
let objectsNode:cc.Node = mapNode.getChildByName(“Objects”); // get the layer node
let texture:cc.Texture2D = objectsLayer.getTexture(0); // get its own texture
let spriteFrame:cc.SpriteFrame = new cc.SpriteFrame(texture);
let node:cc.Node = new cc.Node();
let spriteComponent:cc.Sprite = node.addComponent(cc.Sprite);
spriteComponent.spriteFrame = spriteFrame;
// add a test node with the object layer texture to make sure I’m not messing up batching(?)
// that texture just contains bunch of walls, its enough for me to test if any ordering changes
node.z = 1000;
node.zIndex = 1000; // trying to play with z’s…
So here you can see how I’m trying to modify all of the z’s I found, lol. The result is still the same: the “node” is rendered above all of the walls from “objectsNode”, no matter which Z I try. I even tried -1000…0, 100, 1000 etc. No change. I can’t get any visible sorting changes, like the top wall beign inserted under one of the bottom walls. Just to illustrate:
The “top” wall is dynamically created (this one I’m trying to affect with zIndex). The “bottom” wall is placed inside the “objectsNode” (tmx map layer) - thats all loaded from the tmx.
So, the “top” / dynamic wall is now a child of objectsNode. I’ve inserted it into “objectsNode”, where the other walls are. But it stays on the top of the the .tmx walls for any zIndex.
I briefly checked more of the C++ code. I’m not 100% sure whats going on there but it doesn’t seem straightforward at all with the z’s. The indexForZ seems to be refering to some texture atlas index. Not sure if that equals to zIndex in cocos creator(I’d say zIndex is more for cc.Node ordering, not for messing with texture atlas drawing order or whatever its using underneath).
You can use Object Group to make buildings, trees and other objects that need to be shielded from each other with nodes. Since the Object Group is implemented by nodes, occlusion can be achieved by zIndex attribute. The nodes created by developers can be added to the corresponding map layer.