Progress Update #13

 Niagara Asteroids in UE4


    These past few days I have continued work on asteroids. Only that now I have ditched instanced static meshes in favor of getting my hands on the new-ish Niagara particle system. 


Learning Niagara

    I have decided that since this is an entirely new subsystem of the engine I would do everything in blueprint first, then transition everything to C++ when I knew it was working reliably. Additionally, because Niagara is still relatively new, the official documentation is sparse if existent at all. So with most tutorials at this time geared towards blueprint implementation, starting in blueprint will give me the highest likelihood of finding a solution to any problems I may come across.


The Niagara Workflow

    The first thing I learned about Niagara is that the workflow has changed significantly from what cascade was. Where in cascade (to my limited knowledge) you worked in one particle system where everything to do with it was self-contained, Niagara separates pretty much every aspect into its own asset. An emitter can be made part of a system which can implement a script of which there are three different kinds. This new approach makes things way more modular and expandable, which is super awesome, only it's at the cost of the learning curve being very steep. 
    My first attempt at making asteroids started with me trying to teach myself the system from scratch. That didn't last too long and was a bad idea. I tried again, this time having watched many official tutorial videos and having read as much documentation as I could. 


The Emitter

    The core element of any Niagara system is an emitter so I started there. The goal was to have a generalized emitter that could be used for two types of asteroid 'volumes': asteroid fields, which is just a bunch of asteroids strewn about; and asteroid belts, akin to the asteroid or Kuiper belts in our solar system. 
    To accomplish this, I started with the "hanging particles" template and edited a few of the defaults. Firstly I changed the emitter to be a GPU sim as I knew I would want thousands of particles at once which would be much too slow on a CPU. I then changed the spawning of particles from being a rate to an instantaneous burst so that all asteroids would exist at the start of simulation. Then in particle initialization I changed lifetime to 0 (meaning infinite life) so that the asteroids wouldn't disappear. I knew that I would be making my own movement system so I removed everything to do with particle movement, this included: curl noise force, drag, scale sprite size, scale color, and solve forces and velocity. Lastly I changed the renderer from sprite to mesh and put in a placeholder cube. 
    Here's what the emitter looked like at this point:

There will probably be less pictures in this update because it's really hard to show everything important at once in Niagara. If you want to see the result in more detail I recommend that you download the project form the GitHub page and snoop around the system yourself.

    Now that I had a bunch of cubes spawning at random positions I got started on getting them to move. For now the goal was just to get all of the asteroids gravitationally attracted to the sun with the same algorithm that is used for planets. This required the use of two Niagara Module Scripts. 


The Module Scripts

    VelocityCalc:

        Just as the rest of the celestial bodies do, the first of two stages of movement is calculating the new velocity for this tick. The difficulties at this stage were just getting used to the input and output 'maps', getting used to nodes having different names, and the fact that it looks really ugly because of a plugin I have that changes the way blueprint lines work. also I couldn't figure out how to loop through objects in Niagara so the only mass that effects the movement of the asteroids is the sun. Here's the result:


And here's the C++ is is replicating:
    

    UpdatePosition:

        The second and most crucial part of moving celestial bodies is changing their positions. This is quite trivial, but here are the comparisons anyway:


The Systems

    So now I had the base for my asteroids, I needed to specialize them into the two variants I mentioned earlier: fields and belts. The main difference between the two is the spawn pattern. Belts will spawn in a ring, fields will spawn randomly within a bounding box/sphere. 

Spawning:

    Fields were super simple, I just changed the box location in the particle spawn to be a half-million unit cube and that was that. (I also changed the fixed bounds of the emitter to be really big because otherwise the entire particle system would be culled from view if you weren't looking at the sun where the center of the particle system was)

    The belts weren't too difficult either only because I had watched a certain official tutorial from GDC 2019 where the presenter explained how to spawn particles in a ring by using a cylinder location, enabling surface only spawning, and disabling endcaps (7:46 in the video). Now that was done too.


Initial Velocities:

    The very last thing I had to do to get the system working correctly was to set initial velocities for the asteroids. With no initial velocities the asteroids would spawn where they were supposed to but would all immediately start collapsing toward the sun. However, each system required a different initial velocity calculation as the belt needed to all go in one direction and the field I wanted to be almost entirely random to mimic random debris wandering the solar system. 

    Fields:
        So when I said almost random that is only because I found Niagara has really nice integrated support for timeline curves. This got me thinking about a weighted random initial velocity where there would be more slower moving asteroids but a few fast ones. I made this graph to represent that with the same curve representing X, Y, and Z:


        The only problem with this is that I don't know how to randomly select a different value for X, Y, and Z. This has to be done thought because if it's not then all asteroids will all be going towards the all positive "quadrant" or all negative because X, Y, and Z have the same values. I got around this with a really hacky and all-around bad solution; sample the curve thrice at different time values and make a vector from that:


        Again, I can't stress enough how dumb this is, and I will fix it in the future, but I thought I'd include it for the time being because of how ridiculous it is.


    Belts:
        Figuring out how to get the belt asteroids to all orbit in the same direction was the hardest part of this whole experience. This is because of a combination of the lack of debugging tools in Niagara (or my inability to find them) and the difference of function names from C++ to Niagara. 
        The main challenge was getting the tangent vector. This is the exact same vector as from the SetToOrbit() function from last progress update. So I knew exactly what I had to do, but the whole problem was telling Niagara that. Niagara doesn't have a function FindLookAtRotation() which was pivotal to the orbit function's calculation.
        So I spent a while looking into the engine code, trying to wrap my head around what FindLookAtRotation() was doing. As it turns out, it's matrix math which I don't fully understand. I also dug into a bunch of Niagara functions trying to find a substitute. I'll skip a lot of boring backtracking and frustration here and just cut to the chase.
        Niagara does have a substitute to FindLookAtRotation() but it's just not documented anywhere or intuitive once you find it (that last part might not be true but it didn't and still doesn't really make sense to me). The substitute is a node called Make Quaternion from X And Y Vectors. The X vector output is the vector for the look rotation from the C++ function. 


        Side-note: The lower part of the graph that makes the Z of the final vector offsets it so that the asteroids don't all bunch up on the same Z-plane. It also produces some weird formations of the belt which I may end up changing later.

Comments

Popular posts from this blog

Polishing the Foundation

A New Chapter