diff --git a/addons/boids/boids.gd b/addons/boids/boids.gd index 60e5e6c..318659a 100644 --- a/addons/boids/boids.gd +++ b/addons/boids/boids.gd @@ -3,8 +3,8 @@ extends EditorPlugin func _enter_tree() -> void: - add_autoload_singleton("BoidsProcess_2D", "res://addons/boids/boids_process_2d.tscn") + add_autoload_singleton("ProcessBoids", "res://addons/boids/process_boids.tscn") func _exit_tree() -> void: - remove_autoload_singleton("BoidsProcess_2D") + remove_autoload_singleton("ProcessBoids") diff --git a/addons/boids/process_boids.tscn b/addons/boids/process_boids.tscn index d123314..a552b71 100644 --- a/addons/boids/process_boids.tscn +++ b/addons/boids/process_boids.tscn @@ -1,4 +1,3 @@ [gd_scene format=3 uid="uid://c84c62urmhxbm"] [node name="BoidsProcess" type="BoidsProcess"] -process_2d = true diff --git a/examples/boids/2d/simple/example.gd b/examples/boids/2d/simple/example.gd index 3f06d49..b17a5d4 100644 --- a/examples/boids/2d/simple/example.gd +++ b/examples/boids/2d/simple/example.gd @@ -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: Flock2D) -> void: var boid: Boid2D = preload("../example_boid.tscn").instantiate() diff --git a/rust/src/boid/boid_2d.rs b/rust/src/boid/boid_2d.rs index 0bd75ae..a4bf13a 100644 --- a/rust/src/boid/boid_2d.rs +++ b/rust/src/boid/boid_2d.rs @@ -7,6 +7,8 @@ use crate::{BoidProperties, Flock2D}; #[class(init, base=Node2D)] pub struct Boid2D { #[export] + /// The properties of this boid. + /// Note: this cannot be changed in runtime, aside from removing and readding the node. properties: Gd, props: BoidProperties, vel: Vec2, @@ -18,18 +20,21 @@ pub struct Boid2D { impl Boid2D { #[func] #[inline(always)] + /// Get the current velocity of this boid. fn get_velocity(&self) -> Vector2 { Vector2::new(self.vel.x, self.vel.y) } #[func] #[inline(always)] + /// Get the ID of this boid. pub fn get_id(&self) -> i64 { self.base().instance_id().to_i64() } #[func] #[inline(always)] + /// Get the flock ID of this boid. pub fn get_flock_id(&self) -> i64 { self.flock_id } diff --git a/rust/src/boid_properties.rs b/rust/src/boid_properties.rs index e91728f..7d77f6f 100644 --- a/rust/src/boid_properties.rs +++ b/rust/src/boid_properties.rs @@ -5,20 +5,26 @@ use godot::prelude::*; pub struct BoidProperties { #[export] #[init(val = 4.0)] + /// Max speed of this boid. pub max_speed: f32, #[export] #[init(val = 1.0)] + /// Max force that will be applied to this boid at once. pub max_force: f32, #[export] #[init(val = 1.5)] + /// How much to align with other boids. pub alignment: f32, #[export] #[init(val = 1.0)] + /// How much to cohere to other boids. pub cohesion: f32, #[export] #[init(val = 1.2)] + /// How much to seperate from other boids. pub seperation: f32, #[export] #[init(val = 0.8)] + /// How much to follow a flock target (if there is one). pub targeting: f32, } \ No newline at end of file diff --git a/rust/src/flock/flock_2d.rs b/rust/src/flock/flock_2d.rs index 2e5a002..72f132b 100644 --- a/rust/src/flock/flock_2d.rs +++ b/rust/src/flock/flock_2d.rs @@ -9,9 +9,12 @@ use super::Flock; #[class(init, base=Node2D)] pub struct Flock2D { #[export] + /// Properties of this flock. + /// Note: this cannot be changed in runtime, aside from removing and readding the node. properties: Gd, props: FlockProperties, #[export] + /// A target node for the flock to follow. target: Option>, pub boids: FxIndexMap>, base: Base, diff --git a/rust/src/flock_properties.rs b/rust/src/flock_properties.rs index 001d084..8fdce9b 100644 --- a/rust/src/flock_properties.rs +++ b/rust/src/flock_properties.rs @@ -5,14 +5,14 @@ use godot::prelude::*; pub struct FlockProperties { #[export] #[init(val = 625.0)] - /// squared + /// Distance (squared) to apply seperation force between boids in a flock. pub goal_seperation: f32, #[export] #[init(val = 2500.0)] - /// squared + /// Distance (squared) to apply alignment force between boids in a flock. pub goal_alignment: f32, #[export] #[init(val = 2500.0)] - /// squared + /// Distance (squared) to apply cohesion force between boids in a flock. pub goal_cohesion: f32, } \ No newline at end of file diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 2edc8d1..7e9a5cc 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -73,13 +73,20 @@ unsafe impl ExtensionLibrary for BoidsExtension { #[derive(GodotClass)] #[class(init, base=Node)] +/// Node that will make calls automatically to process 2D/3D boids, providing some configuration options. +/// It's best to use this as an autoload singleton. pub struct BoidsProcess { #[export] + #[init(val = true)] + /// Whether to process 2D boids or not. process_2d: bool, #[export] + #[init(val = true)] + /// Whether to process 3D boids or not. process_3d: bool, #[export] #[init(val = 1)] + /// Process boids per N physics ticks. process_per_tick: i64, boids: Option>, engine: Option>, @@ -117,6 +124,7 @@ impl INode for BoidsProcess { #[derive(GodotClass)] #[class(init, base=Object)] +/// Singleton that holds all boids and flocks and manages them. struct Boids { flocks2d: FxIndexMap>, boids2d: FxIndexMap>, @@ -150,15 +158,25 @@ impl Boids { impl Boids { #[func] #[inline(always)] + /// Process all 2D boids once. + /// NOTE: This function is not intended to be manually called. Prefer using `BoidsProcess` as an autoload singleton where possible. fn process_boids_2d(&mut self) { process_boids(&mut self.boids2d, &self.flocks2d) } #[func] #[inline(always)] - fn get_total_boid_count(&self) -> i64 { + /// Gets the total 2D boid count. + fn get_total_boid_2d_count(&self) -> i64 { self.boids2d.len() as i64 } + + #[func] + #[inline(always)] + /// Gets the total 2D flock count. + fn get_total_flock_2d_count(&self) -> i64 { + self.flocks2d.len() as i64 + } } #[inline(always)] @@ -202,8 +220,8 @@ where } #[cfg(feature = "stats")] godot_print!( - "[Boids] preparing all calculations took {} ms", - time.elapsed().as_millis() + "[Boids] preparing all calculations took {} micros", + time.elapsed().as_micros() ); #[cfg(feature = "stats")] @@ -211,7 +229,7 @@ where let forces: Vec<(i64, Vec3)> = calc_funcs .into_par_iter() .fold( - || Vec::<(i64, Vec3)>::with_capacity(total_boid_count / 10), + || Vec::<(i64, Vec3)>::with_capacity(total_boid_count), |mut acc, (boid_id, calc_fn)| { let force = calc_fn(); acc.push((boid_id, force)); @@ -219,7 +237,7 @@ where }, ) .reduce( - || Vec::<(i64, Vec3)>::with_capacity(total_boid_count / 10), + || Vec::<(i64, Vec3)>::with_capacity(total_boid_count), |mut left, mut right| { left.append(&mut right); left @@ -227,19 +245,19 @@ where ); #[cfg(feature = "stats")] godot_print!( - "[Boids] calculating all boids took {} ms", - time.elapsed().as_millis() + "[Boids] calculating all boids took {} micros", + time.elapsed().as_micros() ); #[cfg(feature = "stats")] let time = std::time::Instant::now(); for (boid_id, force) in forces { - let boid = boids.get_mut(&boid_id).unwrap(); + let boid = unsafe { boids.get_mut(&boid_id).unwrap_unchecked() }; boid.bind_mut().apply_force(force); } #[cfg(feature = "stats")] godot_print!( - "[Boids] applying forces took {} ms", - time.elapsed().as_millis() + "[Boids] applying forces took {} micros", + time.elapsed().as_micros() ); }