Skip to content

Instantly share code, notes, and snippets.

@mingyang91
Last active February 28, 2024 14:44
Show Gist options
  • Save mingyang91/cbce832f5a6b2fb098bc0e40164964cc to your computer and use it in GitHub Desktop.
Save mingyang91/cbce832f5a6b2fb098bc0e40164964cc to your computer and use it in GitHub Desktop.
Object Pool for Godot
impl ReuseObject for Bullet {
fn init(&mut self) {
}
fn prepare(&mut self) {
if self.state != State::Init {
self.state = State::Init;
self.base_mut().set_process(true);
self.base_mut().set_physics_process(true);
self.base_mut().show();
}
}
fn recycle(&mut self) {
if self.state != State::Inactive {
self.state = State::Inactive;
self.base_mut().hide();
self.base_mut().set_process(false);
self.base_mut().set_physics_process(false);
}
}
}
use std::rc::Rc;
use object_pool::Pool;
use std::cell::{Ref, RefCell, RefMut};
use std::collections::VecDeque;
use std::ops::Deref;
use godot::obj::{Bounds, bounds, Gd, GodotClass};
pub trait ReuseObject {
fn init(&mut self);
fn prepare(&mut self);
fn recycle(&mut self);
}
impl <C> ReuseObject for Gd<C>
where C: ReuseObject + GodotClass + Bounds<Declarer = bounds::DeclUser>, {
fn init(&mut self) {
self.bind_mut().init();
}
fn prepare(&mut self) {
self.bind_mut().prepare();
}
fn recycle(&mut self) {
self.bind_mut().recycle();
}
}
pub struct GdPool<R: ReuseObject> {
capacity: usize,
pool: Rc<Pool<R>>,
buffer: RefCell<VecDeque<Rc<Reuse<R>>>>,
}
pub struct Reuse<R: ReuseObject> {
pool: Rc<Pool<R>>,
obj: RefCell<Option<R>>
}
impl <R: ReuseObject> Reuse<R> {
pub fn borrow(&self) -> Ref<R> {
Ref::map(self.obj.borrow(), |obj| {
obj.as_ref().expect("impossible")
})
}
pub fn borrow_mut(&self) -> RefMut<R> {
RefMut::map(self.obj.borrow_mut(), |obj| {
obj.as_mut().expect("impossible")
})
}
}
impl <R> Drop for Reuse<R>
where R: ReuseObject {
fn drop(&mut self) {
if let Some(object) = self.obj.borrow_mut().take() {
tracing::debug!("Dropping obj");
self.pool.attach(object);
}
}
}
impl <R: ReuseObject> GdPool<R> {
pub fn new<F>(capacity: usize, init: F) -> Self
where F: Fn() -> R {
GdPool {
capacity,
pool: Rc::new(Pool::new(
capacity * 2,
|| {
let mut obj = init.call(());
ReuseObject::init(&mut obj);
obj
}
)),
buffer: RefCell::new(VecDeque::with_capacity(capacity * 2)),
}
}
pub fn get(&self) -> Option<Rc<Reuse<R>>> {
let object = self.pool.try_pull()?;
let (_, mut obj) = object.detach();
obj.prepare();
tracing::debug!("object prepare");
let reuse = Rc::new(Reuse {
pool: self.pool.clone(),
obj: RefCell::new(Some(obj)),
});
let mut buf = self.buffer.borrow_mut();
buf.push_front(reuse.clone());
if buf.len() > self.capacity {
tracing::debug!("buffer full {}, drop older object", buf.len());
let drain: Vec<Rc<Reuse<R>>> = buf
.drain(self.capacity..)
.collect();
for reuse in drain {
tracing::debug!("destroy object");
reuse.obj
.borrow_mut()
.as_mut()
.expect("impossible")
.recycle()
}
}
Some(reuse)
}
}
fn process(&mut self) {
if input.is_action_just_released("attack".into()) {
let start = self.base().get_global_position();
let mut bullet = self.create_bullet();
let mut bullet = bullet.borrow_mut();
bullet.bind_mut().shot(start, velocity);
tracing::debug!("bullet shot");
}
}
fn ready(&mut self) {
let bullet_scene = load::<PackedScene>("res://Bullet.tscn");
let base = self.base().clone();
let pool = GdPool::new(10, || {
tracing::debug!("Instantiate bullet");
let bullet = bullet_scene.instantiate_as::<Bullet>();
let Some(mut parent) = base.get_parent() else {
tracing::error!("Failed to get parent of bullet");
unreachable!("Failed to get parent of bullet");
};
parent.call_deferred("add_child".into(), &[bullet.clone().to_variant()]);
bullet
});
if let Err(_) = self.bullet_pool.set(pool) {
tracing::error!("Failed to create bullet pool!");
}
}
fn create_bullet(&mut self) -> Rc<Reuse<Gd<Bullet>>> {
let pool = self.bullet_pool
.get_mut()
.expect("uninitialized bullet pool");
let bullet = pool.get()
.expect("pool exhausted")
.clone();
return bullet
}
#[derive(GodotClass)]
#[class(base=Area2D)]
pub struct Player {
bullet_pool: OnceCell<GdPool<Gd<Bullet>>>,
base: Base<Area2D>,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment