Closed ilexp closed 8 years ago
Progress:
A Tileset defines a set of tiles. (Well, obviously.) This does not include any actual visuals / pixel data. All pixel data is stored in distinct Pixmap resources, which are referred to by the Tileset.
A single tile can have multiple visual layers, which can be used by the renderer later as needed. Examples are {diffuse, normal, specular}, {regular, snowy, wet} or similar. The tileset doesn't care or define what to do with each layer. There is one Pixmap for each visual layer, not for each tile. A tile's visual representation always consists of the same combination of visual layer parts - think of them as the different textures in a tileset material, although their actual usage will be entirely up to the renderer.
A visual layer is configurable using the following data:
A tileset also defines metadata about each tile. Certain kinds of metadata are fixed an required by the system, but it is important to allow users to specify their own metadata as well - for game-related code and general customization. In general, tile meta information should be stored as a struct array to streamline access and storage.
System metadata (see below for more info):
Custom metadata can be added to each tile as a tag. The actual tags should be stored in a distinct array, whereas each tile just contains an index to the tag array. This allows users to easily modify tags centrally and is potentially more GC friendly as a bonus.
There are various ways to define collision data and integrate collision detection with Duality. The easiest way for integration would be to define collision data in the Tileset generate RigidBody shapes from the actual tilemap. This allows for physical interaction without specialized code. How should this collision data look like?
Each tile is either passable or not. This approach is insufficient for more complex tiles.
Each tile defines passability for all of its four edges separately. This allows "corridor" tiles that allow to move in one direction, but block paths in another. It's also what RpgMaker does, to name one example. This approach is a step in the right direction, as it allows more flexibility.
Each tile defines whether each of its four corners is solid or not, as well as a general "fill" flag on whether the specified shape is filled or just a set of edges.
It also allows a limited degree of collision shape interaction between tiles when their corners overlap, resulting in two adjacent near-impassable tiles joining together to form one solid impassable one.
This approach is likely to be preferred.
Building up on the previous approach, more vertices could be added to allow more sophisticated slopes and passability. However, this is probably too much.
When more complex collision shapes are required, users can always tag their tiles and write a custom Component that expresses these complex shapes.
The plugin should use the Extended AutoTile pattern for user convenience. Internally, each AutoTile (3x4) should be expanded into 47 distinct tiles for quick and generalized access. This affects both visual layers and collision information and can be done as an OnLoad
step.
The original AutoTile block no longer needs to be included explicitly in the final expanded tileset.
As it becomes apparent when thinking about the implication of this expansion and editor support, tilesets need to think about two kinds of tiles: Conceptual tiles and actual tiles. While all permutations of an AutoTile are each an actual tile (as stored in the tilemap), they are all the same conceptual tile (as seen by the editor for user experience).
With tilemap support in mind, it becomes even more important that the serialization of struct arrays is as efficient as possible. Check this.
Progress:
Note: A tilemap should be configurable as to whether it should apply Z offsets (actual Z or vertex offset) to each subsequent row of tiles, and how exactly to apply them. This is required for perspectives like the one seen in typical retro RPG games, where characters and mapped objects can span multiple tiles and need to be z-sorted correctly.
See the characters being behind the palm tree - if they walked around it, they should properly appear in front of it.
There could be different modes of Z offsets, and individual tiles could allow specifying additional Z offset data, for example to adjust the Z layer of higher palm-tree tiles, so the renderer can match their Z offset with the root's and they appear on a single Z layer. How "high above the ground" a tile begins is an information that would be defined in the tileset as well, and not primarily the tilemap - although it should be considered to allow tilemaps to specify this information as well, so the same wall-tile can be "stacked" Z-correctly.
Note: There probably should be some way to handle heights in a non-hacky way. See this for reference, especially the sideways stairs. All of this can certainly be done with some scripting / hacking power, but if there is a clean, generalized way to handle this, it could be part of the equation. Reconsider this later.
Dialog with one of the Crosscode guys about their tilemap implementation, to get some inspiration on how to handle heights:
The above approach of allowing to specify height offsets per tileset item and per tile in a tilemap, as well as a flat / upright distinction should be enough to allow tilemap renderers to handle this with proper per-vertex Z offsets.
For collisions, you could include the primitive (boolean) collision by default, and allow users to choose any other included types as well.
Some tiles wouldn't need any other sort of collision detection logic, such as completely impassable tiles (chasms etc).
For collisions, you could include the primitive (boolean) collision by default, and allow users to choose any other included types as well.
Some tiles wouldn't need any other sort of collision detection logic, such as completely impassable tiles (chasms etc).
Yep, certainly true!
The current plan of using the "Corner-Fill Solidify" approach would still allow boolean passability by simply specifying a tile to be completely solid, so this is essentially a matter of how comfortable the editor for this will be.
Since I plan a distinct Tileset / Tilemap editor, rather than relying only on the default Object Inspector editing, this shouldn't be a big problem. I'll have the workflow in mind and will try to not force users to use a more complex approach than they require.
Progress:
Compile
method, which transforms the specified source Pixmap
data of each visual layer into internal target data to be uploaded into an internal texture. This step can later be used for AutoTile expansion, added spacing or similar OnLoad-processing.Tiles with spacing: AutoTile Template: All 47 permutations of an expanded AutoTile:
Okay, so this issue has been mostly a random stream of thoughts, ideas and concepts. This needs to change, as it already becomes apparent that this will be a bigger task to do. Progress posts should use an "immediate todo" footer, so it's always clear what's next on the list and a general overview remains.
In order to generate a feedback loop during development, the first goal should be to create an operational implementation of Tilesets, Tilemaps and TilemapRenderers. This will enable test code to set up Resources and GameObjects that are directly visible in the editor and testbed. From there, new features can be added continuously.
Immediate ToDo:
Progress:
Immediate ToDo:
BeginUpdate
/ EndUpdate
API with optional spec of the modified rect in EndUpdate? Begin returns the internal Grid<T>
, End emits change events.SetTiles
API for copying a source grid onto the existing tilemap.AssumeRect
/ Resize
/ Clear
API.LockRegion
/ UnlockRegion
approaches are probably slow because of copying. Avoid.Progress:
Roadmap:
Progress:
Roadmap:
ISerializeExplicit
similar to PixelData
. Store all tilemap data as a compressed byte[]
. Right now, a 1000x1000 Tilemap with just indices takes about 160 MB in XML and 26 MB in binary. The optimal (uncompressed) size with the current data (one int32) would be about 4 MB.Progress:
TilemapRenderer
now renders textures tiles.Tilemap
now uses a new TilemapData
object to store its tile data, which implements a specialized compressed byte array serialization. A 1000x1000 with a simple pattern can now be stored in 27 KB XML, rather than 160 MB.Tileset
now supports different source and target tile spacings. It will use source spacings for the mapping and fill up target spacings with the nearest tile pixels color. That way, filtering artifacts can be prevented despite using an atlas texture.Roadmap:
Progress:
TilemapRenderer
culling was optimized to calculate the exact (object-local) rectangular region of tiles to render, reducing the number of actually rendered tiles by up to 50%, depending on the setup.Roadmap:
Progress:
CamViewState
to not include object editing functionality and moved it into a distinct ObjectEditorCamViewState
class, which derives from CamViewState
. This was a preparation step for implementing the Tileset editor later.Roadmap:
Public Domain / CC0 testing tileset with actual content: Taken from here.
Progress:
CamViewState
, including selection and simple drawing logic.Roadmap:
Progress:
Roadmap:
Progress:
Roadmap:
Progress:
Roadmap:
ALT
key.CTRL
keyProgress:
Roadmap:
CTRL
keyProgress:
Roadmap:
For reference on the Z sorting / height / depth handling of tilemaps, this is how RMXP handles it:
Basically the same as the above mentioned "height offset" idea for tilesets.
Progress:
Roadmap:
Progress:
Roadmap:
Progress:
OriginSelector
code to AdamsLair.WinForms
and tweaked its visuals.AdamsLair.WinForms
dependency.TilemapSetupDialog
.Roadmap:
PreviewProvider
for Tilesets.Progress:
Resize Tilemaps
dialog.PreviewProvider
for tilesets.Roadmap:
This plugin being an optional sidetrack of Duality development, its workload should be reduced to an absolute minimum until its first fully functional release in order to shift towards parallel development. Maintaining an extra branch makes it harder to introduce bigger changes into the framework on a non-master
branch.
Things that need to be done before release:
Things that are optional and can be left out for now:
New Tilemap
dialog. It's really just a rare convenience.Progress:
Roadmap:
This issue has been split up into multiple issues to keep track of progress and concept, see the referencing issues above.
Tilemaps are an incredibly easy way to create game worlds in 2D games, used in platformers, topdown games, strategy games, etc., so it might be worth considering to create an "official" tilemap implementation - especially since it has often been asked how to do this.
Some early (personal) notes on the topic as a reminder for myself:
Sorry for the bad image quality - I will transcribe the most vital information into (generally) readable text when requested or while iterating on it.