Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Fluid Particles interaction Script
#21
Exclamación 
(23-11-2020, 01:56 PM)josemendez Wrote: Hi there,

If you run this script, it will result in a NullRefException error (visible in the console), and the script will halt execution.

This is because of this line:

Code:
emitter.speed = 0;

"emitter" will always be null because you only require a component of type ObiSolver to be present:
Code:
[RequireComponent(typeof(ObiSolver))]

But then you try to retrieve a ObiEmitter component, which is more than likely to be absent. So this call to GetComponent will return null:
Code:
emitter = GetComponent<ObiEmitter>();

Easiest fix is to just make the emitter member public, then assign the emitter instance trough the inspector:

Code:
using UnityEngine;
using Obi;

[RequireComponent(typeof(ObiSolver))]
public class KillFluidOnContact : MonoBehaviour
{
    ObiSolver solver;
    public ObiEmitter emitter;
    public float targetTime = 2.0f;

    void Awake()
    {
        solver = GetComponent<ObiSolver>();
    }

    private void Update()
    {
        targetTime -= Time.deltaTime;

        if (targetTime <= 0.0f)
        {
            timerEnded();
        }

    }

    void timerEnded()
    {
        if (emitter != null)
            emitter.speed = 0;
        else Debug.LogWarning("No se ha proporcionado un emisor a este script.");
    }


    void LateUpdate()
    {

        if (!isActiveAndEnabled)
            return;

        // iterate over all particles in the solver, looking for particles
        // that have had their diffusion data altered by contact with other particles.
        for (int i = 0; i < solver.particleToActor.Length; ++i)
        {
            // if this particle is part of an actor,
            if (solver.particleToActor[i] != null)
            {
                // if the "user" (aka diffusion) data is not either (1,0,0,0) or (0,0,0,0)
                if (solver.userData[i].x < 0.9f && solver.userData[i].x > 0.1f)
                {
                    // take a reference to the emitter that the particle belongs to, and kill it.
                    var emitter = solver.particleToActor[i].actor as ObiEmitter;
                    emitter.KillParticle(solver.particleToActor[i].indexInActor);
                }
            }
        }

    }
}

This is basics of the basics, tough. You should train your programming skills a bit further Guiño.


It keeps spawning, when all particles are destroyed. Doesn´t seem to change emitter speed... Yeah, this is my training! Gran sonrisa
Reply
#22
(23-11-2020, 03:01 PM)JoseCarlos S Wrote: It keeps spawning, when all particles are destroyed. Doesn´t seem to change emitter speed...

It does stop after 2 seconds for me :S.

Did you make sure to set the emitter reference by dragging the emitter from the scene into the inspector? If there's no emitter, the script will do nothing and will instead spam a warning to the console.

(23-11-2020, 03:01 PM)JoseCarlos S Wrote: Yeah, this is my training! Gran sonrisa

Fluid simulation is quite complex, and quite hard to work with for someone that's starting out. All Obi assets in fact are aimed at advanced users, so this is kinda like practicing to get your driver's license on a McLaren F1 instead of a Fiat Punto. Not meant to discourage you, but be prepared for a really steep learning curve!
Reply
#23
(23-11-2020, 03:13 PM)josemendez Wrote: It does stop after 2 seconds for me :S.

Did you make sure to set the emitter reference by dragging the emitter from the scene into the inspector? If there's no emitter, the script will do nothing and will instead spam a warning to the console.


Fluid simulation is quite complex, and quite hard to work with for someone that's starting out. All Obi assets in fact are aimed at advanced users, so this is kinda like practicing to get your driver's license on a McLaren F1 instead of a Fiat Punto. Not meant to discourage you, but be prepared for a really steep learning curve!


Yep, the correct emitter is asigned, no alerts on console, and still keeps spawning new particles...

Yeah, i know is gonna be hard learning, but afterwards ill drive the Fiat like a pro Lengua
Reply
#24
(23-11-2020, 04:32 PM)JoseCarlos S Wrote: Yep, the correct emitter is asigned, no alerts on console, and still keeps spawning new particles...

I just realized that you were using Burst emission mode, which won't stop emitting even if speed is zero. I was testing with Stream mode instead.

Open ObiEmitter.cs, and change line 467 from this:

Code:
if (activeParticleCount == 0)

to this:

Code:
if (activeParticleCount == 0 && speed > 0)

That will make Burst emission stop when speed is set to zero.
Reply
#25
(23-11-2020, 05:09 PM)josemendez Wrote: I just realized that you were using Burst emission mode, which won't stop emitting even if speed is zero. I was testing with Stream mode instead.

Open ObiEmitter.cs, and change line 467 from this:

Code:
if (activeParticleCount == 0)

to this:

Code:
if (activeParticleCount == 0 && speed > 0)

That will make Burst emission stop when speed is set to zero.


Hi, yes that seemed to fix the issue! With that i have all my interactions between particles solved (unless i decide to change or add to the main mechaninc). But for now my last question is about particle collision with objects/player. Where do i have to put my script? On the object or the fluid? Collision is detected between particles and objects? I found this in an older post in here ;

Code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Obi;

[RequireComponent(typeof(ObiSolver))]
public class CollisionEventHandler : MonoBehaviour {

    ObiSolver solver;

   public Collider killer;

    void Awake(){
        solver = GetComponent<Obi.ObiSolver>();
    }

    void OnEnable () {
        solver.OnCollision += Solver_OnCollision;
    }

    void OnDisable(){
        solver.OnCollision -= Solver_OnCollision;
    }
    
