TilePositionDb Service
Introduction
The TilePositionDb Service monitors Tilemap callbacks to update a small internal dataset.
The dataset keeps track of which tile positions are occupied on the specific Tilemaps that you tell it to monitor.
TilePositionDb can optionally keep track of tiles with scaled sprites (x,y > 1).
Given this information, use query methods to see if a position is occupied by another tile even if the tile sprite is enlarged; for one or several Tilemaps at once.
This is useful, for example, as part of a custom Grid Graph for the A* Pathfinding Project (code available on request).
TilePositionDb supports time-varying size and position, such as you'd have when tweening a TPT tile's scale. BUT: It doesn't support accurately tracking rotated sprites.
If a sprite is rotated and Warnings are active (Checkbox in TilePlus.Config) then a warning message is printed to the console the first time that a rotated sprite is encountered. To avoid console spamming this warning occurs only once per run-session.
Use
Please check out the TpInputActionToTile component. This shows how to use position DB via the TpActionToTile Scriptable Object.
Here's an example that can guide how to use it yourself:
var posDb = GetServiceOfType<TpTilePositionDb>();
if (!posDb)
return;
//Add a map to monitor
posDb.AddMapToMonitor(m_Tilemap); //you have a field with this reference.
posDb.HandleScaledSprites = true; //recognize and keep track of scaled sprites (this is the default).
//-- Optional, not needed if there are no active tilemaps when the game starts.
//-- this is generally only needed in the Editor since there can be a scene already present
//-- when you click the PLAY button.
//it may be the case that the scene has tilemaps with existing tiles
//at startup. Hence, add all existing positions 'manually'
//duplicates are rejected in the AddPosition method.
var n = tilemap.GetUsedTilesCount();
if (n != 0)
{
tilemap.CompressBounds();
foreach (var pos in tilemap.cellBounds.allPositionsWithin)
posDb.AddPosition(tilemap, pos); //ignores positions without TPT tiles.
posDb.ForceUpdate(); //Force an Update so that the internal data structures are refreshed.
}
What is this?
A mini-database of occupied positions on Tilemaps.
This was originally intended for use with the 'Chunking'/Layout mode of TilePlus Toolkit, although there's no restriction on other uses. It's also optionally used by TpActionToTile, TpInputActionToTile, and other code where one wants to locate tiles on a tilemap from a mouse position.
Since it also (optionally) keeps track of tiles whose sprites are larger than one Tilemap unit, you can locate tiles EVEN if what you click on is the sprite outside of the tile's single position. It also properly handles sprites where the position has changed and the sprite's bounds no longer overlap the tile's native sprite bounds. But for efficiency, a sprite's rotation is ignored.
You can see this in the Oddities/Jumper demo where some of the tiles have sprites bigger than one unit.
When using the Chunking system: even if you have a huge Tilemap only the parts of it within the camera view (plus optional padding) are loaded into the Tilemap. Hence, the HashSets in this Service never get that large.
HOWEVER, this assumes that you do not use this subsystem to monitor dense maps such as a 'floor' or 'ground' Tilemap where almost all positions are filled. One should use colliders or some other approach for dense maps.
Sparse tilemaps make sense to use with this Service, so roads, obstacles etc.
Tiles are added and deleted from the data structures in this Service by the OntilemapTileChanged callback (from Tilemap).
Any tiles which are on the specified Tilemaps are added or deleted from a HashSet.
But that only covers one single position. If you have tiles with sprites that are offset by the transform-position of the tile sprite then that doesn't work.
Scaled or position-shifted sprites are handled automatically IF the
HandleScaledSprites property is set true. That's the default value. If you don't want the extra overhead involved in keeping track of these type of sprites then set the property false
immediately after the first request for this service.
After the first OnTilemapChanged event from the Tilemap the property value is locked.
Since evaluating scaled sprites involves some computation and Tilemap access (to get the actual sprite transform) these are cached and evalutated when this SRS is updated. That occurs at EndOfFrame (using the Awaitable pump) or at PostLateUpdate when using the PlayerLoop pump.
Note that TileBase tiles like Rule Tile and AnimatedTile cannot have scaled or position-shifted sprites since these tiles don't have a transform. They appear internally only at their actual Tilemap position. However, this isn't much of a limitation: use TpAnimatedTile or TpFlexAnimatedTile; and Rule Tiles (this one assumes) never have enlarged sprites.
When using the layout system you handle initialization via a callback (see Chunking Demo).
Otherwise you use AddMapsToMonitor (several overloads). After initialization the only way to completely change the setup is to use the .ResetPositionDb method or terminate/restart the service.
Once initialized, when a tile is added or deleted the internal HashSets are updated.
Use PositionOccupied(Tilemap, Vector3Int) to test if there's a tile at a position.
Important
Tiles are deleted from the internal dataset during the Tilemap callback. However, as mentioned earlier, additions are cached and evaluated near the end of the frame.
This means that there's always at least a one-frame delay for additions to the internal data.
The PosDBRefreshPerUpdate
property
The Tilemap callback can dump an enormous amount tiles into the update queue. This property can be used to control how many updates are processed each time that the PositionDb updates. This defaults to int.MaxValue.
For example, if you move a 10,000 tiles there will be 10,000 entries in the update queue. If PosDbRefreshPerUpdate is left at it's default value execution will block until all the updates are processed.