|
|
|
|
Reply From: |
Zylann |
I use tilemap for its initial purpose: build all parts of the world that are tiled so it’s more efficient to edit and more efficient for the engine to render. It’s like a terrain of static things.
If I ever need to add something that can move, rotate or mark a trigger position, I would do that with a node, not a tile (or use a script, read below).
Sometimes I use another layer of Tilemap for marking areas, but mostly because they are bound to tiles as well with the same constraints.
For the same reason the Particles
node exists instead of a bunch of Sprites
flying around, I see Tilemap
as both a tool and engine optimizations technique. Optimizing requires constraints (using a grid is one) but that doesn’t mean you are limited to what Tilemap
provides. You can think differently to add new features to it.
For example, in my game I needed to make some tiles emit particles. The “node” way would be to add a Particles
node to every tile, but it’s tedious and wastes a huge amount of time and resources the bigger your map is. So instead, I’ve gone through the “tile” way: I created a script that picks a random position one time per frame on the visible world rectangle, and if this lies into a specific type of tile, I draw a particle from the script (no need for nodes). It is surprisingly cheap for a pure GDScript technique
Another example: I need to make a tile “bump”, so it really creates a moving sprite for a brief instant. Tilemap doesn’t allow that. So what I did is to spawn an animated sprite only at the required position, then hide it when the animation is finished. If the whole tile was moving, I would have removed the tile and spawned its animated version as a node, and when it’s done I would place again the static tile.
The games you listed can perfectly use the tilemap, although I’m not sure yet how performant it would be for Conway’s game of Life or the tiny tiles of Terraria (maybe it is? would be worth trying, always test before optimizing stuff).
You can implement techniques like I described to get the extra effects.
The only bit I’m not sure of is frame-by-frame animated tiles. Sure you can script that too, but I’m surprised Godot doesn’t have support for it.
To save tilemaps we need to mess with ResourceSaver and save the part of the scene tree at the root of the tilemap layers. We can mess with ownership so that the tilemaps are owned by a “mapgen” object and sprites and such are owned by some other object. This acts as a filter so only the tilemaps get saved. The level is reloaded as a scene then placed into the game’s scene at the appropriate node. This works better on large “infinite” sandbox maps if the map is generated (and saved) in chunks of fixed size. Each chunk would have a parent node root with the tilemap as a child. Within a chunk the upper left tile is always 0,0 and when loading a chunk the root node of the chunk is translated to move the loaded chunk into the correct world space coordinate position. So for instance you’d load the origin chunk and translate it 0,0
but the x+1 chunk (assuming 16x16 chunks) would have the root node moved to 16*tile_width,0
the x-1 chunk -16*tile_width,0
the y+1 chunk 0,16*tile_height
the y-1 chunk 0,-16*tile_height
the x+1,y+1 chunk 16*tile_wdith,16*tile_height
, etc.
There are caveats if multiple layers are used. If the layers are fixed (don’t move relative to other layers) it’s okay to generate the chunks in such a way that each chunk has all the layers within that chunk region. This would net the best compromise between file/object count and flexibility (map layers cannot scroll independently).
If however the layers use parallax and thus move relative to one another then each layer needs it’s own chunk system (a separate chunk ends up being made at each layer). This creates a large number of files and could be slow due to the large number of additional objects in the scene.
Mobiles (sprites) need a bit more work to move correctly on such maps since their movement scripts need to child them to the appropriate chunk’s tilemap. Easiest way to move such an object is to temporarily set_parent()
it to a non-visible “black hole” object, calculate which chunk and position modulo, set_parent()
it to the appropriate tilemap within the chunk (based on layer the object should be in) set its translation to ( world_x % (chunk_width * tile_width) , world_y % (chunk_height * tile_height) )
PS: There’s also all sorts of edge cases to watch out for such as moving a sprite to a location with no chunk (should one be created/generated?) or if the chunk is not loaded (it needs to be loaded in). Chunks could load on the fly if small enough and only one or two need to load but when a whole region of chunks need to load a loading screen with progress bar might be appropriate. Et cetera.
gau_veldt | 2018-10-29 00:27