Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only.

    Please, do not make any changes to your username or email addresses at id.unity.com during this transition time.

    It's still possible to reply to existing private message conversations during the migration, but any new replies you post will be missing after the main migration is complete. We'll do our best to migrate these messages in a follow-up step.

    On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live.


    Read our full announcement for more information and let us know if you have any questions.

Question How to trigger a particle death ?

Discussion in 'Visual Effect Graph' started by Guema, Sep 10, 2023.

  1. Guema

    Guema

    Joined:
    Jun 21, 2015
    Posts:
    8
    Hey.

    I am playing around with VFX graph for a while now, and something i can't figure out to do properly is triggering a specific particle death on event triggered.

    To explain my case, i'm trying to make a customizable projectile vfx, and for that i would like to embark the whole life loop of it in a single vfx graph.

    I would like to handle birth (projectile life beginning), stand (the projectile is looping a generation) and death (projectile vanish with an eventual explosion or flash) states.

    It work correctly when i use only continuous generation of aging particles but i want to be able to manage a case where the head is a single, non-aging particle, something very basic, like the "bullet itself". I have a very retro style fireball which its head is just a sphere, for exemple.

    Generating it without aging is quite easy but i can't find a proper way to kill it on Cpu event.

    Something I explored is using the properties, but it's not satisfying, as exposed values are systematically put in the inspector... I obviously don't want that because this is not something meant to be configured and customizable, it's a runtime value meant to be managed by a script that manage the gameplay part of the projectile. However, non-exposed ones are just impossible to access with the code. This is quite stupid.

    I then did a large tour on what i could do with attributes, but with all of them being instantiated on particle generation there is nothing i could find for now to propagate the death event into a bool value that would disable the alive state of the head particle.

    I can't find a real proper solution to my problem. And i don't really understand why so few people seem to encounter it, as i feel like this is an obvious use case.

    I already asked this question on the forum and i'm quite surprised it has no evident answer.
     
  2. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    I think the only way to do this is to expose the boolean property or int property "frameIndex" and compare it inside update to kill particle when it's set to proper value (I think this is what you mentioned already).
    I am also missing this feature, it would be nice to access unexposed variables.

    Possibly there is more elegant way, but it requires sending event, so I am not sure we could call it elegant (assuming it works). You could send event with attributes to set frame of death and acces this value with get xyz with mode set to source and then compare in update. Also with custom HLSL maybe you could use some buffer or something else externally exposed to set some flag to kill particles.

    In general I am also interested in this problem.
     
  3. Guema

    Guema

    Joined:
    Jun 21, 2015
    Posts:
    8
    Yup as i said

    Yup, as i said it work with the exposed property but it's not a value meant to be exposed in the inspector. The reason why i want to hide this is that it should not be edited : it's a value meant to be used only by script (or animation graph eventually), not manually edited.

    For the second part, i tested this very much too.
    But actually, it's impossible. Every single event attribute will only be used in the event, spawn and init part. After this, when the particle is actually created, all the values are snapshot and you can't access it from event part.
    I tried to make 2 different events that modify a bool attribute and make this attribute the alive value in update.
    The "OnPlay" Event leaded to a Spawn that made this value true.
    The "OnStop" Event leaded to a Spawn that made this value false.

    Then this two where connected to the same System. I was expecting that a GetAttribute (in Source mode) was going to retrieve the value, modified by the last event called, but no. As soon as OnPlay triggered and the particle was emit, it snapshoted its own generation context and when some algorithm refer to a source value, from update to output, it will always use the attribute from the snapshot.

    A custom HLSL will not work for what i want to do, because it only act on the render part.
    There is actually a detail i voluntarily omit because it is out of range : I plan to use the active particle count to know when the GamObject can be destroyed (or stored for reuse)
    This will allow the Projectile to play its entire death FX before it is disposed.

    A custom Shader would work to totally suppress it visually, but the particle will actually stay, so not ideal. Could compare with the last one through... Maybe i'll investigate. Unfortunately i'm not really good at writing shaders, mainly because how verbose and low level they are, even if i think i understand the concept well. This is a very expensive solution for me right now, so i would prefer not.
     
  4. OrsonFavrel

    OrsonFavrel

    Unity Technologies

    Joined:
    Jul 25, 2022
    Posts:
    255
    Morning. So at the moment, you cannot access unexposed variables. And while you could destroy the VFX thought Script, I guess that you might want to keep some Systems within your Graph. So I would still suggest to use exposed properties. While it's true that they're going to appear in your VFXG instance inspector, you can do severals things to mitigate the risk of others people messing with it.

    • Use a Prefix:
      ex:"DoNotTouch" _myExposedProperties
      This could indicate your team member that they shouldn't mess around with this property.

    • Set a Tooltip:
      ex: "This property shouldn't be touched in editor, as it's only supposed to be accessed thought Script.
    • Put this Property in a Category:
      All Properties can be put into Category to organize and clean how your properties appears in your inspector.
      By putting your property into a "doNotTouch/Hide" category, you could collapse it so that those properties inside are less visible/discoverable.

    • upload_2023-9-11_11-22-32.png upload_2023-9-11_11-22-6.png

    Now, as @PaulDemeulenaere mentioned me, an elegant solution could be to use a Boolean property linked to a Boolean port to decide when to "Age" the particles.
    By default, this is done implicitly by the Update Context. The Update Context implicitly “Age Particles” and “Reap Particles” when the Age attribute values exceed the Lifetime.

    So in your case, you could still set up a Lifetime in the "Init Context" for your Head projectiles. And make sure to Uncheck the “Age particles” in the Update Context. From here you can add an “Add Age” Block with a Boolean port linked to a Boolean Property. When Turn ON, the Age attribute will be updated and your particles will die after exceeding your lifetime.
    This can be useful if you want to do stuff on your particles before that they die (Changing color, growing in size etc...)
    I know that it's not exactly what you wanted to hear, but I hope that it will still be of some help.
    upload_2023-9-11_11-41-22.png

    Have a great day.
     
  5. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    I also use category with name like "Script driven properties" or "Don't touch" - well it works, but it is far from elegant.

    What do you mean by render part? I meant here VFX HLSL (custom block), although this is only available in the latest version. However, actually to sample buffer you don't need custom block, you could just sample buffer of size 1 with constant index 0 and use that value.