the terrain cost separation is something i struggled with too. what worked for me was keeping a flat array of cost multipliers indexed by tile coordinate and just passing it to A* as a heuristic modifier. rebuilding the graph every time a tile changes was killing my frame time, but swapping a value in an array is basically free. the trick is making sure your pathfinding query reads the cost array at query time instead of caching it during graph construction.
tile-based pathfinding with dynamic terrain costs — A* isn't enough on its own
been banging my head against this for the past few weeks and finally have something worth sharing. if your game has terrain that changes cost at runtime — mud that slows enemies, fire that damages them but they'll still walk through it, roads that speed them up — vanilla A* with static weights breaks down fast and the fixes are not obvious.
the core issue: A* with a simple heuristic assumes your cost map is stable. the moment you start mutating costs at runtime (burning fields, flooding tiles, player-placed obstacles), you either re-run the full pathfind every frame which is brutal, or you cache paths that go stale and cause agents to clip through hazards they should be avoiding.
what actually helped me:
first, separate structural impassability (walls, chasms) from cost modifiers (terrain type, status effects). the former changes rarely, the latter changes constantly. cache the navigation mesh based on structural data, then apply cost modifiers only during the pathfind evaluation step. this means you only invalidate the full cache when geometry changes, not when someone drops a fire trap.
second, don't store absolute costs per tile — store a cost multiplier and a list of modifier sources. when a modifier is removed (fire burns out, effect expires), you recalculate that tile's effective cost from the remaining sources rather than trying to undo an accumulated value. sounds obvious but i spent three days debugging a cost-underflow bug caused by subtraction order.
rough code sketch in pseudocode:
class TileCostMap:
base_costs: Dict[TilePos, float] # terrain type base
modifiers: Dict[TilePos, List[Modifier]] # stacked sources
def effective_cost(pos):
base = base_costs.get(pos, 1.0)
mult = 1.0
for mod in modifiers.get(pos, []):
mult *= mod.cost_multiplier
return base * mult
def add_modifier(pos, mod):
modifiers[pos].append(mod)
def remove_modifier(pos, mod):
modifiers[pos].remove(mod)
# effective_cost recalculated on next query, no manual undo neededthird — and this took me longest to accept — sometimes you don't want agents to find the optimal path. dumber enemies should take a plausible path, not a perfect one. running full Dijkstra on every cost change is correct but slow. for grunt-type enemies i now run A* on a coarser grid and only refine to tile resolution when they get within a certain range of the target. massive perf win, barely noticeable to the player.
happy to dig into the coarse/fine grid handoff if anyone's doing something similar. also curious whether anyone's tried flow fields for this use case — i've read the theory but haven't implemented one yet.
what's the specific problem you're hitting with A*? Jump Point Search is almost always the answer for uniform-cost grids if A* is too slow, but the implementation details matter a lot depending on whether your grid allows diagonal movement and whether tile weights are uniform or variable.
Late to this one but are you using a grid or a navmesh under the hood? Tile-based pathfinding with A* on a pure grid can get expensive fast if your map is large. Hierarchical pathfinding (HPA*) is worth looking into if that's the bottleneck, breaks the grid into clusters and finds paths between clusters first before drilling down.
For tile-based pathfinding have you looked at Jump Point Search? Huge improvement over standard A* on uniform grids, cuts down the nodes you need to evaluate dramatically. Works best when most of your tiles share the same traversal cost but even with weighted tiles it's a solid baseline. There's a good breakdown of it on the GameAIPro site if you haven't seen it.