VFX Graph Component does not play on awake.

We are having an issue where some VFX effects simply refuse to Play or listen to events in any manner whether they’re called from code, sent correct events in some specific sets of circumstances that vary wildly depending on how the game is started. Essentially rendering them deadweight.

The weird bit is that toggling said component or object on/off fixes the issue, in player runtime or editor playmode, but automating that to happen as a result of calling it to toggle itself on/off from Unity awake does Not function. Also calling .Play for the effect in runtime does not fix it if this issue occurs, only toggling it on/off does.

So i’ll need to describe our specific situation to ensure all the relevant details for the issue are clear:

We load a single scene first, then we load/unload other scenes additively,
we use crossscene references (though the effects that are glitching out do not use those),
sometimes changing a scene suddenly makes them work again.

edit: Starting from loading a shared globalscene then the actual location scene it works, loading a loadscene then loading the globalscene + location does Not work either in editorplay or built player.

Its not just one effect either but a good portion of them and i’m
wondering if they’ve become corrupted somehow or what is going on here.
The effects always work in editor scene view. We also use Property binders to update some graph component properties, these are also confirmed to be inputting accurate data regardless of whether the effect is glitching out or not.

None of the above are affected in anyway regardless of the type of initial event setup so i dont think the initevents have nothing to do with this issue.

And while i absolutely Love the incredibly easy workflow of Visual Effect Graph,
the behavior regarding playstates etc should also attempt to remain very consistent between runtime/editor.

So is there some feature i’m missing here or have i found a genuine bug and you want a repro?

Hi @JJRivers ,

It’s quite hard to say without looking at your setup, but it’s not a known issue. A few initial things to consider:

  • Are you using events? If so, are the right events being called?
  • If you are not using custom events, have you renamed the initial event (OnPlay) in either the asset or the VFX Component?
  • With all the multi-scene setup, are you losing references of what you are trying to set via code? If I remember correctly, you can’t hold cross-scene references of assets in Unity, so if your VFX are in a different scene from the script calling on them, it might introduce some complications.
  • Are you spawning more particles than your capacity allows?
  • Are the VFX bounds visible by the camera? (you can debug by making the bounds massive, or plugging in the main camera position in the bounds position so they’ll always move with the camera)

If none of these yield any result, please feel free to file a bug with a small repro and we can take a look.

OP’s issue solved in-team: ashamed to admit this was a bug with a custom PropertyBinder and some recent Scene loading setups of ours, so definitely not a bug. :sweat_smile:

Specifically, we had PropertyBinders that were getting their targets set via Script, but the PropertyBinders had a “Update Every Frame In Play Mode” setting that was disabled - hence they had only updated their bound properties values once in their own OnEnable, but as the PropertyBinders were loaded and enabled before their targets were getting set, the targets were never affecting the value output of the PropertyBinders.

This was easily fixed by making a custom forced update for said PropertyBinders every time when new targets were added or removed via code, so they still didn’t even need to update every frame.

@VladVNeykov , thank you anyway for a good troubleshoot checklist - surely of help to people who run into this topic and a good reminder in general. :slight_smile:

We had a similar issue and the fix was the same.

This issue is separate from that however and isnt the same sadly but i applaud you for the brave effort!

Hi, I know this post is kinda old, but I’m having a very similar issue and it’s driving me mad. I’m on Unity 2022.3.26f1 with VFX Graph 14.0.11.

Many of my VFX simply refuse to show up no matter what I do. Only enabling/disabling or manually recompiling them makes them work, but as soon as as I enter/exit PlayMode, they stop working again.
I have a similar setup as OP of using Additive scenes, but I’m not using any property binders.

I went through @VladVNeykov list but I don’t seem to be doing anything wrong. And in fact, if I follow some specific steps, the particles DO show up.

