Initial working implementation of flow field.
Debug methods to visualize the flow field Player updates in game update on tile change Map does the actual looping and setting of values
This commit is contained in:
+110
-3
@@ -36,17 +36,21 @@ tile_defs: [Tile_Type]Tile_Def = {
|
||||
},
|
||||
}
|
||||
|
||||
Grid_Pos :: [2]int
|
||||
|
||||
Game_Map :: struct {
|
||||
tile_size: int,
|
||||
width: int,
|
||||
height: int,
|
||||
width_px: f32,
|
||||
height_px: f32,
|
||||
data: [400]int
|
||||
data: [400]int,
|
||||
// Flow Field props
|
||||
cost_field: [400]int,
|
||||
flow_field: [400]rl.Vector2,
|
||||
player_grid_pos: Grid_Pos
|
||||
}
|
||||
|
||||
Grid_Pos :: [2]int
|
||||
|
||||
map_init :: proc() -> Game_Map {
|
||||
// @TODO: store maps and load them later
|
||||
// NOTE: flat, row-major — data[row * width + col]
|
||||
@@ -213,3 +217,106 @@ draw_map :: proc(current_map: Game_Map) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset_cost_array :: proc(cost_arr: ^[400]int) {
|
||||
for val, idx in cost_arr {
|
||||
cost_arr[idx] = -1
|
||||
}
|
||||
}
|
||||
|
||||
compute_flow_field :: proc(game_map: ^Game_Map, player_pos: rl.Vector2) {
|
||||
reset_cost_array(&game_map.cost_field)
|
||||
player_grid_pos := world_to_grid(player_pos)
|
||||
player_idx := grid_to_index(player_grid_pos.x, player_grid_pos.y, game_map.width)
|
||||
game_map.cost_field[player_idx] = 0
|
||||
|
||||
flow_field_queue: [dynamic]Grid_Pos
|
||||
defer delete(flow_field_queue)
|
||||
append_elem(&flow_field_queue, player_grid_pos)
|
||||
|
||||
for len(flow_field_queue) != 0 {
|
||||
current := pop_front(&flow_field_queue)
|
||||
current_idx := grid_to_index(current.x, current.y, game_map.width)
|
||||
current_cost := game_map.cost_field[current_idx]
|
||||
|
||||
// Check all 8 neighbors (3x3 grid minus center):
|
||||
// (-1,-1) (0,-1) (1,-1)
|
||||
// (-1, 0) X (1, 0)
|
||||
// (-1, 1) (0, 1) (1, 1)
|
||||
for loop_y in -1..=1 {
|
||||
for loop_x in -1..=1 {
|
||||
if loop_x == 0 && loop_y == 0 do continue
|
||||
|
||||
nx := current.x + loop_x
|
||||
ny := current.y + loop_y
|
||||
get_neighbor_cost({nx, ny}, &flow_field_queue, game_map, current_cost)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_neighbor_cost :: proc(grid_pos: Grid_Pos, queue: ^[dynamic]Grid_Pos, game_map: ^Game_Map, current_cost: int) {
|
||||
if grid_pos.x >= 0 && grid_pos.x < game_map.width && grid_pos.y >= 0 && grid_pos.y < game_map.height {
|
||||
grid_pos_index := grid_to_index(grid_pos.x, grid_pos.y, game_map.width)
|
||||
|
||||
if game_map.cost_field[grid_pos_index] == -1 {
|
||||
tile_t := Tile_Type(game_map.data[grid_pos_index])
|
||||
def_t := tile_defs[tile_t]
|
||||
if !def_t.solid {
|
||||
game_map.cost_field[grid_pos_index] = current_cost + 1
|
||||
append_elem(queue, grid_pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compute_flow_directions :: proc(game_map: ^Game_Map) {
|
||||
// walk every tile, look at neighbor costs in cost_field,
|
||||
// write a direction vector into direction_field[index]
|
||||
for value, index in game_map.data {
|
||||
row := index / game_map.width
|
||||
column := index % game_map.width
|
||||
current_index := grid_to_index(column, row, game_map.width)
|
||||
|
||||
// skip unreachable tiles
|
||||
if game_map.cost_field[current_index] == -1 do continue
|
||||
current_cost := game_map.cost_field[current_index]
|
||||
lowest_cost := current_cost
|
||||
|
||||
// x = column, y = row — matches grid_to_world / screen space
|
||||
a := rl.Vector2{f32(column), f32(row)}
|
||||
b := a
|
||||
|
||||
// Check all 8 neighbors (3x3 grid minus center):
|
||||
// (-1,-1) (0,-1) (1,-1)
|
||||
// (-1, 0) X (1, 0)
|
||||
// (-1, 1) (0, 1) (1, 1)
|
||||
for loop_y in -1..=1 {
|
||||
for loop_x in -1..=1 {
|
||||
if loop_x == 0 && loop_y == 0 do continue
|
||||
|
||||
ncolumn := column + loop_x
|
||||
nrow := row + loop_y
|
||||
|
||||
if ncolumn >= 0 && ncolumn < game_map.width && nrow >= 0 && nrow < game_map.height {
|
||||
cost_index := grid_to_index(ncolumn, nrow, game_map.width)
|
||||
neighbor_cost := game_map.cost_field[cost_index]
|
||||
|
||||
if neighbor_cost != -1 && neighbor_cost < lowest_cost {
|
||||
lowest_cost = neighbor_cost
|
||||
b = rl.Vector2{f32(ncolumn), f32(nrow)}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// point from current tile toward the lower-cost neighbor
|
||||
delta := b - a
|
||||
if delta == {0, 0} {
|
||||
game_map.flow_field[current_index] = {0, 0}
|
||||
} else {
|
||||
game_map.flow_field[current_index] = rl.Vector2Normalize(delta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user