Skip to content

Commit 84ce9f9

Browse files
committed
Flow Field Agents Move Successfully to Target
1 parent ed899de commit 84ce9f9

File tree

5 files changed

+86
-26
lines changed

5 files changed

+86
-26
lines changed

demo/controller/agent/AgentController_FlowField.gd

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,14 @@ func _cancel_navigation() -> void:
6969

7070
func _move_agent(safe_velocity : Vector3):
7171
velocity = safe_velocity
72-
73-
var collision : KinematicCollision3D = move_and_collide(safe_velocity * get_physics_process_delta_time())
74-
if collision:
75-
var collider : Object = collision.get_collider()
76-
if collider is CharacterBody3D:
77-
velocity = safe_velocity.slide(collision.get_normal())
78-
elif collider is StaticBody3D:
79-
move_and_slide()
72+
move_and_slide()
73+
#var collision : KinematicCollision3D = move_and_collide(safe_velocity * get_physics_process_delta_time())
74+
#if collision:
75+
#var collider : Object = collision.get_collider()
76+
#if collider is CharacterBody3D:
77+
#velocity = safe_velocity.slide(collision.get_normal())
78+
#elif collider is StaticBody3D:
79+
8080

8181
func update_target_loc(target_loc):
8282
moveToLocation = target_loc + Vector3(randf_range(-1.5,1.5), 0, randf_range(-1.5,1.5))
@@ -91,10 +91,10 @@ func moveTo() -> int:
9191
if(global_position != moveToLocation):
9292
var new_direction : Vector3 = moveVec
9393
# Ensure Units Rotate correctly
94-
_rotate_agent(new_direction)
95-
var steering_behavior_velocity : Vector3 = (new_direction - velocity) * agentDelta * SPEED
96-
var new_velocity : Vector3 = velocity + steering_behavior_velocity
97-
_move_agent(new_velocity);
94+
#_rotate_agent(new_direction)
95+
#var steering_behavior_velocity : Vector3 = (new_direction - velocity) * agentDelta * SPEED
96+
#var new_velocity : Vector3 = velocity + steering_behavior_velocity
97+
_move_agent(moveVec * SPEED);
9898

9999
return TaskStatus.Continue;
100100
elif(global_position == moveToLocation):

demo/scenes/Level/flow_field.tscn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,5 @@ surface_material_override/0 = SubResource("StandardMaterial3D_brjmj")
136136

137137
[node name="CollisionShape3D" type="CollisionShape3D" parent="Obstacles/MeshInstance3D4/StaticBody3D"]
138138
shape = SubResource("ConcavePolygonShape3D_ukqwy")
139+
140+
[node name="GridCoordsLabels" type="Node3D" parent="."]

demo/scripts/Levels/LevelScript_Flow_Field.gd

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ const agent = preload("res://controller/agent/AgentController_FlowField.tscn")
55
signal spawn_player(playerID : int)
66

77
var grid_origin: Vector3 = Vector3(-100.0, 0.0, -100.0); # Example for a 200×200 area centered at (0,0,0)
8-
var cell_size: float = 1.0;
8+
var cell_size: float = 2.0;
99

1010
var myFlowField: FlowField;
1111

12+
var grid_size = 100 # Grid cells
13+
var label_spacing = 20 # Display labels every 20 cells
14+
15+
1216
func _ready():
1317
GlobalSettings.apply_graphics_settings(get_window(), $WorldEnvironment.environment, self)
1418
if multiplayer.is_server():
@@ -36,7 +40,7 @@ func _ready():
3640
for y in range(min_cell_y, max_cell_y + 1):
3741
all_occupied_cells.append(Vector2(x, y))
3842

39-
myFlowField.compute_flow(200, 200, 0, 0, all_occupied_cells);
43+
myFlowField.compute_flow(grid_size, grid_size, 0, 0, all_occupied_cells);
4044

4145
multiplayer.peer_connected.connect(_spawn_player)
4246
var peers = multiplayer.get_peers()

src/Flow/Flow_Field.cpp

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void FlowField::compute_flow(int set_width, int set_height, int arrivalX, int ar
2323
fmm::UniformSpeedEikonalSolver<float, 2>(grid_spacing, uniform_speed));
2424

