Little Doctor, Enders Game

Hey everyone,

This is a new personal project I have been working on for the past few days. I wanted to recreate the Little Doctor weapon from the movie enders game. I really liked the effect and it has a lot happening in it. I thought it would be a good effect to challenge myself both visually and technically. My goal is to make the whole thing work within niagara, no blueprints. I am also trying to use only GPU particles. Here are some still frames for reference.

This is my initial block out. This is mainly establishing the feel and experimenting with the more technically challenging aspects of the effect. The asteroids are sprites with normal maps on them.

Sill very WIP, Feedback Welcome!

Things left to do:
Hero asteroid meshes breaking up
Lightning ribbons jumping between hero asteroids
Weapon Charge/Impact/Beam
Post processes
More cowbell!

5 Likes

Update!

I have been working on the hero asteroid meshes. The goal was to be able to use gpu particles for a fractured/destructible mesh. It turned out to be way more complicated than i thought but i managed to cobble it together. The first issue was that each mesh pivot was at the center of each piece, so when i spawned them as gpu particles, they all overlapped.
Here is what i did…

First in 3ds max I fractures each piece, then i centered the pivots to each piece.

Next i wrote a script that would output all the pivots of each mesh to give their local offsets from 0,0,0.

Inputting each one of these values manually for each mesh index would be a nightmare, HLSL to the rescue! I used scratchpad to be able to input each position and map it to the mesh index.

// === EXACT positions provided by user ===
static const float POS_DATA[] = {

//Paste X/Y/Z vectors here.

};
// Safety cap to avoid out-of-bounds if PosCount is wrong
static const int MAX_POS = 999;
// ========================================

// Niagara inputs: Index (int), PosCount (int)
// Niagara output: OutPosition (float3)

float3 result = float3(0.0, 0.0, 0.0);
int count = clamp(PosCount, 0, MAX_POS);

if (Index >= 0 && Index < count)
{
    int baseIndex = Index * 3;
    result = float3(
        POS_DATA[baseIndex + 0],
        POS_DATA[baseIndex + 1],
        POS_DATA[baseIndex + 2]
    );
}

OutPosition = result;

This gives each mesh the correct position based on their ID. Having it as an array of vectors i can simply copy/paste from my max script was a massive time (and sanity) saver. My test mesh has 25 pieces, but i made another one with 96 pieces and it still works great.

Next i wanted to be able to spawn multiple clusters based on parent positions so i used the attribute reader to set that up. I needed it to evenly distribute the particles across each parent particle. I also needed it to spawn one of each mesh index per parent particle so each parent would spawn the cluster.

Once that was working i wanted to be able to add random rotations to each cluster. This is where things got tricky. I first tried to base the cluster rotations off of the parent orientation but it was causing each particle in the cluster to orient randomly instead of applying the rotation to the entire cluster. I ended up passing the parent index into the custom HLSL and using that as a random seed value for each cluster and doing the rotations in the hlsl.


Now i can set how many clusters and locations by the parent particles. which make it easy to place things exactly how i need in the scene. Since i am also outputting the parent locations from the scratchpad i can link that to point forces to blast apart each cluster.

next i need to figure out how to trigger the point force on each individual cluster instead of affecting them all that the same time.

I plan to have lightning ribbons chain from one to another and link the point force to the lightning so each one blows up sequentially as the lightning jumps from one cluster to another. I might be able to use the plexus mini tutorial posted here to achieve that. :thinking:

Anyways, back to work! :upside_down_face:

Your to do list seems really great. Consider adding some lighter debris flying at the camera and camera shake.

1 Like

UPDATE!

I have been working on the chain lightning effects between the hero asteroids. I still have some improvements to make but it is functioning correctly. For this effect i am using the simulation stages. It is applied to the same parent particles that are spawning each asteroid cluster. It uses a forloop setup to loop through all the particles and compare their distance to 0,0,0. Then when it has the distances it assigns a “rank” to each particle. Particles closer to 0,0,0 (local space) have a higher rank. The forloop ensures that there are no particles with a duplicate rank, which might happen if i simply got the distance from each particle and quantize it down to the amount of particles being spawned.

Once each “destination” particle has a rank, i have another emitter spawning a single particle. It gets the particles start position and rank, then it gets the position of the particle starting at the highest rank and lerps between its start position and the target particles position. Once it gets within a certain arrival radius, it switches to the next target location. It bounces through all of the target locations until it hits the end of the chain. When it arrives at each location, it outputs the position of that location and toggles a bool for X sections. Im using that for the position and impulse time for the point force to push the cluster particles away.

As the seeker particle moves i have another emitter using the “spawn from other emitter” modules to create the lighting ribbon.

I have a few planned improvements. One of them is to improve the rank ordering of the destination particles. Right now its ordered strictly by distance, but sometimes that causes the seeker particle to ignore particles close by and link to a particle that is far away. I want to weight the rank ordering to also take neighbor distance into account. This should bias the rank to slightly favor particles that are closer to its current target.

I also need to add a rotation force to each cluster particle but i need to only apply it to the particles that are being hit by the lighting. If i can assign the rank param to each cluster particle perhaps i can set something up to make this work. :thinking:

