I actually met Craig (the inventor of Boids) last week. He's a cool guy, 70, and still doing cool stuff. Recently on entities camouflaging themselves using a GAN
The intro is perfect for your channel name. "Games that have extra CPU cycles" and wanting to use them is a pretty good definition for "useless", but it's also amazing!!!
You get a lot of info across in a short time in a way that isn't overwhelming. Doing Sebastian Lague's landmass generator series right now and think I'll follow this one hands-on next. Cheers!
I supposedly have a master's in complexity. I don't know half the crap you do. This channel totally helps. EDIT: I can confirm the English word is indeed "octant".
You should have them speed up and slow down as well as steer, and an animation of flapping/gliding when they do this or when they gain/lose altitude. You could also have Stamina which makes 'tired' boids more likely to glide and stops the lead boid just powering away on full throttle.
I feel like those birds need a bit stronger forces on the separation rule...they were colliding a lot in the center. But that's just settings. Cool video as always! And thanks for showing off the new Unity features.
Ooooh crap I've been working on my own version of this but literally got Unity to BSOD my computer... This is a great guide that will correct my ship! Thanks! Subbed and put on alerts.
Support for c# async await has indeed been available for a while but not unity has actually implemented stuff you can await like waiting for the next frame. It's a game changer
Hmm in the end demo it looks like the separation rule was not quite met, as there were several birds clumping together, looking very unnatural. There were some outlier birds, keeping distance to a cluster, almost looked like an atomic nucleus and electrons. This might be because the center boids have the sum of their "avoidance forces" be 0 as they're equally clumped together by the "electron boids" which circulate around them?
Vector fields can be a good alternative method to optimise and can get you closer to O(n) complexity. The tricky part is calculating the field efficiently, but if you do it right you should only need to sample each boid's position and heading once. Conceptually you are generating a heatmap of boid density and average boid direction, and sampling that map instead of querying neighbouring boids. You can get tremendous speedups this way and it GPU-ifies nicely since it maps nicely to the concept of textures.
Dude, they finally added async/await standard support (like JS and C#) in Unity apparently this took so long for them... Welp, I'm glad it's here now. btw, great video!!
It has been here for a while is just wasn't very useful because it wasn't well integrated with Unity. you couldn't await for the next frame or await for in-game time, which made it sort of useless. But you could use async functions before
@@uselessgamedev Yes, that was the situation last time I checked out. Basically useful for stuff not related to Unity itself (like serialization for save files)
this could be interrest if you could give a target other than unexplored zone, likeuse boids for group of ants that goes to a food zone and back, or moths that flock randonly but when near lamp they hover around it
Learn something new everytime here! The async stuff sounds honestly magical (as someone who has always been scared of parallelisation), gotta check that out for sure. I noticed in both your 2D and 3D example your boids tend to get really close together or even kind of "collide" - did you take out the avoidance rule at some point or is that intentional? In my experiments i gave each rule a weight parameter to be able to adjust how "flock-likely" different types of boids could be, and i noticed at least in early versions for some reason i always had to slightly decrease the cohesion factor to avoid "collisions"... Thanks for the video, looking forward to learning and then teaching me compute shaders!
I did not remove rules but I did tweak the weights of each rule and probably should have had a higher weight for separation. Async executes on the main thread by default but if you parallelise it using BackgroundThreadAsync you will still run into concurrency issues so you *should* be scared. I'm terrified of parallel stuff also haha
Super interesting overview and nice examples. This has certainly given me some ideas for "emergent behavior" from move data particles (which could be used for anything! granular wavetable synthesis anyone?)
Hi thank you! I use Adobe Premiere for 99% of the editing, and sometimes when there's a particularly tricky shot I animate it in Unity using shaders/scripts. Probably not the best option but that's what I'm most familiar with, I'm not very good at video editing
You could probably get a performance boost by pre-calculating the scene colliders into a distance field or something. Could even speed that up by limiting the distance it calculates to the view distance of the boids. It'd be _much_ faster to fetch from the distance field than have to check for surrounding colliders. Though if you wanted to get fancy and have it move towards the area with least density, you'd be doing 6-8 fetches per boid, which would still not be ideal. I'd wager it's still faster than checking for physics collisions though.
I love that the "swoosh" sound effect when your avatar pops up is literally just you making a "swoosh" sound, haha. Excellent video too, such a simple and effective way to describe the mechanics of such a ubiquitous concept. I especially appreciate the time you spent on optimisation.
@@uselessgamedev like to choose different characters. Like on the hit game Hotline Miami. You can choose different characters, I asked some friends and they say its a data structure. But I can't find any tuts on it
Yeah it's kinda misleading but I forgot to mention the test scene was much much larger than what you see on screen, and therefore there are like 2K+ fog probes, which are also costly because scanning them is O(nBoids * nProbes)
Instead of using the position of colliders to apply a force, could you use their bounds to create unnavigable holes in your fog? Say the fog stored a number to represent strength, with zero meaning it's just been explored and positive values indicating spots worth exploring. Negative values could then be used to push the boids away from an area, so your physics call could find the fog probes that are within colliders, set them to negative, and thus let the boids avoid the whole bounds of the collider without needing to check the collider positions on the main thread. I have no idea if that would end up improving performance or not if you recalculate the fog in response to dynamic colliders, but if you're just worried about static obstacles, that could be computed once and let you keep all boid calculations parallelized-plus the benefit of full collider avoidance.
Yeah I guess that would work, it's basically baking the colliders into the fog "map", which would eliminate the physics rule (which is a good thing because it's the only one that's not threaded)
Heyo I'm currently taking an intro into computer Science course, and am really interested in the type of unity linked coding I see you do, but I was wondering what coding language you use?
Thank you for the like on my other comment! I was trying to branch out more and was wondering if there was a version control platform I could follow you on. Fair enough if you aren't distributing your creations though.
Hi! If you're looking for the source code of the video projects, they're available on Patreon (link in the description). Otherwise I do have personal GitHub but there's basically nothing on it and it needs some cleaning up. It's mostly projects from a past life
@@uselessgamedev totally understand, I honestly thought you were doing much better on youtube before I just looked now. Will consider when I can, that's an attractive offer
can the octree or quadtree be infinite (or really big) ? like the outro scene, the limit range of boids are big af And can i make it for multiplayer (which means people can see the same boids)
I don't think there's a limit to how large they can be, especially since if a region isn't in use it won't be consuming resources. As for multiplayer, I guess it's technically feasible but you might have a hard time synchronizing every one
@@uselessgamedev thanks bro, can i ask some more ? - is a boid a gameObject ? Can i give them some collisions ? (Like, in ULTRAKILL when i shoot a fish (which is not a boid and just moving randomly), it dies (Destroy())) -if they have collisions can i optimize them ? Can they interact with other collisions ? Thanks, dont take this too seriously
Yes and yes, my boids are game objects with rigid bodies and colliders. Although it's not good for performance so I ended up disabling the physics at some point
@@uselessgamedev Thanks A Lot. You are an amazing person Unfortunately (im sorry), i still got some questions: -how can you turn off physics, bc i think they are moved by Rigidbody, so when u turn off physics, they will move by changing position over time ? (I think u meant u will turn off collisions ?) -Do you think using DOTS will optimise them ? Thanks again, you can answer anytime, dont take this too seriously
DOTS will definitely be more performant than GameObjects. By disabling physics I mean removing the collider and rigidbody components. The boids move forward by themselves
You seem to have a bug, I suspect in the octree calculation or the grouping behavior has too strong a weight. If you look at the birds at the end they go to the corners and centers of a cube and group tightly. At 11:16 you can even see a perfect hexagon of birds: they're at the corners of the cube.
Yeah I think it would make a difference (and Unity's tech demo for DOTS has boids so). But if I was to remake this, I would directly make a compute shader (provided I didn't need the boids to interact with other stuff)
Yes, the one-month period ends on Oct. 14th, but iff I manage to publish the next video before then I'll unsuspend it when releasing the video Sorry for the inconvenience :(
Wait a second. How did you get those pretty dropdowns and read only fields in the inspector seen at 10:29? That would be so useful to have. You should definitely learn compute shaders btw. (Trust Acerola if not me)
That's Odin Inspector. It's a must have if you're using Unity. It allows you to organize your fields (and properties!) with better drawers, groups, required fields, a bunch of things. But the main feature is the ability to add [Button] before a function to instantly create a button in the inspector that calls this function. No custom editor bs required. I can't think of a single other asset that boosted my productivity even half as much as Odin
Your performance numbers are very weird, much lower than I'd expect :/. Computers are fast, and using my game engine, single threaded, n² complexity, I can get a boid-like simulation to run fine at 70 fps with 1000 boids (on a 5 year old processor, Ryzen 5 2600)
Yes, most of the cost in my simulation comes from the fog probes as it was tested on a much larger scene with 2000+ probes. I forgot to mention the test environment in the video, I guess it works from the clickbaity "wow we got from 15 boids to 1000 aMaZiNg omggg" point of view but it's not very telling the whole story
You went with complex optimizations way before applying simpler optimizations. There's no reason why you couldn't do 1000 boids on a single core, if done properly. There may be some massive hidden Unity bottlenecks here. Your 15 boids running at 3 FPS isn't due to lack of acceleration structures and multithreading. I know nothing about Unity though, since I build engines for a living.
Yes that may be true. Also I forgot to mention it in the video but the test scene is much much larger than what you see on screen, and thus there are thousands of fog probes. That was a major issue
I'm wondering - is a quadtree the best way to partition the space for boids? I've done a lot of Boid simulations in a lot of different lanauges (C#, Rust, Lua, etc) found a generic sparse spatial hash seems to work better for performance. Good video either way!
I don't know what "the best way" is, I bet a sparse spatial hash would work great too. I should also test the performance differences between quadtree and kd-tree but maybe another day
Boids will be boids
I'm pinning this.
@@uselessgamedev Much obliged, wise CoderKappa
Me and the boids:
@@revimfadli4666 we moving in synchronous cellular automaton fashion
sick of this boids club humour
I actually met Craig (the inventor of Boids) last week. He's a cool guy, 70, and still doing cool stuff. Recently on entities camouflaging themselves using a GAN
Oh nice! I should look that up
The intro is perfect for your channel name. "Games that have extra CPU cycles" and wanting to use them is a pretty good definition for "useless", but it's also amazing!!!
🎵Two boids, chillin' in the hot tub, 5 feet apart because🎵...the separation has already been implemented.
This is incredibly not-useless for the project I'm currently working on.
Also, glad to see a new video! Was hoping we'd see you back soon.
Thank you!
Great man, you got me curious now. What are u working on?
You get a lot of info across in a short time in a way that isn't overwhelming. Doing Sebastian Lague's landmass generator series right now and think I'll follow this one hands-on next. Cheers!
I love how these behaviors can arise from a very simple set of rules
Thank god unity was kind enough to sponsor this video, happy to know they care about their devs😊
I supposedly have a master's in complexity. I don't know half the crap you do. This channel totally helps.
EDIT: I can confirm the English word is indeed "octant".
I absolutely love the optimization quad data structure and how cleverly you managed to increase performance. Thank you for the wonderful video!
I was hoping you'd cover this topic, boids are so simple in nature but I love them so much!
You should have them speed up and slow down as well as steer, and an animation of flapping/gliding when they do this or when they gain/lose altitude. You could also have Stamina which makes 'tired' boids more likely to glide and stops the lead boid just powering away on full throttle.
The 'woosh' always makes me smile.
Great stuff as always
I'm slowly building up a sound bank of mouth-made SFX it's coming along nicely
I feel like those birds need a bit stronger forces on the separation rule...they were colliding a lot in the center. But that's just settings.
Cool video as always! And thanks for showing off the new Unity features.
Thanks! Indeed it seems like I have used the wrong separation weight there
Ooooh crap I've been working on my own version of this but literally got Unity to BSOD my computer... This is a great guide that will correct my ship! Thanks! Subbed and put on alerts.
I first learned the word "boids" when looking through the Half-Life character models.
the birds coinciding with each other actually made me laugh it's so goofy how they clip into each other aggressively
10:14 that guy seems so handsome
the man himself
lol nice
Reminds me of the 1987 CGI short, Stanley & Stella in Breaking the Ice, only now we can do it in real time at much higher fidelity. Amazing, isn't it.
They added native await support? that's amazing
I was using async+await in Unity back in 2022, C# has that built-in. Although I'm glad Unity is able to integrate with it better than it used to.
Support for c# async await has indeed been available for a while but not unity has actually implemented stuff you can await like waiting for the next frame. It's a game changer
@@uselessgamedev Yeah, I forgot to mention how annoying it was to actually get the async code to integrate with Unity nicely
youtube compression is gonna love this one
Watching starling murmuration feels like you could cheat the 3D by using perlin noise as a height map for your boids.
Amazing video and it's great that you mentioned Compute Shaders
Unity sponsorship lets GOOOO
Hmm in the end demo it looks like the separation rule was not quite met, as there were several birds clumping together, looking very unnatural. There were some outlier birds, keeping distance to a cluster, almost looked like an atomic nucleus and electrons.
This might be because the center boids have the sum of their "avoidance forces" be 0 as they're equally clumped together by the "electron boids" which circulate around them?
those new async functions are awesome!
Vector fields can be a good alternative method to optimise and can get you closer to O(n) complexity. The tricky part is calculating the field efficiently, but if you do it right you should only need to sample each boid's position and heading once.
Conceptually you are generating a heatmap of boid density and average boid direction, and sampling that map instead of querying neighbouring boids. You can get tremendous speedups this way and it GPU-ifies nicely since it maps nicely to the concept of textures.
in 3D it will be cumbersome on memory though! 3D textures are still difficult for our hardware to cope with, its just such a huge amount of data!
Dude, they finally added async/await standard support (like JS and C#) in Unity
apparently this took so long for them... Welp, I'm glad it's here now.
btw, great video!!
It has been here for a while is just wasn't very useful because it wasn't well integrated with Unity. you couldn't await for the next frame or await for in-game time, which made it sort of useless. But you could use async functions before
@@uselessgamedev Yes, that was the situation last time I checked out.
Basically useful for stuff not related to Unity itself (like serialization for save files)
i love shader computing :)
The result was beautiful... Nice video!
this could be interrest if you could give a target other than unexplored zone, likeuse boids for group of ants that goes to a food zone and back, or moths that flock randonly but when near lamp they hover around it
I really like this flocking video.
Learn something new everytime here! The async stuff sounds honestly magical (as someone who has always been scared of parallelisation), gotta check that out for sure.
I noticed in both your 2D and 3D example your boids tend to get really close together or even kind of "collide" - did you take out the avoidance rule at some point or is that intentional? In my experiments i gave each rule a weight parameter to be able to adjust how "flock-likely" different types of boids could be, and i noticed at least in early versions for some reason i always had to slightly decrease the cohesion factor to avoid "collisions"...
Thanks for the video, looking forward to learning and then teaching me compute shaders!
I did not remove rules but I did tweak the weights of each rule and probably should have had a higher weight for separation.
Async executes on the main thread by default but if you parallelise it using BackgroundThreadAsync you will still run into concurrency issues so you *should* be scared. I'm terrified of parallel stuff also haha
Thanks Pseuduck
I would love to see this in Unity 2021 uuuugghhh
I don't think it will be backported. Maybe to 2022 but even that, there's no reason to
@@uselessgamedev Well, new unity versions are slower and cluttered with stuff. I'm developing for VR and the newer versions are trash for this...
Super interesting overview and nice examples. This has certainly given me some ideas for "emergent behavior" from move data particles (which could be used for anything! granular wavetable synthesis anyone?)
Mother flocking birds was too good
Ideally you would have them keep equal distances from one another too. I see they kinda clump together.
Love your content , im always exited for new vids, can u ask u, how are you animating your videos?
Hi thank you! I use Adobe Premiere for 99% of the editing, and sometimes when there's a particularly tricky shot I animate it in Unity using shaders/scripts. Probably not the best option but that's what I'm most familiar with, I'm not very good at video editing
Loved your graphics!
Thanks!
nice video :))))
Acerola whha?
LOL
commenting to help the algorithm, great stuff
"Just me and the boids" ;-)
You could probably get a performance boost by pre-calculating the scene colliders into a distance field or something. Could even speed that up by limiting the distance it calculates to the view distance of the boids. It'd be _much_ faster to fetch from the distance field than have to check for surrounding colliders. Though if you wanted to get fancy and have it move towards the area with least density, you'd be doing 6-8 fetches per boid, which would still not be ideal. I'd wager it's still faster than checking for physics collisions though.
A SDF would actually be usable by a compute shader so that's a pretty great suggestion, thanks!
I love that the "swoosh" sound effect when your avatar pops up is literally just you making a "swoosh" sound, haha. Excellent video too, such a simple and effective way to describe the mechanics of such a ubiquitous concept. I especially appreciate the time you spent on optimisation.
Thanks! Indeed all sound effects are now mouth-made since the Moebius video. Slowly building up to a sizeable sound bank haha
I fuckin love the boid algorithm lmao
next step is compute shaders
WOOOOO! My favourite game developing tortoise!
that's cool seriously
Take a sip everytime he says boids
bro got sponsored by unity :OO
FUCKYEAH FUCKING BOIDS IVE BEEN WAITING FOR THISSSS
Youre awesome btw
Thanks! You're awesome too
:O woh, hope youre having a good day, cant wait till you talk about character selection type data structures
@@uselessgamedev
What do you mean? Always interested to learn about more data structures haha
@@uselessgamedev like to choose different characters. Like on the hit game Hotline Miami. You can choose different characters, I asked some friends and they say its a data structure. But I can't find any tuts on it
I'll look into it thanks!
Congrats on the sponsor
I LOVE BOIDS ❤
Me and the boids at 3am looking for beans
Boids. Beans. Battlestar Galactica.
Odd 6:37 . Even with O(n^2) the framerate for 15 boids should be higher than with O(log(n)) with 1000 boids. Did you have some other tweaks?
Yeah it's kinda misleading but I forgot to mention the test scene was much much larger than what you see on screen, and therefore there are like 2K+ fog probes, which are also costly because scanning them is O(nBoids * nProbes)
Instead of using the position of colliders to apply a force, could you use their bounds to create unnavigable holes in your fog?
Say the fog stored a number to represent strength, with zero meaning it's just been explored and positive values indicating spots worth exploring. Negative values could then be used to push the boids away from an area, so your physics call could find the fog probes that are within colliders, set them to negative, and thus let the boids avoid the whole bounds of the collider without needing to check the collider positions on the main thread.
I have no idea if that would end up improving performance or not if you recalculate the fog in response to dynamic colliders, but if you're just worried about static obstacles, that could be computed once and let you keep all boid calculations parallelized-plus the benefit of full collider avoidance.
Yeah I guess that would work, it's basically baking the colliders into the fog "map", which would eliminate the physics rule (which is a good thing because it's the only one that's not threaded)
Me and the homies fr
Heyo I'm currently taking an intro into computer Science course, and am really interested in the type of unity linked coding I see you do, but I was wondering what coding language you use?
Hi! Unity uses C#
Your videos are very cool :)
That's cool! I wonder how it compares with DOTS
wait this is Awsome!
Thank you for the like on my other comment! I was trying to branch out more and was wondering if there was a version control platform I could follow you on. Fair enough if you aren't distributing your creations though.
Hi! If you're looking for the source code of the video projects, they're available on Patreon (link in the description). Otherwise I do have personal GitHub but there's basically nothing on it and it needs some cleaning up. It's mostly projects from a past life
@@uselessgamedev totally understand, I honestly thought you were doing much better on youtube before I just looked now. Will consider when I can, that's an attractive offer
Me and the boids:
Its acerola yo
can the octree or quadtree be infinite (or really big) ? like the outro scene, the limit range of boids are big af
And can i make it for multiplayer (which means people can see the same boids)
I don't think there's a limit to how large they can be, especially since if a region isn't in use it won't be consuming resources.
As for multiplayer, I guess it's technically feasible but you might have a hard time synchronizing every one
@@uselessgamedev thanks bro, can i ask some more ?
- is a boid a gameObject ? Can i give them some collisions ? (Like, in ULTRAKILL when i shoot a fish (which is not a boid and just moving randomly), it dies (Destroy()))
-if they have collisions can i optimize them ? Can they interact with other collisions ?
Thanks, dont take this too seriously
Yes and yes, my boids are game objects with rigid bodies and colliders. Although it's not good for performance so I ended up disabling the physics at some point
@@uselessgamedev Thanks A Lot. You are an amazing person
Unfortunately (im sorry), i still got some questions:
-how can you turn off physics, bc i think they are moved by Rigidbody, so when u turn off physics, they will move by changing position over time ?
(I think u meant u will turn off collisions ?)
-Do you think using DOTS will optimise them ?
Thanks again, you can answer anytime, dont take this too seriously
DOTS will definitely be more performant than GameObjects.
By disabling physics I mean removing the collider and rigidbody components. The boids move forward by themselves
Nice vid !
Thanks!
You seem to have a bug, I suspect in the octree calculation or the grouping behavior has too strong a weight. If you look at the birds at the end they go to the corners and centers of a cube and group tightly. At 11:16 you can even see a perfect hexagon of birds: they're at the corners of the cube.
Yeah I guess I messed up on the separation weight when I was tweaking it
0:54 -1:06
Now just take a few minutes to redo it in Unity's ECS and let's see the performance difference.
Yeah I think it would make a difference (and Unity's tech demo for DOTS has boids so). But if I was to remake this, I would directly make a compute shader (provided I didn't need the boids to interact with other stuff)
i didnt see the shell. i apologize for my past comments 🙏
Boids are too "sticky", might look better implemented with "min distance" parameter.
Your Patreon isn’t accepting new members, any updates on that?
Yes, the one-month period ends on Oct. 14th, but iff I manage to publish the next video before then I'll unsuspend it when releasing the video
Sorry for the inconvenience :(
@@uselessgamedev as long as I ca subscribe in the future, thank you
Your Moebius outline effect looks nice and I wanted to try it out
so useless, love it!!
I can just the boys lol
1:25 - 2:30
Ca frise la poésie...
honestly the birds dont look that great, but I can get the concept and stuff
Wait a second. How did you get those pretty dropdowns and read only fields in the inspector seen at 10:29? That would be so useful to have.
You should definitely learn compute shaders btw. (Trust Acerola if not me)
That's Odin Inspector. It's a must have if you're using Unity. It allows you to organize your fields (and properties!) with better drawers, groups, required fields, a bunch of things. But the main feature is the ability to add [Button] before a function to instantly create a button in the inspector that calls this function. No custom editor bs required.
I can't think of a single other asset that boosted my productivity even half as much as Odin
booooooiiiiiiids
kinda like biomimicry 😅
New UselessGameDev vid just dropped :D
Not useless.
Unity sponser 💀
Now give the player a rifle =p
Did you make this entire video just to say the word boid a thousand times?
Okay, you call this useless, but the binary tree might be a good solution for what I'm doing rn.
Yeah quadtrees are life-saving. However they're not binary trees. If you specifically need a binary tree for some reason, check out kd-trees
@@uselessgamedev True. It's definitely a topic I'll have to look into a bit more to fully understand things.
Thanks for the tip!
Your performance numbers are very weird, much lower than I'd expect :/.
Computers are fast, and using my game engine, single threaded, n² complexity, I can get a boid-like simulation to run fine at 70 fps with 1000 boids (on a 5 year old processor, Ryzen 5 2600)
Yes, most of the cost in my simulation comes from the fog probes as it was tested on a much larger scene with 2000+ probes. I forgot to mention the test environment in the video, I guess it works from the clickbaity "wow we got from 15 boids to 1000 aMaZiNg omggg" point of view but it's not very telling the whole story
You went with complex optimizations way before applying simpler optimizations. There's no reason why you couldn't do 1000 boids on a single core, if done properly. There may be some massive hidden Unity bottlenecks here. Your 15 boids running at 3 FPS isn't due to lack of acceleration structures and multithreading.
I know nothing about Unity though, since I build engines for a living.
Yes that may be true. Also I forgot to mention it in the video but the test scene is much much larger than what you see on screen, and thus there are thousands of fog probes. That was a major issue
Birds aren't real.
3th!
thirth?
I contemplated the option of posting the "first!" comment myself
@@uselessgamedev more like -1 st
@@uselessgamedev "UA-camrs channels owners don't count" as if they uplaod they are first to do imagine uploading Private and conpleting
You can't comment in private I tried :( when I had the sponsorship for last video I had to rush after publication to comment and pin
please stop playing the whoosh sound when you make your character appear
Why don't you like the woosh noises?
Is there any reason you'd still use a coroutine instead of await? Your accent is cool by the way.
Thanks!
I think there is something to do with garbage collection (or rather, garbage creation) but I haven't looked too far into that yet
I'm wondering - is a quadtree the best way to partition the space for boids? I've done a lot of Boid simulations in a lot of different lanauges (C#, Rust, Lua, etc) found a generic sparse spatial hash seems to work better for performance. Good video either way!
I don't know what "the best way" is, I bet a sparse spatial hash would work great too. I should also test the performance differences between quadtree and kd-tree but maybe another day
Its acerola yo