There’s some weird behavior that I notice as well:

  • Particles are usually visible in EditMode, but I’m pretty sure there were times where even in EditMode they didn’t show up
  • They don’t even appear in Builds.
  • Sometimes enabling/disabling it makes them start showing, other times it doesn’t. Sometimes manually recompiling them also makes them show up, other times it doesn’t.
  • It seems “one shot” Visual effects (the ones that are dynamically spawn and automatically triggered) works without any problem. Only VFXs that are pérsistent and that are triggered by code/events doesn’t show up.
  • I’ve tried adding an Output Event after the “Spawn” phase of the system an see if a C# script can listen to it and, surprisingly, it can. This tell me that they ARE listening to events, but for some reason the particles are not rendering.
  • In the same manner, printing the number of alive particles seems to show that there ARE particles alive, but again, nothing is rendering.
  • This seems to be something that was introduced after 14.0.8, because this started happening after I’ve upgraded my Unity version from 2022.3.6f1. Also, if I go back to this version, they seem to work again (didn’t do many tests though).
  • If I start PlayMode, see that the particle is not rendering, but then I pick the particle prefab from the project window and manually place it anywhere in the scene and press play, this particle will work. But weirdly enough, then the next time the event is fired on the already existing particles, they will also work! Almost as if I had forced Unity to load something by manually placing it in the scene.
  • Not sure if related, but it seems that I sometimes get a warning of a missing component when selecting the Visual Effect Game object. I don’t have any missing components on the object, and by the message it seems to be something from the VFX package (only these 2 lines show up)

9792750--1405431--upload_2024-4-25_1-51-9.png

I’m trying to see if I can reproduce the issue on a empty project but it’s late and I’m tired after hours of trying to find why this is happening.

Hi, I just want to give a small update on my problem. While I couldn’t really reproduce this in an empty project yet, I kinda found the precise thing causing the problem, which doesn’t make much sense to me yet, but I can reproduce it 100% of times inside my own project (which is too large and contains too much stuff to be used as a bug report).

Putting it simply, I have a prefab with a script called “AnimationController” that controls things like animation and particles, and also setups some properties on a number of the Visual Effects, like the main Color. I dynamically spawn this prefab when starting the game and after certain events happen in the game, I send events to the VisualEffects to trigger them. My prefab has the following hierarchy:

- Avatar (AnimationController)
    - Particles
        - ParticleA (VisualEffect)
        - ParticleB(VisualEffect)
        - etc.
    - Visual
        - Mesh (Animator)

For some reason that I’m yet to understand, this does not work. When I call particleA.SetFloat4(colorPropertyID, color) to set the particle color, sometimes it works, sometimes it doesn’t, but even if the color gets set, the VFX will no get rendered.

Now for the weird part: if I change the “AnimationController” script from the “Avatar” gameObject to the “Visual” GameObject, it suddently starts working! I didn’t change any code, I didn’t change any reference, and the only public method on this script is a “Setup” method that I call after instantiating and calling GetComponentInChildren(), which sets the color of the particles.

I’ve tried placing the script on different objects in hierarchy and I found out that some always work (like the “Visual” gameObject), but other never work (like the “Avatar” or the “Particles” gameObjects).

Again, i have no idea why this is happening and I’ve tried recreating a similar environment on an Empty project without much success (or failure, in this case), but at least since I kidna know how to reproduce it in my project, I can now change my code/prefabs to circunvent this problem.

This is different problem, but might be good for you to know: https://forum.unity.com/threads/visualeffect-component-prevents-animator-from-being-culled.1537093/#post-9627422

Could you show your script? You swap your AnimationController script position only?
Do I understand this correctly that if you move that controller from Avatar object to Visual it starts to work? Are you sure there is only one controller in this prefab?

Unfortunately I believe it’s an entirely different problem. I’ve removed the Animator and Mesh entirely and the Visual Effects still doesn’t render. And like I said before, if I open the VFX graph, attach the particle and open the “alive” debug graph , it shows the “live count” increasing when I fire the event that triggers the VFX, showing that particles are technically being created.

And yeah, I can show my script. There’s a bunch of things happening but it shouldn’t be too hard to understand.
CharacterAnimationController

using System;
using System.Collections;
using Cinemachine;
using DeiveEx.GameplayTagSystem;
using DeiveEx.Utilities;
using FMOD.Studio;
using FMODUnity;
using Ignix.EventBusSystem;
using Ignix.ServiceLocatorSystem;
using Ignix.TimeScaleSystem;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.VFX;

