0
Fixed

Get Particles Value cannot be null.

stockingem 1 year ago updated 1 year ago 7

Hi everyone. I looked for solution in existing topics, but this bug reproduces even in fresh empty project with all settings correct (net4 etc)

Every time I connect Particle System variable to Get Particles node — I get this error spam

ArgumentNullException: Value cannot be null.
Parameter name: particles
UnityEngine.ParticleSystem.GetParticles (UnityEngine.ParticleSystem+Particle[] particles, System.Int32 size) (at C:/buildslave/unity/build/Modules/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs:96)
UnityEngine.ParticleSystem.GetParticles (UnityEngine.ParticleSystem+Particle[] particles) (at C:/buildslave/unity/build/Modules/ParticleSystem/ScriptBindings/ParticleSystem.bindings.cs:97)
Ludiq.InstanceFunctionInvoker`3[TTarget,TParam0,TResult].InvokeUnsafe (System.Object target, System.Object arg0) (at C:/Users/lazlo/Projects/Bolt1/Package/Ludiq.Core/Runtime/Reflection/Optimization/InstanceFunctionInvoker_1.cs:51)
Ludiq.InstanceFunctionInvoker`3[TTarget,TParam0,TResult].Invoke (System.Object target, System.Object arg0) (at C:/Users/lazlo/Projects/Bolt1/Package/Ludiq.Core/Runtime/Reflection/Optimization/InstanceFunctionInvoker_1.cs:32)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
Ludiq.InstanceFunctionInvoker`3[TTarget,TParam0,TResult].Invoke (System.Object target, System.Object arg0) (at C:/Users/lazlo/Projects/Bolt1/Package/Ludiq.Core/Runtime/Reflection/Optimization/InstanceFunctionInvoker_1.cs:40)
Ludiq.InstanceFunctionInvoker`3[TTarget,TParam0,TResult].Invoke (System.Object target, System.Object[] args) (at C:/Users/lazlo/Projects/Bolt1/Package/Ludiq.Core/Runtime/Reflection/Optimization/InstanceFunctionInvoker_1.cs:20)
Ludiq.Member.Invoke (System.Object target, System.Object[] arguments) (at C:/Users/lazlo/Projects/Bolt1/Package/Ludiq.Core/Runtime/Reflection/Member.cs:1045)
Bolt.InvokeMember.Invoke (System.Object target, Bolt.Flow flow) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Framework/Codebase/InvokeMember.cs:220)
Bolt.InvokeMember.Enter (Bolt.Flow flow) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Framework/Codebase/InvokeMember.cs:262)
Bolt.Flow.InvokeDelegate (Bolt.ControlInput input) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Flow.cs:447)
Bolt.Flow.Invoke (Bolt.ControlOutput output) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Flow.cs:341)
Bolt.Flow.Run (Bolt.ControlOutput port) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Flow.cs:244)
Bolt.EventUnit`1[TArgs].Run (Bolt.Flow flow) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Framework/Events/EventUnit.cs:179)
Bolt.EventUnit`1[TArgs].Trigger (Ludiq.GraphReference reference, TArgs args) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Framework/Events/EventUnit.cs:150)
Bolt.EventUnit`1+<>c__DisplayClass14_0[TArgs].b__0 (TArgs args) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Flow/Runtime/Framework/Events/EventUnit.cs:69)
Bolt.EventBus.Trigger[TArgs] (Bolt.EventHook hook, TArgs args) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Core/Runtime/Events/EventBus.cs:72)
Bolt.EventMachine`2[TGraph,TMacro].TriggerRegisteredEvent[TArgs] (Bolt.EventHook hook, TArgs args) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Core/Runtime/Events/EventMachine.cs:36)
Bolt.EventMachine`2[TGraph,TMacro].TriggerEvent (System.String name) (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Core/Runtime/Events/EventMachine.cs:14)
Bolt.EventMachine`2[TGraph,TMacro].Update () (at C:/Users/lazlo/Projects/Bolt1/Package/Bolt.Core/Runtime/Events/EventMachine.cs:75)

Bolt Version:
1.4.10
Unity Version:
2019.1.12f1
Platform(s):
Windows
Scripting Backend:
.NET Version (API Compatibility Level):
.NET 4.x

Hello Stockingem,

I'm sorry you're experiencing this issue and I apologize for the late reply on this. You should hear back on this from the lead developer soon.

Best,

Hasan from Ludiq

Need More Information

Hi Stockinghem,

Could you please send a screenshot of your graph?

Sorry for the delay

Escalated

Hi Sotckingem,

Thanks for the screenshot. This does look like a bug indeed, I'll investigate.

Fixed (Unreleased)

Hi Stockingem,

Thanks for the report. I investigated and learned something new!

Unity declares ParticleSystem.GetParticles like this:

public int GetParticles([Out] ParticleSystem.Particle[] particles) { return GetParticles(particles, -1); }

Notice the [Out] attribute, which is necessary for the native P/Invoke interop.

However, this [Out] attribute also makes it so that ParameterInfo.IsOut returns true. Which is what Bolt uses to check whether a parameter has the C# "out" modifier, that guarantees assignment from within the method itself. 

This is a tricky edge case of reflection and the CLR not uniformly implementing the out modifier:

https://stackoverflow.com/questions/30102174/differentiating-between-outattribute-and-out-modifier-in-reflection

In other words, this attribute made Bolt think that the particles parameter was a pure output parameter, whereas in fact, Unity requires an existing array to be passed.

I fixed it by changing my detection code for the "out" modifier to also check for IsByRef:

        // <a href="https://stackoverflow.com/questions/30102174/" class="redactor-autoparser-object">https://stackoverflow.com/questions/30102174/</a>
        public static bool HasOutModifier(this ParameterInfo parameterInfo)
        {
            Ensure.That(nameof(parameterInfo)).IsNotNull(parameterInfo);

            // Checking for IsOut is not enough, because parameters marked with the [Out] attribute
            // also return true, while not necessarily having the "out" modifier. This is common for P/Invoke,
            // for example in Unity's ParticleSystem.GetParticles.
            return parameterInfo.IsOut && parameterInfo.ParameterType.IsByRef;
        }

Funnily enough, Unity's own documentation doesn't differentiate those properly either! In the documentation page, you'll see the particles parameter being falsely tagged as having an out modifier:

https://docs.unity3d.com/2018.4/Documentation/ScriptReference/ParticleSystem.GetParticles.html

Now, getting this particular method to work in Bolt 1 is a bit tricky, because you need to pre-allocate a properly typed array. Here's a guide (that will only work once Bolt has been updated with the fix):

1. Open Tools > Bolt > Unit Options Wizard

2. Add System.Array to your types


3. Add "Particle of Particle System" to your types:


4. Hit Generate and wait

5. In your graph, add Array: Create Instance with a single element type


6. On the Element Type port, pick Particle of Particle System:

7. In the Length port, pick how many max particles you want to retrieve from the particle system.

8. Pass the created array to ParticleSystem.GetParticles

9. Use a For Loop + Get List Item to retrieve each returned particle

In the following example, I log the position of the first 100 particles in the system:

Thank you, Lazlo!