refactor: eh gonna rewrite it in rust

This commit is contained in:
dusk 2024-08-27 17:32:42 +03:00
parent b970959e65
commit 54a01c2ecc
Signed by: dusk
SSH Key Fingerprint: SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw
5 changed files with 153 additions and 14 deletions

View File

@ -10,12 +10,14 @@ var velocity := Vector2.ZERO
# this is assigned by the flock, if this boid is a child of it
var flock: Flock
var last_processed_in: int = 0
## applies some force to this boid.
func apply_force(spatial_force: Vector3) -> void:
var force := Vector2(spatial_force.x, spatial_force.y)
velocity += force
velocity = velocity.limit_length(properties.max_speed)
position += velocity * BoidManager.SIMULATION_RATE
position += velocity
func _get_boid_position() -> Vector3:
return Vector3(position.x, position.y, 0.0)

View File

@ -10,11 +10,13 @@ var velocity := Vector3.ZERO
# this is assigned by the flock, if this boid is a child of it
var flock: Flock
var last_processed_in: int = 0
## applies some force to this boid.
func apply_force(force: Vector3) -> void:
velocity += force
velocity = velocity.limit_length(properties.max_speed)
position += velocity * BoidManager.SIMULATION_RATE
position += velocity
func _get_boid_position() -> Vector3:
return position

View File

@ -4,8 +4,6 @@ extends Node
# this seems to help with 1000 boids in a single flock from 400ms to 180ms (before quadtrees)
const PARALLELIZATION_RATE: int = 50 # 50 seems to be the best value?
const EPSILON: float = 0.00001
# simulate per n physics frame ticks
var SIMULATION_RATE: int = 1
var flocks: Dictionary = {}
var total_boid_count: int = 0:
@ -17,6 +15,7 @@ var total_boid_count: int = 0:
# create our arrays for parallel processing
var args_array: Array[Dictionary] = []
var forces_array: PackedVector3Array = []
#var grids: Dictionary = {}
func _ready() -> void:
get_tree().node_added.connect(_register_flock)
@ -34,18 +33,20 @@ func _init_register_flock(node: Node = get_tree().root) -> void:
func _register_flock(maybe_flock: Node) -> void:
if maybe_flock is not Flock: return
flocks[maybe_flock.get_instance_id()] = maybe_flock
var flock_id := maybe_flock.get_instance_id()
flocks[flock_id] = maybe_flock
#grids[flock_id] = Grid.new()
print_verbose("[BoidManager] flock ", maybe_flock, " registered")
func _unregister_flock(maybe_flock: Node) -> void:
if maybe_flock is not Flock: return
flocks.erase(maybe_flock.get_instance_id())
var flock_id := maybe_flock.get_instance_id()
flocks.erase(flock_id)
#grids.erase(flock_id)
print_verbose("[BoidManager] flock ", maybe_flock, " unregistered")
func _physics_process(delta: float) -> void:
# run the simulation at a given rate
if Engine.get_physics_frames() % SIMULATION_RATE == 0:
_process_boids()
_process_boids()
func _process_boids() -> void:
var total_parallel_tasks := total_boid_count / PARALLELIZATION_RATE
@ -55,8 +56,10 @@ func _process_boids() -> void:
# organize the work into tasks
for flock: Flock in flocks.values():
var flock_args := _pack_calc_args_flock(flock)
for boid in flock.boids.values():
var args := _pack_calc_args_boid(boid, flock_args.duplicate())
var boids := flock.boids.values()
#grids.get(flock.get_instance_id()).build(Vector3.ONE * 1000.0, 30.0, boids)
for boid in boids:
var args := _pack_calc_args_boid(flock, boid, flock_args.duplicate())
args_array[boid_count] = args
forces_array[boid_count] = Vector3.ZERO
boid_count += 1
@ -97,7 +100,17 @@ func _pack_calc_args_flock(flock: Flock) -> Dictionary:
flock_args['target_position'] = flock.target.global_position
return flock_args
func _pack_calc_args_boid(boid, args: Dictionary) -> Dictionary:
func _pack_calc_args_boid(flock: Flock, boid, args: Dictionary) -> Dictionary:
#var nearby_boids: Array[Node] = grids.get(flock.get_instance_id()).get_nearby_boids(boid)
#var others_pos := PackedVector3Array([]); others_pos.resize(nearby_boids.size())
#var others_vel := PackedVector3Array([]); others_vel.resize(nearby_boids.size())
#var idx := 0
#for aboid in nearby_boids:
#others_pos.set(idx, aboid._get_boid_position())
#others_vel.set(idx, aboid._get_boid_velocity())
#idx += 1
#args['others_pos'] = others_pos
#args['others_vel'] = others_vel
args['boid'] = boid
args['self_props'] = boid.properties
args['self_vel'] = boid._get_boid_velocity()
@ -109,7 +122,7 @@ func _calculate_boid_parallel(idx: int) -> void:
var end_at := mini(start_from + PARALLELIZATION_RATE, total_boid_count)
var arg_idx := start_from
while arg_idx < end_at:
var force = _calculate_boid(args_array[arg_idx])
var force := _calculate_boid(args_array[arg_idx])
forces_array[arg_idx] = force
arg_idx += 1

