Collision Mapping¶
This document explains how the Grid Building plugin converts Godot 2D physics shapes and collision objects into tile coverage used for placement validation and rule checks.
Purpose¶
When a player previews or places a building/object, the system needs to know which grid tiles are "touched" by that object's collision so that:
- Placement rules (e.g. cannot overlap, must be on foundation) can be evaluated per tile
- Visual indicators (RuleCheckIndicator) can show which tiles are valid/invalid
- Downstream systems (e.g. cost, biome, adjacency, blocking) can reason about exact occupied tiles
The Collision Mapping subsystem standardizes this translation with consistent geometric rules and thresholds.
Key Components¶
| Component | Responsibility |
|---|---|
CollisionMapper |
Orchestrates extraction of collision geometry from objects and produces tile position → rule / object maps. |
IndicatorCollisionTestSetup |
Lightweight context wrapper storing an object's transform, origin offset, and cached geometry meta used in mapping. |
RuleCheckIndicator |
Visual probe shape whose bounds define the sampling region; per-frame it revalidates placement using mapped collision tiles. |
GBGeometryMath |
Static geometry helpers: shape → polygon conversion, polygon bounds, polygon/tile overlap tests with epsilon filtering. |
TileCheckRule |
Rule data object listing which collision layers (mask) it applies to and additional semantic constraints. |
GridTargetingState |
Aggregates targeting context: maps, positioner, currently inspected TileMap layer, etc. |
Supported Collision Sources¶
Collision tiles can be derived from:
CollisionObject2Downers (e.g.StaticBody2D,Area2D,CharacterBody2D) via their childCollisionShape2Dnodes- Direct
CollisionPolygon2Dnodes (even if not wrapped by a physics body) – handled specially when no owner test setup exists - Generated indicator shape (rectangle or custom) used as a bounding region for rule checks (not itself mapped but used for pruning)
Currently supported primitive shapes (converted to polygons):
RectangleShape2D→ 4-point polygonCircleShape2D→ N-gon approximation (default 16 segments)- (Internal / transitional) Capsule → approximated polygon (now mostly replaced by explicit polygons in assets)
Shape → Polygon Conversion¶
All collision evaluation reduces to polygon vs tile area overlap. Each source shape is converted into a local-space polygon and then transformed into world space using the owning node's global transform plus any indicator offset. For complex objects with multiple shapes, polygons are processed independently and merged into a tile coverage map.
Advantages:
- Unified overlap test implementation
- Enables accurate partial tile filtering via area threshold
- Supports arbitrary future shapes by adding only one conversion function
Tile Overlap Determination¶
The grid uses uniform tiles (commonly 16×16). For each polygon:
- Compute its axis-aligned bounds (
get_polygon_bounds). - Convert bounds into an inclusive tile range (
get_inclusive_tile_range). - Iterate candidate tile coordinates (coarse pruning).
- For each candidate tile, compute polygon ∩ tile rectangle area (fast path + precise fallback inside
does_polygon_overlap_tile_optimized). - Apply epsilon threshold to decide if the overlap is significant enough to count.
Epsilon Threshold (Significance Filter)¶
Very thin or pixel-fringe touches (e.g. a 0.1×0.1 shape brushing a tile corner) should not reserve a tile or trigger rule failures. The system applies an epsilon threshold that can be interpreted as:
- If < 1.0: fraction of the tile's full area (e.g. 0.05 = 5% of a 256 px² 16×16 tile ≈ 12.8 px² minimum)
- If ≥ 1.0: absolute pixel area (legacy / fallback behavior)
This dual semantics supports both expressive fractional thresholds and exact pixel thresholds when needed. Current standard: 0.05 (5%).
Rationale: Stabilizes placement visuals and prevents single-pixel alpha artifacts from inflating occupied tile counts.
Multiple Shapes & Aggregation¶
If multiple shapes overlap the same tile, that tile is inserted only once; the system aggregates contributing objects or rules in a list attached to the tile coordinate key.
Collision Layers & Rule Filtering¶
Each TileCheckRule declares an apply_to_objects_mask. During mapping, collision objects are filtered by their collision layer bits. A tile is considered relevant for a rule only if at least one contributing object has a matching layer bit set. This ensures rules don't falsely trigger on unrelated decorative collisions.
Examples:
- Foundation rule only inspects tiles occupied by layer 1 (foundation-tagged objects)
- Clearance rule inspects blocking layers (e.g. foliage, structures) but ignores visual-only layers
Indicator Integration¶
RuleCheckIndicator performs per-frame validation:
- Indicator requests collision mapping within its shape bounds
- Placement rules are evaluated against the mapped tile set
- Visual state (valid/invalid color) updates immediately when geometry changes (e.g. moving over edge cases)
The indicator shape itself does not contribute to collision mapping—it's a sampling window that caps unnecessary tile iteration.
Precision vs Performance¶
Optimizations:
- Bounding box prefilter drastically reduces per-polygon tile checks
- Early reject when polygon is wholly outside tile rect
- Simple area estimate shortcut before optional precise intersection (implementation detail)
- Fractional epsilon prevents noise from increasing map size
Performance Notes:
- Typical building polygons span < 150 tiles; mapping occurs per-frame only while targeting (preview state)
- Use minimal polygon vertex counts consistent with silhouette fidelity (e.g. 20-point egg instead of high-res capsule sampling)
Example Scenarios¶
Small 15×15 Rectangle on 16×16 Grid¶
- Bounds cover exactly one tile
- Overlap area = 225 px² (> 5% threshold)
- Mapped tiles: 1
Thin Edge Polygon (0.1×0.1) Touching Tile Corner¶
- Area = 0.01 px² (<< 5% of 256)
- Filtered out (0 tiles)
Complex Building with Multiple CollisionShapes¶
- Each shape converted, tiles aggregated
- If two shapes share a tile, tile stored once with both object refs
Testing & Validation¶
Automated tests cover:
- Layer filtering (ensuring only matching mask bits considered)
- Rectangle & polygon overlap (including multi-tile coverage)
- Epsilon filtering of micro-overlaps
- Bounds calculation & shape conversion utilities
- CollisionPolygon2D trapezoid detection sanity checks
These tests ensure regressions (e.g. accidental tile overcount) are caught early.
Asset Authoring Guidelines¶
- Prefer
CollisionPolygon2Dfor irregular silhouettes (e.g. large egg) over shape approximations (capsule) for tighter tile usage - Keep polygons reasonably simple (<= 32 vertices) unless higher fidelity is required
- Ensure collision layer bits reflect semantic purpose (foundation, blocking, decorative) to enable accurate rule filtering
- Avoid tiny disconnected polygon slivers; they may be ignored by epsilon filtering
Extensibility¶
To add a new shape type:
- Implement shape → polygon conversion in
GBGeometryMath - Add test ensuring correct vertex count and bounds
- (If needed) Expose editor tooling to simplify generating the polygon
To adjust sensitivity:
- Modify global epsilon constant (currently 0.05) or pass explicit threshold for specialized rules (future extension)
Known Limitations & Future Improvements¶
| Area | Current State | Potential Enhancement |
|---|---|---|
| Concave polygons | Supported (processed as provided) but no auto-decomposition | Decompose to convex parts for potential faster intersection |
| Rotated tileset grids | Assumes axis-aligned square tiles | Add support for isometric / staggered grids |
| Large dynamic objects | Per-frame full remap | Add incremental dirty-region updates |
| Epsilon globality | Single threshold shared across rules | Per-rule or per-layer override |
Summary¶
Collision mapping converts all relevant 2D collision shapes into polygons, determines significantly overlapped tiles using a fractional area threshold, filters them by rule-relevant collision layers, and feeds this tile set into placement validation and visual feedback. The design balances accuracy (tight polygon outlines) with performance (bounds pruning + epsilon) to deliver responsive, reliable placement previews.