Particle World
- 11 Devlogs
- 46 Total hours
Writing my own Particle Life simulation, and maybe adding a few twists and additions to it.
Writing my own Particle Life simulation, and maybe adding a few twists and additions to it.
The bug I mentioned in my last devlog, the one where I couldn’t understand why it only happens when a number is odd, I still don’t understand it, but I managed to fix it.
In the shader I use for rendering, I used fmod(InstanceID, colorCount) for finding out what color a particle is, but in my compute shader i do it like this InstanceID % colorCount, and apparently those do something different with the same int input? I don’t know.
But now it’s fixed, and I added a Demo video to my README.
So now I can finally ship this.
So, I now pretty much have finished writing my README, only thing missing is a demo video, but before I can add that, I’ll have to fix a bug.
Because apparently when you input a odd number as the amount of colors, the values in the matrixes are not the one of the color shown, but of the color 1 to the left/up? But only for odd numbers, even numbers work fine?
But anyway, I also added a FPS counter to the simulation.
So now I’ll go to either try find out why that bug happens, or just add something that makes the bug invisible.
Knowing that I know my code to well, I gave a working version of it to my sister, to watch her try it, and see if there’s anything somebody could do, that isn’t really intended.
And it turns out there is!
It seems quite obvious now, but one thing you could do, but weren’t supposed to, was inputting any values you want, which is fine if you know what you’re doing, but most probably you won’t, so I had to add Input validation.
In some places it was more important than in others, because your GPU really doesn’t like it if you define a buffer with a negative size, which happend if you inputted a negative amount of particles.
I also cleaned up the files a bit, by taking all unused files, and putting them in a folder called… “Unused”, really clever name choice, isn’t it?
So now, I get to prepare for Shipping my project, although I’m not really looking forward to writing the README yet.
So I know I said in Devlog 7, I would only polish some stuff and get ready to ship, but I just couldn’t not add the ability to edit the Matrixes and move the Camera, the first of the two, I was able to do with the help the GridLayout Component, which automatically arranges all it’s children in a grid. I did that by dynamically adding an UI input Element for each Value in the Matrix, and putting in the corresponding Value. And now, you can change the values of each Cell, and when you’re finished, you can hit apply, and now you’ve changed the behaviour of the particles, yay!
Getting the Camera to zoom and move in a way I like, was really hard, probably because I was kind of tired, or maybe because I’m just stupid, who knows. But anyway, I’m using Unitys Input System to geth mouse movements and scrolling, so that I can then read out the value of those in my Script.
The zooming is works like this:
Zoom += Zoom * (ScrollValue/10);
CameraSize = OriginalCameraSize * Zoom
And the camera movement works by turning the mouse position an the screen into a position in the World Space, and multiplying it wiht the suqre of zoom, so that the movement gets slower and slower when you zoom in.
There is even more to it than this, but it would be a pretty long Devlog if I explained everything, so I guess next time I shouldn’t wait 8h to write a Devlog. (:
I finally did it, I added an UI to it, it doesn’t include everything I wanted, but it doesn’t have to be perfect, especially seeing as Unity thought it had to nuke half my work an the UI, just as I wanted to save it, that was quite annoying.
But now you can control pretty much everything except the stuff that you can’t through the UI, like the amount of particles to simulate, some range values, physics variables, and of course the color of the particles.
So now I probably just need to polish some stuff and get everything ready to be shipped, and then I’ll probably ship it, we’ll see.
So I know the next thing, and only thing left, on my To-Do list, as seen in my last Devlog, was to add an GUI/interface, but I wanted to improve on how many particles you can simulate at once, so I researched how shaders and compute shader work, and made one that could do everything my C# script could do too, but faster, as compute shaders run on the GPU, which is really good at doing things in parallel.
But it wasn’t easy, as I had no idea how both shaders and compute shaders work, and I still don’t really know how shaders work, but atleast I know how compute shaders work now.
But getting to this point, where it finally works, took me the entire day, with having to do research and having to keep trying new stuff to find something that works, but I think it was worth it.
But I was kind of too lazzy to implement a BitonicMergeSort to sort using the GPU, so I took that code from https://github.com/SebLague/Fluid-Sim, and slightly altered it, so it would work for me.
So now, I’ll just have to add an GUI or something like that, adjust some stuff, (like changing it so the particles don’t spawn in a symmetric grid, or particles don’t spawning inside other particles anymore), and clean everything up, and then I’d probably be ready ship it for the first time, depending on whether I wanna add more stuff first or not.
So now I’ve done it, the next thing on my To-Do list is completed, as seen in my last Devlog, it was to Optimize and Improve calculations, and I did that, mostly by implementing Spatial Partitioning.
How it works is that, if there is only a certain range around an particle, in this case the MaxRange, you can pretty much ignore everything outside of this range when calculating attraction, instead of having to check the distance to each particle, and getting the same result. To do that you split the area where the particles can be into squares(Cells) with length of MaxRange, and then assinging each particle a key, which you get like this:
key = someHashFunction(CellPos.x, CellPos.y) % ParticleAmount
And then with some sorting and such, you can calculate the attractions, only the Cell the particles in, and the 8 cells around it.
This was somewhat simplified, but I hope one could understand it.
And in the video below, you can see me simulating 4000 particles, at roughly 10 fps, which might not sound like a lot, but compared to before, where simulating 2000 particles resulted in about 5 fps, it certainly is.
To-Do:
Like mentioned in my last Devlog, one thing I had to do was adding interaction based on color and distance, and hurray! I did both!
So I did it by having an array of floats, which contains the attraction matrix, but flattend, and did the same for the distance values, basically like this:
array[i + j * width] = matrix[i][j]
I also made a function that based on a minRange and maxRange around a particle calculates it’s attraction to other particles, if it’s closer than minRange, it gets pushed away, otherwise if it’s closer than maxRange, it gets attracted or repelled based on the attraction matrix.
I made it so instead of using Time.deltaTime it now uses a fixed value that you can change, and added friction, so the particles, making the particles alot more stable than they’d be otherwise.
So now, I’d have a usable version of Particle Life, but as you can see my To-Do list isn’t quite finished yet.
To-Do:
In Devlog 2 I wrote down what I planned to do next, and now I’ve done it.
I added an adjustable border that when crossed moves the particles back inside, and makes them bounce of by inverting their velocity, but dampening it a bit.
Then I made it so whenever you start, it arranges the amount of particles you choose in a grid, in the middle of the border, with adjustable spacing between the particles.
Then I made it so you can chose however many colors you want, and the colors will be equaly distributed to all the particles. I did this by basically doing this:
particleColor[i] = colorList[i % colorList.length]
Where the colorList[] is made up from the colors you input in the inspector.
I have now decided to not have each particle be a GameObject, but instead handle all of them from 1 Script that stores each particles position and velocity, to then calculate interactions and draw them. Right now I’m doing the drawing using Graphics.RenderMesh() for each individual particle, and I can already have them be attracted or repelled by each other.
So first I had to set up my Unity project, link it to a GitHub repo, and then installed HackaTime to track the time I work in Unity.
So I wanna create something based on Particle Life, but with a few changes and twists.
So up until now, I have already made a Script that gets all colliders in a certain range around the GameObject it’s attached to, by using Physics2D.OverlapCircleAll(), and then calculates and applies a certain force into the direction of that other Collider, by using RigidBody2D.AddForce().
But right now, they still act the same towards all other particles, not just those of the same color.
And now, I have also come to the conclusion that I will have to think about whether I wanna do this Project using the GPU for the calculations, or not, because I’ve realized that only using the CPU might not have the greatest performance, but the way I’m currently doing stuff, probably won’t work for a compute shader.