    void Solver_OnCollision (object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        for(int i = 0;  i < e.contacts.Length; ++i)
       {
           if (e.contacts[i].distance < 0.001f)
           {

               Component collider;
               if (ObiCollider.idToCollider.TryGetValue(e.contacts[i].other,out collider)){

                   if (collider == killer){

                       ObiSolver.ParticleInActor pa = solver.particleToActor[e.contacts[i].particle];
                       ObiEmitter emitter = pa.actor as ObiEmitter;

                       if (emitter != null)
                           emitter.life[pa.indexInActor] = 0;
                       
                   }
               }
           }
       }
   }
}


I´m looking for a script that works the other way round, that destroys the object instead of the particles. Any help?
Big thanks!
Reply
#26
Hi Jose Carlos,
Just call GameObject.Destroy() passing the object reported by the contact.

The manual describes how to retrieve the collider involved in a contact:
http://obi.virtualmethodstudio.com/tutor...sions.html

So the code would look like this:

Code:
ObiColliderBase collider = ObiColliderWorld.GetInstance().colliderHandles[contact.other].owner;
GameObject.Destroy(collider.gameObject);

The script you posted pretty clearly hints at where to place it Guiño:
[RequireComponent(typeof(      ObiSolver      ))]
Reply
#27
(27-11-2020, 09:04 AM)josemendez Wrote: Hi Jose Carlos,
Just call GameObject.Destroy() passing the object reported by the contact.

The manual describes how to retrieve the collider involved in a contact:
http://obi.virtualmethodstudio.com/tutor...sions.html

So the code would look like this:

Code:
ObiColliderBase collider = ObiColliderWorld.GetInstance().colliderHandles[contact.other].owner;
GameObject.Destroy(collider.gameObject);

The script you posted pretty clearly hints at where to place it Guiño:
[RequireComponent(typeof(      ObiSolver      ))]

Hi, found this script in the API, but it dosnt seem to read my variables ... Any help?:


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Obi;

[RequireComponent(typeof(ObiSolver))]
public class Collision2 : MonoBehaviour
{

    ObiSolver solver;
    public ObiCollider2D collider;

    Obi.ObiSolver.ObiCollisionEventArgs collisionEvent;

    void Awake()
    {
        solver = GetComponent<Obi.ObiSolver>();
    }

    void OnEnable()
    {
        solver.OnCollision += Solver_OnCollision;
    }

    void OnDisable()
    {
        solver.OnCollision -= Solver_OnCollision;
    }

    void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        var world = ObiColliderWorld.GetInstance();
        foreach (Oni.Contact contact in e.contacts)
        {
            // this one is an actual collision:
            if (contact.distance < 0.01)
            {
                ObiColliderBase collider = world.colliderHandles[contact.other].owner;
                if (collider != null)
                {
                    ObiColliderBase collider = ObiColliderWorld.GetInstance().colliderHandles[contact.other].owner;
                    GameObject.Destroy(collider.gameObject); // do something with the collider.
                }
            }
        }
    }

}
Reply
#28
(27-11-2020, 02:38 PM)JoseCarlos S Wrote:             // this one is an actual collision:
            if (contact.distance < 0.01)
            {
                ObiColliderBase collider = world.colliderHandles[contact.other].owner;
                if (collider != null)
                {
                    ObiColliderBase collider = ObiColliderWorld.GetInstance().colliderHandles[contact.other].owner;
                    GameObject.Destroy(collider.gameObject); // do something with the collider.
                }
            }

This won't compile because you're defining "collider" twice. Fixing it is left as an exercise to the reader Guiño (you should really be able to fix simple things as these, but let me know if you struggle too much with it).
Reply
#29
(27-11-2020, 02:53 PM)josemendez Wrote: This won't compile because you're defining "collider" twice. Fixing it is left as an exercise to the reader Guiño (you should really be able to fix simple things as these, but let me know if you struggle too much with it).


Okay, i did this one, no compilation errors, put the script in the solver. But insted of only killing the player when it touches the fluid, it destroys a lot of blocks of terrain right from the start (wich have the obicollider2d). Why is that?


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Obi;

[RequireComponent(typeof(ObiSolver))]
public class Collision2 : MonoBehaviour
{

    ObiSolver solver;
    public ObiCollider2D player;
   

    Obi.ObiSolver.ObiCollisionEventArgs collisionEvent;

    void Awake()
    {
        solver = GetComponent<Obi.ObiSolver>();
       
    }

    void OnEnable()
    {
        solver.OnCollision += Solver_OnCollision;
    }

    void OnDisable()
    {
        solver.OnCollision -= Solver_OnCollision;
    }

    void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {

        var world = ObiColliderWorld.GetInstance();
        foreach (Oni.Contact contact in e.contacts)
        {
            // this one is an actual collision:
            if (contact.distance < 0.01)
            {
               
                if (player != null)
                {
                    ObiColliderBase collider = ObiColliderWorld.GetInstance().colliderHandles[contact.other].owner;
                    GameObject.Destroy(collider.gameObject); // do something with the collider.
                }
            }
        }
    }

}
Reply
#30
You're not checking if the collider reported by the contact is the player, you're merely checking if the player exists. Since this is probably always true, any collider relatively close to the fluid will be destroyed.

Correct solution:

Code:
void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        var world = ObiColliderWorld.GetInstance();
        foreach (Oni.Contact contact in e.contacts)
        {
            // this one is an actual collision:
            if (contact.distance < 0.01)
            {
                ObiColliderBase collider = world.colliderHandles[contact.other].owner;
                if (collider == player)
                        GameObject.Destroy(collider.gameObject);
            }
        }
    }
Reply