2525
// Apply Obstacles
26+
//TODO Make a separte function that gets applied, for static obstacles and use this for dynamic obstacles
2627
vector<bool> is_obstacle(width * height, false);
2728
for (int i = 0; i < obstacles.size(); ++i) {
2829
Vector2i pos = obstacles[i];
@@ -33,13 +34,37 @@ void FlowField::compute_flow(int set_width, int set_height, int arrivalX, int ar
3334
flow_field.resize(width * height);
3435

3536
// Compute flow field for each cell
36-
for (int x = 1; x < width - 1; ++x) {
37-
for (int y = 1; y < height - 1; ++y) {
37+
for (int x = 0; x < width; ++x) {
38+
for (int y = 0; y < height; ++y) {
3839
int idx = x + y * width;
3940

4041
if (is_obstacle[idx]) {
41-
flow_field[idx] = { 0.0f, 0.0f };
42-
continue;
42+
float min_time = 1e6f;
43+
array<float, 2> best_vector = { 0.0f, 0.0f };
44+
45+
// Check the four neighbors for the smallest arrival time
46+
int neighbors[4] = { idx - 1, idx + 1, idx - width, idx + width };
47+
48+
for (int n = 0; n < 4; n++) {
49+
if (neighbors[n] < 0 || neighbors[n] >= width * height || is_obstacle[neighbors[n]])
50+
continue;
51+
52+
if (arrival_times[neighbors[n]] < min_time) {
53+
min_time = arrival_times[neighbors[n]];
54+
55+
// Compute direction to the lowest-time neighbor
56+
best_vector[0] = (neighbors[n] % width) - x;
57+
best_vector[1] = ((neighbors[n] / width) - y);
58+
}
59+
}
60+
61+
// Normalize the vector if a valid neighbor was found
62+
float mag = std::sqrt(best_vector[0] * best_vector[0] + best_vector[1] * best_vector[1]);
63+
if (mag > 0) {
64+
flow_field[idx] = { best_vector[0] / mag, best_vector[1] / mag };
65+
} else {
66+
flow_field[idx] = { 0.0f, 0.0f }; // No valid direction found
67+
}
4368
}
4469

4570
// Compute Vector gradient
@@ -55,11 +80,11 @@ void FlowField::compute_flow(int set_width, int set_height, int arrivalX, int ar
5580
}
5681
}
5782

58-
String godot_string = String(vectorToString(flow_field).c_str());
59-
UtilityFunctions::print("Flow Field Vector: ", godot_string);
83+
// String godot_string = String(vectorToString().c_str());
84+
// UtilityFunctions::print("Flow Field Vector: ", godot_string);
6085
}
6186

62-
std::string FlowField::vectorToString(const std::vector<std::array<float, 2>> &flow_field) {
87+
std::string FlowField::vectorToString() {
6388
std::ostringstream oss;
6489
oss << "["; // Start with an opening bracket
6590

@@ -79,19 +104,45 @@ Vector3 FlowField::get_move_direction(const Vector3 &world_position, float grid_
79104
int gridY = static_cast<int>(world_position.z / grid_cell_size);
80105

81106
int index = gridX + gridY * width;
82-
if (index < 0 || index >= static_cast<int>(flow_field.size())) {
83-
UtilityFunctions::push_error("AI Blue Error : Flow Field | Final Flow Field Vector is empty");
107+
if (index < 0 || index > static_cast<int>(flow_field.size())) {
108+
// UtilityFunctions::push_error("AI Blue Error : Flow Field | Final Flow Field Vector is empty");
84109
return Vector3();
85110
}
86111

87-
const auto &direction_vector = flow_field[index];
112+
// for (size_t i = 0; i < flow_field.size(); ++i) {
113+
// UtilityFunctions::print("Index: ", i, " Value: ", flow_field[i][0], ", ", flow_field[i][1]);
114+
// }
115+
116+
// UtilityFunctions::print("World Position: ", world_position);
117+
// UtilityFunctions::print("Index: ", index);
118+
// UtilityFunctions::print("Flow Field Max Size: ", flow_field.size());
119+
120+
// Ref<FileAccess>
121+
// file = FileAccess::open("user://debug_output.txt", FileAccess::WRITE_READ);
122+
// if (file.is_valid()) {
123+
// file->seek_end(); // Move to end before writing
124+
125+
// // Convert flow_field to a readable string
126+
// String flow_data;
127+
// for (const auto &vector : flow_field) {
128+
// flow_data += String("(") + String::num(vector[0]) + ", " + String::num(vector[1]) + ")\n";
129+
// }
88130

89-
Vector3 move_direction(direction_vector[0], 0.0f, direction_vector[1]);
131+
// file->store_string(flow_data); // Write properly formatted data
132+
// file->close();
133+
// }
134+
135+
// String godot_string = String(const_cast<FlowField *>(this)->vectorToString().c_str());
136+
// UtilityFunctions::print("Flow Field Vector: ", godot_string);
137+
138+
Vector3 move_direction(this->flow_field[index][0], 0.0f, this->flow_field[index][1]);
90139

91140
if (move_direction.length() > 0.0f) {
92141
move_direction = move_direction.normalized();
93142
}
94143

144+
// UtilityFunctions::print("Final Output: ", move_direction);
145+
95146
return move_direction;
96147
}
97148

src/Flow/Flow_Field.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#ifndef FLOWFIELD_H
22
#define FLOWFIELD_H
33

4+
#include <godot_cpp/classes/file_access.hpp>
45
#include <godot_cpp/classes/node.hpp>
6+
#include <godot_cpp/classes/os.hpp>
7+
58
#include <godot_cpp/variant/utility_functions.hpp>
69

710
#include "fast_marching_method.hpp"
@@ -21,7 +24,7 @@ class FlowField : public Node {
2124
int height;
2225
vector<array<float, 2>> flow_field;
2326

24-
string vectorToString(const std::vector<std::array<float, 2>> &flow_field);
27+
string vectorToString();
2528

2629
public:
2730
FlowField();

0 commit comments

Comments
 (0)