feat: add some docs
This commit is contained in:
parent
7da23e19b3
commit
2d11c9c9ec
@ -3,8 +3,8 @@ extends EditorPlugin
|
|||||||
|
|
||||||
|
|
||||||
func _enter_tree() -> void:
|
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:
|
func _exit_tree() -> void:
|
||||||
remove_autoload_singleton("BoidsProcess_2D")
|
remove_autoload_singleton("ProcessBoids")
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
[gd_scene format=3 uid="uid://c84c62urmhxbm"]
|
[gd_scene format=3 uid="uid://c84c62urmhxbm"]
|
||||||
|
|
||||||
[node name="BoidsProcess" type="BoidsProcess"]
|
[node name="BoidsProcess" type="BoidsProcess"]
|
||||||
process_2d = true
|
|
||||||
|
@ -2,7 +2,7 @@ extends Node2D
|
|||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
for flock in get_children():
|
for flock in get_children():
|
||||||
for i in 1000: spawnBoid(flock)
|
for i in 100: spawnBoid(flock)
|
||||||
|
|
||||||
func spawnBoid(flock: Flock2D) -> void:
|
func spawnBoid(flock: Flock2D) -> void:
|
||||||
var boid: Boid2D = preload("../example_boid.tscn").instantiate()
|
var boid: Boid2D = preload("../example_boid.tscn").instantiate()
|
||||||
|
@ -7,6 +7,8 @@ use crate::{BoidProperties, Flock2D};
|
|||||||
#[class(init, base=Node2D)]
|
#[class(init, base=Node2D)]
|
||||||
pub struct Boid2D {
|
pub struct Boid2D {
|
||||||
#[export]
|
#[export]
|
||||||
|
/// The properties of this boid.
|
||||||
|
/// Note: this cannot be changed in runtime, aside from removing and readding the node.
|
||||||
properties: Gd<BoidProperties>,
|
properties: Gd<BoidProperties>,
|
||||||
props: BoidProperties,
|
props: BoidProperties,
|
||||||
vel: Vec2,
|
vel: Vec2,
|
||||||
@ -18,18 +20,21 @@ pub struct Boid2D {
|
|||||||
impl Boid2D {
|
impl Boid2D {
|
||||||
#[func]
|
#[func]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
/// Get the current velocity of this boid.
|
||||||
fn get_velocity(&self) -> Vector2 {
|
fn get_velocity(&self) -> Vector2 {
|
||||||
Vector2::new(self.vel.x, self.vel.y)
|
Vector2::new(self.vel.x, self.vel.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[func]
|
#[func]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
/// Get the ID of this boid.
|
||||||
pub fn get_id(&self) -> i64 {
|
pub fn get_id(&self) -> i64 {
|
||||||
self.base().instance_id().to_i64()
|
self.base().instance_id().to_i64()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[func]
|
#[func]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
/// Get the flock ID of this boid.
|
||||||
pub fn get_flock_id(&self) -> i64 {
|
pub fn get_flock_id(&self) -> i64 {
|
||||||
self.flock_id
|
self.flock_id
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,26 @@ use godot::prelude::*;
|
|||||||
pub struct BoidProperties {
|
pub struct BoidProperties {
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 4.0)]
|
#[init(val = 4.0)]
|
||||||
|
/// Max speed of this boid.
|
||||||
pub max_speed: f32,
|
pub max_speed: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 1.0)]
|
#[init(val = 1.0)]
|
||||||
|
/// Max force that will be applied to this boid at once.
|
||||||
pub max_force: f32,
|
pub max_force: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 1.5)]
|
#[init(val = 1.5)]
|
||||||
|
/// How much to align with other boids.
|
||||||
pub alignment: f32,
|
pub alignment: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 1.0)]
|
#[init(val = 1.0)]
|
||||||
|
/// How much to cohere to other boids.
|
||||||
pub cohesion: f32,
|
pub cohesion: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 1.2)]
|
#[init(val = 1.2)]
|
||||||
|
/// How much to seperate from other boids.
|
||||||
pub seperation: f32,
|
pub seperation: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 0.8)]
|
#[init(val = 0.8)]
|
||||||
|
/// How much to follow a flock target (if there is one).
|
||||||
pub targeting: f32,
|
pub targeting: f32,
|
||||||
}
|
}
|
@ -9,9 +9,12 @@ use super::Flock;
|
|||||||
#[class(init, base=Node2D)]
|
#[class(init, base=Node2D)]
|
||||||
pub struct Flock2D {
|
pub struct Flock2D {
|
||||||
#[export]
|
#[export]
|
||||||
|
/// Properties of this flock.
|
||||||
|
/// Note: this cannot be changed in runtime, aside from removing and readding the node.
|
||||||
properties: Gd<FlockProperties>,
|
properties: Gd<FlockProperties>,
|
||||||
props: FlockProperties,
|
props: FlockProperties,
|
||||||
#[export]
|
#[export]
|
||||||
|
/// A target node for the flock to follow.
|
||||||
target: Option<Gd<Node2D>>,
|
target: Option<Gd<Node2D>>,
|
||||||
pub boids: FxIndexMap<i64, Gd<Boid2D>>,
|
pub boids: FxIndexMap<i64, Gd<Boid2D>>,
|
||||||
base: Base<Node2D>,
|
base: Base<Node2D>,
|
||||||
|
@ -5,14 +5,14 @@ use godot::prelude::*;
|
|||||||
pub struct FlockProperties {
|
pub struct FlockProperties {
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 625.0)]
|
#[init(val = 625.0)]
|
||||||
/// squared
|
/// Distance (squared) to apply seperation force between boids in a flock.
|
||||||
pub goal_seperation: f32,
|
pub goal_seperation: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 2500.0)]
|
#[init(val = 2500.0)]
|
||||||
/// squared
|
/// Distance (squared) to apply alignment force between boids in a flock.
|
||||||
pub goal_alignment: f32,
|
pub goal_alignment: f32,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 2500.0)]
|
#[init(val = 2500.0)]
|
||||||
/// squared
|
/// Distance (squared) to apply cohesion force between boids in a flock.
|
||||||
pub goal_cohesion: f32,
|
pub goal_cohesion: f32,
|
||||||
}
|
}
|
@ -73,13 +73,20 @@ unsafe impl ExtensionLibrary for BoidsExtension {
|
|||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(init, base=Node)]
|
#[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 {
|
pub struct BoidsProcess {
|
||||||
#[export]
|
#[export]
|
||||||
|
#[init(val = true)]
|
||||||
|
/// Whether to process 2D boids or not.
|
||||||
process_2d: bool,
|
process_2d: bool,
|
||||||
#[export]
|
#[export]
|
||||||
|
#[init(val = true)]
|
||||||
|
/// Whether to process 3D boids or not.
|
||||||
process_3d: bool,
|
process_3d: bool,
|
||||||
#[export]
|
#[export]
|
||||||
#[init(val = 1)]
|
#[init(val = 1)]
|
||||||
|
/// Process boids per N physics ticks.
|
||||||
process_per_tick: i64,
|
process_per_tick: i64,
|
||||||
boids: Option<Gd<Boids>>,
|
boids: Option<Gd<Boids>>,
|
||||||
engine: Option<Gd<Engine>>,
|
engine: Option<Gd<Engine>>,
|
||||||
@ -117,6 +124,7 @@ impl INode for BoidsProcess {
|
|||||||
|
|
||||||
#[derive(GodotClass)]
|
#[derive(GodotClass)]
|
||||||
#[class(init, base=Object)]
|
#[class(init, base=Object)]
|
||||||
|
/// Singleton that holds all boids and flocks and manages them.
|
||||||
struct Boids {
|
struct Boids {
|
||||||
flocks2d: FxIndexMap<i64, Gd<Flock2D>>,
|
flocks2d: FxIndexMap<i64, Gd<Flock2D>>,
|
||||||
boids2d: FxIndexMap<i64, Gd<Boid2D>>,
|
boids2d: FxIndexMap<i64, Gd<Boid2D>>,
|
||||||
@ -150,15 +158,25 @@ impl Boids {
|
|||||||
impl Boids {
|
impl Boids {
|
||||||
#[func]
|
#[func]
|
||||||
#[inline(always)]
|
#[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) {
|
fn process_boids_2d(&mut self) {
|
||||||
process_boids(&mut self.boids2d, &self.flocks2d)
|
process_boids(&mut self.boids2d, &self.flocks2d)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[func]
|
#[func]
|
||||||
#[inline(always)]
|
#[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
|
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)]
|
#[inline(always)]
|
||||||
@ -202,8 +220,8 @@ where
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
godot_print!(
|
godot_print!(
|
||||||
"[Boids] preparing all calculations took {} ms",
|
"[Boids] preparing all calculations took {} micros",
|
||||||
time.elapsed().as_millis()
|
time.elapsed().as_micros()
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
@ -211,7 +229,7 @@ where
|
|||||||
let forces: Vec<(i64, Vec3)> = calc_funcs
|
let forces: Vec<(i64, Vec3)> = calc_funcs
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.fold(
|
.fold(
|
||||||
|| Vec::<(i64, Vec3)>::with_capacity(total_boid_count / 10),
|
|| Vec::<(i64, Vec3)>::with_capacity(total_boid_count),
|
||||||
|mut acc, (boid_id, calc_fn)| {
|
|mut acc, (boid_id, calc_fn)| {
|
||||||
let force = calc_fn();
|
let force = calc_fn();
|
||||||
acc.push((boid_id, force));
|
acc.push((boid_id, force));
|
||||||
@ -219,7 +237,7 @@ where
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.reduce(
|
.reduce(
|
||||||
|| Vec::<(i64, Vec3)>::with_capacity(total_boid_count / 10),
|
|| Vec::<(i64, Vec3)>::with_capacity(total_boid_count),
|
||||||
|mut left, mut right| {
|
|mut left, mut right| {
|
||||||
left.append(&mut right);
|
left.append(&mut right);
|
||||||
left
|
left
|
||||||
@ -227,19 +245,19 @@ where
|
|||||||
);
|
);
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
godot_print!(
|
godot_print!(
|
||||||
"[Boids] calculating all boids took {} ms",
|
"[Boids] calculating all boids took {} micros",
|
||||||
time.elapsed().as_millis()
|
time.elapsed().as_micros()
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
let time = std::time::Instant::now();
|
let time = std::time::Instant::now();
|
||||||
for (boid_id, force) in forces {
|
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);
|
boid.bind_mut().apply_force(force);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
godot_print!(
|
godot_print!(
|
||||||
"[Boids] applying forces took {} ms",
|
"[Boids] applying forces took {} micros",
|
||||||
time.elapsed().as_millis()
|
time.elapsed().as_micros()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user