pathfinding with bfs
This commit is contained in:
@@ -13,3 +13,7 @@ INVALID_ENTITY :: Entity(0)
|
||||
|
||||
MOVE_SPEED :: 4.0
|
||||
|
||||
MAX_PATH :: 512
|
||||
|
||||
NEIGHBOR_OFFSET :: [4]Tile_Coord{{0, -1}, {0, 1}, {1, 0}, {-1, 0}}
|
||||
|
||||
|
||||
+40
-13
@@ -14,6 +14,8 @@ Entity_World :: struct {
|
||||
move_state: [MAX_ENTITIES]Move_State,
|
||||
move_target: [MAX_ENTITIES]Tile_Coord,
|
||||
visual_position: [MAX_ENTITIES]rl.Vector2,
|
||||
path: [MAX_ENTITIES]Path,
|
||||
path_index: [MAX_ENTITIES]int,
|
||||
}
|
||||
|
||||
Move_State :: enum {
|
||||
@@ -68,17 +70,29 @@ tile_center_world :: proc(t: Tile_Coord) -> rl.Vector2 {
|
||||
}
|
||||
}
|
||||
|
||||
entity_set_move_target :: proc(ew: ^Entity_World, e: Entity, target: Tile_Coord) -> bool {
|
||||
entity_set_move_target :: proc(
|
||||
ew: ^Entity_World,
|
||||
e: Entity,
|
||||
tm: ^Tilemap,
|
||||
target: Tile_Coord,
|
||||
) -> bool {
|
||||
i := entity_index(e)
|
||||
if i < 0 || !ew.active[i] {return false}
|
||||
|
||||
if ew.position[i] == target {
|
||||
ew.move_state[i] = .Idle
|
||||
return true
|
||||
if i < 0 || !ew.active[i] {
|
||||
return false
|
||||
}
|
||||
|
||||
start := ew.position[i]
|
||||
if !pathfind_bfs(tm, start, target, &ew.path[i]) {
|
||||
return false
|
||||
}
|
||||
ew.path_index[i] = 1
|
||||
if ew.path[i].count <= 1 {
|
||||
ew.move_state[i] = .Idle
|
||||
} else {
|
||||
ew.move_state[i] = .Moving
|
||||
ew.move_target[i] = target
|
||||
}
|
||||
|
||||
ew.move_target[i] = target
|
||||
ew.move_state[i] = .Moving
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -97,13 +111,15 @@ entity_update_movement :: proc(ew: ^Entity_World, dt: f32) {
|
||||
continue
|
||||
}
|
||||
|
||||
next := pos
|
||||
if pos.x != target.x {
|
||||
next.x += pos.x < target.x ? 1 : -1
|
||||
} else if pos.y != target.y {
|
||||
next.y += pos.y < target.y ? 1 : -1
|
||||
p := &ew.path[i]
|
||||
|
||||
if ew.path_index[i] >= p.count {
|
||||
ew.move_state[i] = .Idle
|
||||
continue
|
||||
}
|
||||
|
||||
next := p.tiles[ew.path_index[i]]
|
||||
|
||||
goal := tile_center_world(next)
|
||||
vp := ew.visual_position[i]
|
||||
|
||||
@@ -114,11 +130,22 @@ entity_update_movement :: proc(ew: ^Entity_World, dt: f32) {
|
||||
if dist < 1.0 {
|
||||
ew.position[i] = next
|
||||
ew.visual_position[i] = goal
|
||||
ew.path_index[i] += 1
|
||||
|
||||
if ew.path_index[i] >= p.count {
|
||||
ew.move_state[i] = .Idle
|
||||
}
|
||||
} else {
|
||||
step := speed * dt
|
||||
if step >= dist {
|
||||
ew.visual_position[i] = goal
|
||||
ew.position[i] = next
|
||||
ew.path_index[i] += 1
|
||||
|
||||
if ew.path_index[i] >= p.count {
|
||||
ew.move_state[i] = .Idle
|
||||
}
|
||||
|
||||
} else {
|
||||
ew.visual_position[i].x += dx / dist * step
|
||||
ew.visual_position[i].y += dy / dist * step
|
||||
|
||||
@@ -107,7 +107,7 @@ game_handle_input :: proc(g: ^Game) {
|
||||
wx, wy := screen_to_world(&g.camera, mx, my)
|
||||
tx, ty := world_to_tile(wx, wy)
|
||||
if tile_in_bounds(&g.tilemap, tx, ty) {
|
||||
entity_set_move_target(&g.world.entities, g.player_colonist, {tx, ty})
|
||||
entity_set_move_target(&g.world.entities, g.player_colonist, &g.tilemap, {tx, ty})
|
||||
g.selection.active = true
|
||||
g.selection.tile = {tx, ty}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
Path :: struct {
|
||||
tiles: []Tile_Coord,
|
||||
count: int,
|
||||
}
|
||||
|
||||
path_clear :: proc(path: ^Path) {
|
||||
delete(path.tiles)
|
||||
path.tiles = nil
|
||||
path.count = 0
|
||||
}
|
||||
|
||||
pathfind_bfs :: proc(tilemap: ^Tilemap, start, goal: Tile_Coord, path: ^Path) -> bool {
|
||||
path_clear(path)
|
||||
|
||||
if start == goal {
|
||||
path.tiles[0] = start
|
||||
path.count = 1
|
||||
return true
|
||||
}
|
||||
|
||||
w, h := tilemap.width, tilemap.height
|
||||
|
||||
parents: [MAP_HEIGHT][MAP_WIDTH]Tile_Coord
|
||||
visited: [MAP_HEIGHT][MAP_WIDTH]bool
|
||||
|
||||
found := false
|
||||
|
||||
for y in 0 ..< h {
|
||||
for x in 0 ..< w {
|
||||
parents[y][x] = {-1, -1}
|
||||
}
|
||||
}
|
||||
|
||||
queue: [MAP_WIDTH * MAP_HEIGHT]Tile_Coord
|
||||
head, tail: int = 0, 0
|
||||
|
||||
queue[tail] = start; tail += 1
|
||||
visited[start.y][start.x] = true
|
||||
parents[start.y][start.x] = start
|
||||
|
||||
for head < tail {
|
||||
current := queue[head]; head += 1
|
||||
|
||||
if current == goal {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
for offset in NEIGHBOR_OFFSET {
|
||||
nx := current.x + offset.x
|
||||
ny := current.y + offset.y
|
||||
if !tile_in_bounds(tilemap, nx, ny) ||
|
||||
visited[ny][nx] ||
|
||||
!tile_walkable(tilemap, nx, ny) {
|
||||
continue
|
||||
}
|
||||
|
||||
visited[ny][nx] = true
|
||||
parents[ny][nx] = current
|
||||
queue[tail] = {nx, ny}
|
||||
tail += 1
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
rev: [MAX_PATH]Tile_Coord
|
||||
rev_count := 0
|
||||
cur := goal
|
||||
for {
|
||||
rev[rev_count] = cur
|
||||
rev_count += 1
|
||||
if cur == start {
|
||||
break
|
||||
}
|
||||
cur = parents[cur.y][cur.x]
|
||||
if rev_count >= MAX_PATH {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
path.tiles = make([]Tile_Coord, rev_count)
|
||||
path.count = rev_count
|
||||
for i in 0 ..< rev_count {
|
||||
path.tiles[i] = rev[rev_count - 1 - i]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+16
-3
@@ -9,11 +9,16 @@ Tilemap :: struct {
|
||||
}
|
||||
|
||||
tilemap_init :: proc() -> Tilemap {
|
||||
return Tilemap {
|
||||
width = MAP_WIDTH,
|
||||
tm := Tilemap {
|
||||
width = MAP_WIDTH,
|
||||
height = MAP_HEIGHT,
|
||||
tiles = make([]u8, MAP_WIDTH * MAP_HEIGHT),
|
||||
tiles = make([]u8, MAP_WIDTH * MAP_HEIGHT),
|
||||
}
|
||||
// test wall
|
||||
for x in 15 ..< 25 {
|
||||
tm.tiles[tile_index(&tm, x, 12)] = 1
|
||||
}
|
||||
return tm
|
||||
}
|
||||
|
||||
tilemap_destroy :: proc(tilemap: ^Tilemap) {
|
||||
@@ -74,3 +79,11 @@ tilemap_draw :: proc(tilemap: ^Tilemap, camera: ^Camera2D) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tile_walkable :: proc(tilemap: ^Tilemap, x, y: int) -> bool {
|
||||
if !tile_in_bounds(tilemap, x, y) {return false}
|
||||
idx := tile_index(tilemap, x, y)
|
||||
|
||||
return tilemap.tiles[idx] != 1
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user