122
addons/boids/grid.gd Normal file
View File

@ -0,0 +1,122 @@
extends RefCounted
class_name Grid
var _cells: Dictionary
var _scale: float
var size: Vector3
var scaled_points: Dictionary
func build(unscaled_size: Vector3, scale: float, boids: Array):
_scale = scale
size = Vector3(_scale_axis(unscaled_size.x), _scale_axis(unscaled_size.y), _scale_axis(unscaled_size.z))
_cells.clear()
scaled_points.clear()
var idx := 0
for boid in boids:
var scaled_point := _scale_point(boid._get_boid_position())
_add_body(boid, scaled_point)
scaled_points[boid.get_instance_id()] = scaled_point
idx += 1
func _scale_axis(point: float) -> float:
return floorf(point / _scale)
func _scale_point(vector: Vector3) -> Vector3:
var scaled_point = (vector / _scale).floor()
scaled_point.x = minf(maxf(scaled_point.x, 0), size.x)
scaled_point.y = minf(maxf(scaled_point.y, 0), size.y)
scaled_point.z = minf(maxf(scaled_point.z, 0), size.z)
return scaled_point
func _add_body(body: Node, scaled_point: Vector3) -> void:
var boids := _cells.get(scaled_point, [])
boids.append(body)
_cells[scaled_point] = boids
func _get_cell(x: float, y: float, z: float, write_to: Array[Node]) -> void:
write_to.append_array(_cells.get(Vector3(x, y, z), []))
func get_nearby_boids(boid: Node) -> Array[Node]:
var scaled_point: Vector3 = scaled_points[boid.get_instance_id()]
# keep the points in bounds
var x := minf(maxf(scaled_point.x, 0), size.x)
var y := minf(maxf(scaled_point.y, 0), size.y)
var z := minf(maxf(scaled_point.z, 0), size.z)
var results: Array[Node] = []
var gb := func(x, y, z): _get_cell(x, y, z, results)
gb.call(x, y, z)
var up := y - 1
var down := y + 1
var left := x - 1
var right := x + 1
var forwards := z - 1
var backwards := z + 1
# up
if up > 0:
gb.call(x, up, z)
if left > 0:
gb.call(left, up, z)
if right <= size.x:
gb.call(right, up, z)
if forwards > 0:
gb.call(x, up, forwards)
if left > 0:
gb.call(left, up, forwards)
if right <= size.x:
gb.call(right, up, forwards)
if backwards <= size.z:
gb.call(x, up, backwards)
if left > 0:
gb.call(left, up, backwards)
if right <= size.x:
gb.call(right, up, backwards)
# down
if down <= size.y:
gb.call(x, down, z)
if left > 0:
gb.call(left, down, z)
if right <= size.x:
gb.call(right, down, z)
if forwards > 0:
gb.call(x, down, forwards)
if left > 0:
gb.call(left, down, forwards)
if right <= size.x:
gb.call(right, down, forwards)
if backwards <= size.z:
gb.call(x, down, backwards)
if left > 0:
gb.call(left, down, backwards)
if right <= size.x:
gb.call(right, down, backwards)
# forwards
if forwards > 0:
gb.call(x, y, forwards)
if left > 0:
gb.call(left, y, forwards)
if right <= size.x:
gb.call(right, y, forwards)
if backwards <= size.z:
gb.call(x, y, backwards)
if left > 0:
gb.call(left, y, backwards)
if right <= size.x:
gb.call(right, y, backwards)
# left and right
if left > 0:
gb.call(left, y, z)
if right <= size.x:
gb.call(right, y, z)
return results

View File

@ -2,7 +2,7 @@ extends Node2D
func _ready() -> void:
for flock in get_children():
for i in 1000: spawnBoid(flock)
for i in 100: spawnBoid(flock)
func spawnBoid(flock: Flock) -> void:
var boid: Boid2D = preload("../example_boid.tscn").instantiate()