public class CharacterAnimationController : MonoBehaviour
{  
    #region Fields

    [SerializeField] private Animator _animator;
    [Title("Animation")]
    [ValueDropdown(nameof(GetAnimatorParameterNames))]
    [SerializeField] private int _hSpeedKey;
    [ValueDropdown(nameof(GetAnimatorParameterNames))]
    [SerializeField] private int _vSpeedKey;
    [ValueDropdown(nameof(GetAnimatorParameterNames))]
    [SerializeField] private int _isGroundedKey;
    [ValueDropdown(nameof(GetAnimatorParameterNames))]
    [SerializeField] private int _stompKey;
    [ValueDropdown(nameof(GetAnimatorParameterNames))]
    [SerializeField] private int _isStompingKey;
    [ValueDropdown(nameof(GetAnimatorParameterNames))]
    [SerializeField] private int _isDeadKey;
    [SerializeField] private CinemachineImpulseSource _impulseSource;
    [Title("Particles")]
    [SerializeField] private VisualEffect _walkParticles;
    [SerializeField] private VisualEffect _hitParticle;
    [SerializeField] private VisualEffect _jumpParticle;
    [SerializeField] private VisualEffect _groundStompParticle;
    [SerializeField] private VisualEffect[] _waveReadyParticles;
    [Title("Sounds")]
    [SerializeField] private EventReference _footstepsSound;
    [SerializeField] private EventReference _jumpSound;
    [SerializeField] private EventReference _footstoolSound;
    [SerializeField] private EventReference _damageSound;
    [SerializeField] private EventReference _deathSound;
    [SerializeField] private EventReference _stompSpinSound;
    [SerializeField] private EventReference _stompGroundHitSound;
    [SerializeField] private EventReference _waveReadySound;
    [Title("Tags")]
    [SerializeField, GameplayTag] private string _waveCooldownTag;
    [Title("HitStop")]
    [SerializeField] private float _damageHitStopDuration;
    [SerializeField] private float _deathHitStopDuration;

    private CharacterComponent _controller;
    private float _normalizedStompHeight;

    //Particles
    private readonly int _walkParticlesEnabled = Shader.PropertyToID("EmissionEnabled");
    private readonly int _waveReadyEvent = Shader.PropertyToID("WaveReady");
    private readonly int _waveSpentEvent = Shader.PropertyToID("WaveSpent");
    private readonly int _groundStompEvent = Shader.PropertyToID("OnGroundStomp");
    private readonly int _colorProperty = Shader.PropertyToID("MainColor");
    private readonly int _stompHeightProperty = Shader.PropertyToID("NormalizedSize");
   
    //Materials
    private readonly int _colorPropertyMaterial = Shader.PropertyToID("_MainColor");
   
    //Sounds
    private PARAMETER_ID _stompHeightSoundParameterID;
   
    #endregion

    #region Properties

    public Animator Animator => _animator;
   
    private IEventBus EventBus => ServiceLocator.GetService<IEventBus>();

    #endregion
   
    #region Unity Events

    private void Awake()
    {
        EventBus.Register<EventOnCharacterDamaged>(OnDamaged);
        EventBus.Register<EventOnCharacterDied>(OnDeath);
       
        var stompGroundHitSoundDescription = RuntimeManager.GetEventDescription(_stompGroundHitSound);
        stompGroundHitSoundDescription.getParameterDescriptionByName("fallHeight", out var parameterDescription);
        _stompHeightSoundParameterID = parameterDescription.id;
    }

    private void OnDestroy()
    {
        EventBus.Unregister<EventOnCharacterDamaged>(OnDamaged);
        EventBus.Unregister<EventOnCharacterDied>(OnDeath);
    }

