Capturing Tile Positions

This guide explains how to capture tile coordinates such as Vector2i(5, 3) when an object is placed with Grid Building.

This is especially useful for:

  • isometric pathfinding and movement
  • occupancy maps
  • save/load of grid-aligned gameplay state
  • associating placed objects with one or more covered cells

Version note: This guide is validated for Grid Building 5.0.7.


Short Answer

The plugin gives you built-in ways to derive tile positions, but it does not automatically persist occupied tiles on the final placed object.

What exists already:

  • GBPositioning2DUtils.get_tile_from_global_position(...) converts a world position into a Vector2i tile.
  • TileCheckRule.get_tile_positions() exposes indicator-covered tiles during rule evaluation.
  • CollisionUtilities.get_rect_tile_positions(...) can derive a rectangular footprint in tile coordinates.
  • BuildingState.pre_instance_added lets you configure the placed instance before _ready() runs.

What you usually do in game code:

  • capture the tile or tiles you need when placement commits
  • store them on your own component, metadata, or save payload
  • let your pathfinding or occupancy system read that data later

When To Capture Tile Positions

There are three common moments:

1. During preview or rule evaluation

Use this when you need tile coverage for validation visuals or per-tile rule logic.

Relevant API:

  • TileCheckRule.get_tile_positions()
  • RuleCheckIndicator.get_tile_position(...)

2. Right before the final placed object is added

Use this when you want to stamp grid coordinates onto the committed instance.

Relevant API:

  • BuildingState.pre_instance_added

This is usually the best hook for pathfinding and occupancy data.

3. Later, from the placed object’s transform

Use this when you only need an anchor tile and can derive it from the final world transform.

Relevant API:

  • GBPositioning2DUtils.get_tile_from_global_position(...)

Single-Tile Anchor Pattern

If you only need one tile per placed object, store the anchor tile.

1
2
func get_anchor_tile(node: Node2D, map: TileMapLayer) -> Vector2i:
    return GBPositioning2DUtils.get_tile_from_global_position(node.global_position, map)

This is the simplest pattern for:

  • unit spawning
  • interaction anchors
  • isometric movement targets
  • tile-based references for save/load

Notes for isometric games

This still works for isometric TileMapLayer setups because the helper uses:

  • map.to_local(global_position)
  • map.local_to_map(...)

So it respects the map transform instead of assuming a square top-down grid.


Multi-Tile Footprint Pattern

If the placed object covers multiple cells, store an array of Vector2i values instead of a single anchor tile.

Option A: capture rule/indicator tiles

If your placement rules already generate the exact footprint you care about, use those tile positions during placement logic.

1
2
func get_rule_tiles(rule: TileCheckRule) -> Array[Vector2i]:
    return rule.get_tile_positions()

This works well when your occupancy footprint matches the plugin’s indicator footprint.

Option B: derive from a rectangular footprint

If your object uses a rectangular footprint, use the collision utility helper.

1
2
func get_rect_tiles(map: TileMapLayer, center_world: Vector2, size_world: Vector2) -> Array[Vector2i]:
    return CollisionUtilities.get_rect_tile_positions(map, center_world, size_world)

This is useful for:

  • buildings with a known width and height
  • pathfinding blockers
  • rectangular occupancy maps

Option C: derive from your own collision or polygon data

If your footprint is not rectangular, store whatever tile set your own movement or occupancy system considers authoritative.

That may come from:

  • collision shapes
  • custom polygons
  • tile rules
  • a custom footprint resource

Best Hook: pre_instance_added

BuildingState.pre_instance_added is emitted after the scene is instantiated but before it is added to the tree.

That makes it the best place to stamp tile data onto the placed instance.

1
2
3
4
5
6
7
8
func _ready() -> void:
    building_state.pre_instance_added.connect(_on_pre_instance_added)


func _on_pre_instance_added(node: Node2D) -> void:
    var map: TileMapLayer = level_context.target_map
    var tile: Vector2i = GBPositioning2DUtils.get_tile_from_global_position(node.global_position, map)
    node.set_meta("anchor_tile", tile)

You can replace metadata with a custom component or script property if you want stronger typing.


For pathfinding-heavy games, avoid recomputing occupied tiles everywhere. Store them once.

Example pattern:

1
2
3
4
5
class_name GridOccupancy
extends Node

var anchor_tile: Vector2i = Vector2i.ZERO
var occupied_tiles: Array[Vector2i] = []

Then populate it when placement commits:

1
2
3
4
5
6
7
8
func _on_pre_instance_added(node: Node2D) -> void:
    var occupancy := GridOccupancy.new()
    var map: TileMapLayer = level_context.target_map

    occupancy.anchor_tile = GBPositioning2DUtils.get_tile_from_global_position(node.global_position, map)
    occupancy.occupied_tiles = [occupancy.anchor_tile]

    node.add_child(occupancy)

For larger buildings, replace occupied_tiles with your full tile footprint array.

This gives your movement or pathfinding code a stable place to read from.


Save/Load Recommendation

The built-in PlaceableInstance.save() stores:

  • instance name
  • transform
  • placeable reference

It does not automatically store tile positions.

If tile coordinates matter to your game state, merge them into your own save payload after calling the plugin save method.

1
2
3
4
5
6
7
8
func save_with_tiles(instance: PlaceableInstance) -> Dictionary:
    var data := instance.save(true)
    var parent := instance.get_parent()

    if parent.has_meta("anchor_tile"):
        data["anchor_tile"] = var_to_str(parent.get_meta("anchor_tile"))

    return data

For multi-tile occupancy, save an array of serialized Vector2i values the same way.


Isometric Pathfinding Advice

For isometric games, the most practical setup is usually:

  1. Store one anchor_tile for interaction and identity.
  2. Store occupied_tiles for blockers or large structures.
  3. Feed occupied_tiles into your own pathfinding graph, AStarGrid2D layer, or occupancy map.

That keeps responsibilities clean:

  • Grid Building handles placement.
  • Your game handles movement and pathfinding.

Practical Recommendations

  • Store tile positions on placement, not by repeatedly recomputing them everywhere.
  • Use a single anchor tile for simple objects.
  • Use an array of Vector2i tiles for blockers and multi-cell buildings.
  • Use pre_instance_added when you want the placed object configured before _ready().
  • Add custom save data if tile occupancy matters after reload.