Search Unity

Single particle system for multiple explosions in various positions and colors from script?

Discussion in 'Visual Effect Graph' started by TwiiK, Mar 28, 2020.

  1. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    I'm currently attempting to upgrade a game that I have been messing about with since Unity 3.x. It started out with the first particle system, then I tried moving to Shuriken, but I seem to remember that Shuriken wasn't mature enough at that time and didn't have proper scripting support and now I'm trying to move it to Visual Effects Graph to serve as my introduction to the system and to try and see what's possible with it.

    The game is basically a clone of Geometry Wars:


    I want to make as much of it as possible in VFX graph, but for the moment I'm focusing on just the explosions. In my previous attempt with the original particle system I had a single game object with a single particle system and for each bullet that hit the level bounds or an enemy I told the particle system to emit x amount of particles with a specific color. I felt like this was the most optimal solution available to me and the it was easy to implement and the performance was really good.

    The code was something like this (where explosions is the singular ParticleEmitter component):
    Code (CSharp):
    1.  for (int i = 0; i < 100; i++) {
    2.     float angle = Random.Range(0, 360);
    3.     float randomVelocity = Random.Range(minSpeed, maxSpeed);
    4.     Vector3 direction = GetUnitOncircle(angle, 1);
    5.     explosions.Emit(transform.position, direction * (tangentSpeed + randomVelocity), explosionParticleSize, 1, explosionColor);
    6. }
    Now that I have the power of the GPU available to me I want millions of particles affected by all sorts of forces going everywhere, but because this is running on the GPU I feel like I have way more limited means of interacting with the particle system at runtime? Or at least it will require a lot more work or at least an alternative approach that I'm not used to. Nothing is intuitive to me straight out of the box at least. :p

    Also in my previous attempt with the original particle system even my own bullets where particles. Is that at all possible here? That is not only having full control over the spawn rate of them, but also detecting collisions with enemies etc. and being able to detect that in script?

    So far I feel like what I'm asking isn't possible without some black magic like plotting the positions and colors for each explosion into textures or signed distance fields or some mumbo jumbo and feeding that into VFX graph. I'm not seeing any way to just tell my particle system to emit particles. I can only play, pause, stop etc. I can't even figure out how I can set the color through script. I've figured out how to set the spawn amount etc. using SetFloat and SetVectorX, but there is no SetColor method so I'm a bit stumped. And I'm not really interested in attempting all the black magic stuff because that would just feel too hacky and not fun at all. :p

    I also tried having a separate game object and VFX graph for each explosion, but that butchers the performance almost independently of the particle count or VFX graph complexity. Just instantiating a lot of VFX graphs in a very short time (dozens of explosions in 1-2 frames) seem to cause major performance spikes. Is it not suited for things like bullet impacts? Is the overhead too high? Or am I doing something wrong here?

    I don't know how to interpret the profiler either as it has changed completely since the last time I've used it, but I'm getting like 2 fps on a 8700K, GTX 1080 Ti. CPU is 300ms per frame according to the stats window and according to the profiler something called the editor loop is the culprit, but there's no way to get any more information about as far as I can tell.
     
    musicdeveloper likes this.
  2. VladVNeykov

    VladVNeykov

    Unity Technologies

    Joined:
    Sep 16, 2016
    Posts:
    550
    Hi @TwiiK ,

    Instead of instantiating multiple VFX graphs, you can use one and spawn particles where needed using event attributes. The idea is that you send which triggers particles to spawn along with attribute data such as the desired position, size, or color.

    As an example, if via code you are sending an event to the VFX graph called "Boom!" and attach to it an event attribute with a desired Position, then in the VFX Graph you can have something like this - an event (Boom!) connected to the spawn context, it will trigger 1 particle, and then in Initialize you need to use Inherit Source Position, which will grab the position you sent via code in the event attribute.
    upload_2020-5-7_19-40-31.png

    This will be one of the limitations; since the particles are calculated on the GPU, they don't work directly with the CPU physics system. If you have a few enemies, you can represent them with collision blocks, or you can collide particles with the depth buffer.

    Hope this helps!
     
    florianhanke likes this.
  3. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,644
    Hello vlad

    It's easy to spawn with events, but still not clear if it's possible to kill a set of particles as consequence of an event.

    Let's say we want to shoot multiple balls of energy that disappear when they hit a wall.
     
  4. VladVNeykov

    VladVNeykov

    Unity Technologies

    Joined:
    Sep 16, 2016
    Posts:
    550
    Hi Seb,
    Not via an event as such - you could add lifetime loss when they hit the wall. Or you can store some per-particle value (say, 0, 1, 2, 3) and compare it to some exposed int you change via script and if your exposed int matches the int of the particle, all particles with than int can be set to Alive = false.
     
    Last edited: May 29, 2020
    florianhanke likes this.
  5. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,644
    Thank you. you confirm that I can store per particle values, however, where do I do the check? I guess in a shader? Is there an example of such a thing?
     
  6. VladVNeykov

    VladVNeykov

    Unity Technologies

    Joined:
    Sep 16, 2016
    Posts:
    550
    First you'll need to enable the Experimental Operators/Blocks


    Then add a Set Custom Attribute block in Initialize and specify its type.


    As an example, here we set a Uint in Initialize (which you can populate with some data you send over an event) and we have a uint exposed in blackboard that you can modify via script. Then in update we are checking if the two match and kill off particles if they do.


    As an example
     

    Attached Files:

    yaawen, sebas77 and florianhanke like this.
  7. sebas77

    sebas77

    Joined:
    Nov 4, 2011
    Posts:
    1,644
    Thank you a lot Vlad. We are still a bit surprised that we can't find a practical application of this in any demo. Even the FPS demo is not using any particle fx that is killed on any kind of event other than the conditions in the particle system itself.
     
    Last edited: Jun 1, 2020
  8. Alaiing

    Alaiing

    Joined:
    Nov 17, 2012
    Posts:
    15
    Hi!

    I tried this, and while the position is indeed passed to the initialization, if I repeat the event several times in the same frame, only the last (or first?) event call seems to be taken into account.
    I'm using ECS to manage lots of enemies and when they burn I want to generate smoke on all of them at the same time and I would like to use only one VisualEffect instance for that. I believe it's close to what @TwiiK wants to do with explosions.
    Here's my code :
    Code (CSharp):
    1.            Entities.WithoutBurst().ForEach((in Translation translation, in CreepComponent creep, in AnimationInstance animationInstance) =>
    2.             {
    3.                 if (animationInstance.fireEffect > 0)
    4.                 {
    5.                     SmokeFarm.SpawnSmoke(translation.Value + creep.selfTargetOffset);
    6.                 }
    7.             }).Run();
    SmokeFarm.SpawnSmoke() does this :
    Code (CSharp):
    1.                 var attribute = Instance._smokeEffect.CreateVFXEventAttribute();
    2.                 attribute.SetVector3("position", position);
    3.                 Instance._smokeEffect.SendEvent("Smoke", attribute);
    So like I said, only one SendEvent() seem to be executed, the smoke only appears on the same enemy, probably the last or first in the ForEach().

    Am I missing something ?

    EDIT : this seems to be an answer to my question https://forum.unity.com/threads/usi...multiple-times-per-frame.825018/#post-5472102
     
    Last edited: Jun 2, 2020
    VladVNeykov and florianhanke like this.