    private void Update()
    {
        if(_controller == null)
            return;
       
        _animator.speed = TimeManager.GetObjectRelativeTimeScale(_controller.gameObject);
       
        if(_controller.IsDead)
            return;
       
        Vector3 hVelocity = _controller.Movement.Velocity;
        float vSpeed = hVelocity.y;
        hVelocity.y = 0;
       
        _animator.SetFloat(_hSpeedKey, hVelocity.magnitude);
        _animator.SetFloat(_vSpeedKey, Mathf.InverseLerp(-_controller.Movement.JumpForce, _controller.Movement.JumpForce, vSpeed) * 2 - 1);
        _animator.SetBool(_isGroundedKey, _controller.Movement.IsGrounded);
        _animator.SetBool(_isStompingKey, _controller.Movement.IsStomping);
       
        _walkParticles.SetBool(_walkParticlesEnabled, _controller.Movement.IsGrounded && !_controller.Movement.IsStomping);
    }
   
    #endregion

    #region Public Methods

    public void Setup(CharacterComponent controller)
    {
        _controller = controller;
       
        _controller.Movement.onJump += OnJump;
        _controller.Movement.onFootstoolJump += OnFootstoolJump;
        _controller.Movement.onStompSpin += OnStompSpin;
        _controller.Movement.onStompGroundHit += OnStompGroundHit;
        _controller.Tags.tagChanged += OnTagChanged;
       
        var renderers = GetComponentsInChildren<Renderer>();

        foreach (var avatarRenderer in renderers)
        {
            avatarRenderer.material.SetColor(_colorPropertyMaterial, _controller.MatchPlayerInfo.AvatarInfo.PrimaryColor);
        }
       
        foreach (var particle in _waveReadyParticles)
        {
            //Since there's no "SetColor" methods, we need to use "SetVector4"
            particle.SetVector4(_colorProperty, _controller.MatchPlayerInfo.AvatarInfo.PrimaryColor);
        }
    }

    public void StartStompFall()
    {
        _controller.Movement.StarStompFall();
    }

    public void OnFootStep()
    {
        RuntimeManager.PlayOneShot(_footstepsSound);
    }

    #endregion
   
    #region Private Methods

    private IEnumerable GetAnimatorParameterNames()
    {
        ValueDropdownList<int> parameterNames = new() {0};

        if (_animator != null)
        {
            foreach (var animatorParameter in _animator.parameters)
            {
                parameterNames.Add(animatorParameter.name, Animator.StringToHash(animatorParameter.name));
            }
        }

        return parameterNames;
    }

    private void SendEventToParticles(int eventID, params VisualEffect[] particles)
    {
        foreach (var particle in particles)
        {
            particle.SendEvent(eventID);
        }
    }
   
    private void OnJump(object sender, EventArgs e)
    {
        _jumpParticle.Play();
        RuntimeManager.PlayOneShot(_jumpSound);
    }
   
    private void OnFootstoolJump(object sender, EventArgs e)
    {
        _jumpParticle.Play();
        RuntimeManager.PlayOneShot(_footstoolSound);
    }
   
    private void OnStompSpin(object sender, EventArgs e)
    {
        _animator.SetTrigger(_stompKey);

        _normalizedStompHeight = Mathf.Clamp01(transform.position.y / _controller.Movement.JumpHeight);
        _groundStompParticle.SetFloat(_stompHeightProperty, _normalizedStompHeight);
       
        //Sound
        var spinSound = RuntimeManager.CreateInstance(_stompSpinSound);
        spinSound.start();
        spinSound.release();
    }
   
    private void OnStompGroundHit(object sender, EventArgs e)
    {
        _impulseSource.GenerateImpulse();
        SendEventToParticles(_waveSpentEvent, _waveReadyParticles);
        SendEventToParticles(_groundStompEvent, _groundStompParticle);
       
        //Sound
        var spinSound = RuntimeManager.CreateInstance(_stompGroundHitSound);
        spinSound.setParameterByID(_stompHeightSoundParameterID, _normalizedStompHeight * 2); //TODO this should be normalized...
        spinSound.start();
        spinSound.release();
    }
   
    private void OnTagChanged(object sender, GameplayTagChangedEventArgs e)
    {
        if(_controller.IsDead)
            return;
       
        if (e.tag.FullTagName == _waveCooldownTag && e.eventType == GameplayTagChangedEventType.Removed)
        {
            SendEventToParticles(_waveReadyEvent, _waveReadyParticles);
            RuntimeManager.PlayOneShot(_waveReadySound);
        }
    }
   