I also need to find a way to control the life time of the cluster particles so i can shrink them after they are blasted apart by the lightning.

Much to do but its getting there! :dizzy:

2 Likes

UPDATE!

Hey everyone, i have continued improving the chain lightning setup. I ended up having to refactor almost everything i had previously. I was originally doing things in the particle spawn/update loop but i ended up having to move almost everything into the simulation stages. Ultimately this allowed me to have more interoperability between the various emitter params but it came with a few issues i had to work out. In order for some of the scratchpads in the simulation stage to work, i needed set the simulation stage to always update. The issue was that when i was doing any logic for changing positions such as the asteroid cluster logic, it was constantly fighting between 2 positions each frame. The fix was to use some IF nodes to create a gate so it only updates for the first 1-2 frames of the simulation, then it stops trying to set the particle positions every frame and lets things play out naturally after words. A bit hacky but it works.

Some other things i have changed was i now have modular time delays i can set for the chain lightning particle and the asteroid impulse forces. Previously, the chain lighting would just move to the first destination and the instant it arrives, it would move to the next destination. I can now pause it at each location. The same was true for the point force that blasts the asteroid pieces apart. It originally triggered the moment the lightning arrived.

These delays allow me to tune the next thing i added which was the energy wave that runs across the asteroid before it blows apart. When the lightning reaches each position, a sphere mesh with a depth material scales up from 0 size. As it increases in size you get a nice energy wave that runs across the surface. I also added some crackling lighting when the asteroid blows apart. Im still working on adding lots of glowing embers etc as well as controlling the mesh material to make it glow as it blows apart.

Another issue i ran into was i couldnt figure out how to trigger particles to spawn at specific times. I wanted to create particles at each location when the lightning arrives. This is so i can spawn embers, the sphere for the depth material and other stuff at each asteroid location. (more cowbell!) My workaround was to not worry about spawning things at specific times at all. All the particles at each asteroid position are spawned when the effect starts. The various meshes and sprites are just scaled down to 0 size. Instead, I decided to manually control their age and lifetime. When everything is created, their age is manually set to 0. I also disable the particle state module so its age doesn’t automatically start counting. When the lightning particle reaches each asteroid position, the corresponding particles start counting their age, which i change into normalized age. (age/lifetime) Then i can use this delayed normalized age to control things such as size over time etc. So far this has been working really well.

Once i have finalized these things a bit more i should be able to start working on some of the other elements such as the weapons effects.

1 Like

Loving it so far! The energy wave is a really nice touch, but I feel like the lightning inside the asteroids after they crack should be more visible, maybe if it were bigger or staying a bit longer?

1 Like

Thanks! The lightning in the asteroids is a bit of a placeholder atm. I plan to spawn a lot of glowing debris and embers and make the rocks glow too. It might coverup the current lightning so im not sure if i will keep it. It depends on how it all fits together when its finished.

1 Like

Wow, learned a ton just reading this, stuff I didn’t even think was possible. Thanks for sharing, am excited to continue reading the updates and see how the final ends up. Awesome work!

1 Like

Update!

I have added more effects to each asteroid when it explodes. It is spawning a bunch of embers, smoke/fire and i also tossed in the ray marched radial flare i posted here awhile ago. Here is a quick material setup to create a radial flare with inverse square falloff. Its much nicer than using a radial gradient. Im using it for the opacity on the ray marched flare.

The glowing asteroid is done with a sphere mask. I read the position of the parent particle that each asteroid piece is spawned around with the attribute reader. Then i fed that position into a sphere mask on the material for the asteroid. It works well to make the asteroid appear like the core is heating up before it explodes. I wanted to have the sphere mask follow each piece as it moves away but i couldnt get it working properly and its probably overkill anyways. :person_shrugging:

I finally feel like i have worked out most of the kinks with the chain lighting setup. For awhile it would break if i looked at it funny. I don’t think the PAR was ever meant to be pushed this far. :sweat_smile:

I expanded the normalized age concept i mentioned above. In some of the emitters i needed the age to start counting as soon as the chain lighting arrived, but in others i wanted it to start counting when the chain lightning leaves, after any delay it might have. Now i have 2 normalized age params, the standard one that is triggered on arrival and a delay one that is triggered after departure. This gives me much more control over the timing of the various elements.

Here is my normalized age setup. Just make sure you disable the particle state module so it doesnt start counting the age as soon as the particle spawns.

I also ended up copy/pasting the chain lightning setup so i have 3 clusters of the chain lightning bouncing around at the same time. I applied different speed, and timings to each one so the overall effect feel more unique. Niagara can be finicky when copy pasting emitters with PAR and scratchpads. After copy/pasting i would have to manually relink all the PAR references since the emitter names changed. A bunch of the scratchpads would also break for no reason. I found restarting the editor after copy/pasting and relinking helped a lot. I would still have to manually check each emitter to make sure it would function properly though. :stethoscope: :expressionless:

I think i am ready to move on to some other aspects of the effect. Next i need to work on either the weapon effects or the huge cluster of explosions/lightning that happens in the background. The current smaller asteroids and the lightning in the background are placeholders from my initial block out, i might tackle that next.

2 Likes