    private async void OnDamaged(EventOnCharacterDamaged args)
    {
        if(args.Character != _controller)
            return;
       
        _impulseSource.GenerateImpulse();
        _hitParticle.Play();
        RuntimeManager.PlayOneShot(_damageSound);
       
        TimeManager.SetObjectRelativeTimeScale(_controller.gameObject, 0);
        await ThreadingUtility.Delay((int)(_damageHitStopDuration * 1000));
        TimeManager.SetObjectRelativeTimeScale(_controller.gameObject, 1);
    }
   
    private async void OnDeath(EventOnCharacterDied args)
    {
        if(args.Character != _controller)
            return;
       
        _impulseSource.GenerateImpulse();
        _animator.SetTrigger(_isDeadKey);
        RuntimeManager.PlayOneShot(_deathSound);
       
        TimeManager.SetObjectRelativeTimeScale(_controller.gameObject, 0);
        await ThreadingUtility.Delay((int)(_deathHitStopDuration * 1000));
        TimeManager.SetObjectRelativeTimeScale(_controller.gameObject, 1);
    }
   
    #endregion
}

Yep, if I remove the script from the Avatar GameObject and add it to the Visual GameObject and copy/assign the exactly same values, then all VisualEffects suddenly start to render again.

Also, I don’t think I’ve mentioned this, but only particles inside this prefab doesn’t render. Other particles in other places render without problem, but even if I instantiate multiple instances of the Avatar prefab, none of them render the particles until I manually interact with it (like enabling and disabling it, or recompiling it)

Ok, I’ve been commenting multiple sections of the code and I believe I’ve finally figured out the problem, and it’s kinda my fault but I think it might ALSO be a bug?

Long story short, on line 136 I’m calling GetComponentsInChildren<Renderer>(); to get all renderers in order to set the color of the Character Mesh, but apparently the VisualEffect component is also considered a Renderer (even though it doesn’t inherit from Renderer), and it seems like I was calling renderer.material.SetColor() on it too, and THAT is what was making the VisualEffect not render! And also, that’s why chaning the script to the Visual GameObject worked: because of how the hierarchy is setup, putting the script there made it so it wouldn’t get the VisualEffect as renderers anymore!

Now, why I think this is still a bug:

  • Should VisualEffect be a Renderer?
  • I didn’t even know we could access the VisualEffect material like this
  • I was setting the Material “_MainColor” property to an actual color (e.g. green). Now, as far as I know, none of the Unity Shaders has a property called “_MainColor”, so calling “SetColor” on a property that doesn’t exist shouldn’t do anything, right?
  • Some of my VFX assets are using a custom ShaderGraph that DOES have an exposed Property called “_MainColor”, so it’s a bit weird that calling “SetColor” on it with an actual color would make it not render.
  • If I play the particle BEFORE calling “SetColor”, it plays as normal all the times. But if I call “SetColor” BEFORE playign the particle, it won’t render.
  • It literally stops rendering. This sounds like a bug to me.

So, yeah, I was also finally able to reproduce this on a empty project, so I’m gonna fill a bug report.

You are a bit wrong, the VisualEffect is not considered as Renderer, however it creates hidden component (VFXRenderer or something like that) and indeed it inherits from Renderer.

Material of this renderer should not be controlled by external scripts as it breaks the vfx or produces strange results.
If you want to keep your hierarchy as it is then an easy fix is to simply add ‘if’ inside your loop to check if renderer is VFXRenderer and skip those.

Ah, I see, I didn’t consider hidden components. Still, it feels like a bug, specially since it works on certains conditions but it doesn’t on others. And, I mean, how would I even know the problem was a HIDDEN component? I’ve created a bug report, so let’s see what Unity thinks of it.

But yeah, now that I know the precise line causing this, it was easy to fix it.

I mean for me this design is also a bit … strange? but this is how it’s made right now and I doubt it will be changed now, so expect something like “by design”. I agree however, it should be described in documentation.

Also I think you will get input from developers here anyway.