Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Check ropes touching each other
#1
Pregunta 
How can I check if two or more ropes are touching each other? As a result of this contact, there will be a flexibility exchange that I want to do.
Reply
#2
(27-11-2021, 02:20 PM)greyhawk Wrote: How can I check if two or more ropes are touching each other? As a result of this contact, there will be a flexibility exchange that I want to do.


Hi there!

Just use collision callbacks. However instead of subscribing to OnCollision (which returns simplex-collider contacts) subscribe to OnParticleCollision (which returns simplex-simplex contacts). See:

http://obi.virtualmethodstudio.com/manua...sions.html

Then for each contact follow the steps in "Retrieving the actor involved in a contact" for both bodyA and bodyB in the contact. This way you'll get the two ropes involved in the contact.
Reply
#3
(29-11-2021, 08:13 AM)josemendez Wrote: Hi there!

Just use collision callbacks. However instead of subscribing to OnCollision (which returns simplex-collider contacts) subscribe to OnParticleCollision (which returns simplex-simplex contacts). See:

http://obi.virtualmethodstudio.com/manua...sions.html

Then for each contact follow the steps in "Retrieving the actor involved in a contact" for both bodyA and bodyB in the contact. This way you'll get the two ropes involved in the contact.

So how do we realize that the contact has ended? just like onCollisionExit
Reply
#4
(30-11-2021, 02:09 PM)greyhawk Wrote: So how do we realize that the contact has ended? just like onCollisionExit

Hi!

Simple: when the contact no longer appears in the list.

You can compare the previous frame's contact list and the current contact list to determine enter, stay and exit events. This is what the ObiContactEventDispatcher utility component does for regular -collider- contacts. You can do the same for particle contacts too.
Reply
#5
(30-11-2021, 02:28 PM)josemendez Wrote: Hi!

Simple: when the contact no longer appears in the list.

You can compare the previous frame's contact list and the current contact list to determine enter, stay and exit events. This is what the ObiContactEventDispatcher utility component does for regular -collider- contacts. You can do the same for particle contacts too.
I am getting the following error. What am I doing wrong?
"ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.ThrowHelper.ThrowArgumentOutOfRangeException () (at <695d1cc93cca45069c528c15c9fdd749>:0)
_Game.Scripts.Entities.Rope.RopeCutter.Solver_OnParticleCollision (System.Object sender, Obi.ObiSolver+ObiCollisionEventArgs e) (at Assets/_Game/Scripts/Entities/Rope/RopeCutter.cs:95)
Obi.ObiSolver.EndStep (System.Single substepTime) (at Assets/Obi/Scripts/Common/Solver/ObiSolver.cs:1598)
Obi.ObiUpdater.EndStep (System.Single substepDeltaTime) (at Assets/Obi/Scripts/Common/Updaters/ObiUpdater.cs:92)
Obi.ObiFixedUpdater.FixedUpdate () (at Assets/Obi/Scripts/Common/Updaters/ObiFixedUpdater.cs:52)
"

Code:
void Solver_OnParticleCollision(object sender, ObiSolver.ObiCollisionEventArgs e)
        {
            var world = ObiColliderWorld.GetInstance();

            // just iterate over all contacts in the current frame:
            foreach (Oni.Contact contact in e.contacts)
            {
                // if this one is an actual collision:
                Debug.Log(contact.bodyB);
                Debug.Log(contact.bodyA);
                ObiColliderBase colB = world.colliderHandles[contact.bodyB].owner;
                ObiColliderBase colA = world.colliderHandles[contact.bodyA].owner;
                if (colA != null && colB != null)
                {
                    int particleIndexA = solver.simplices[contact.bodyA];
                    int particleIndexB = solver.simplices[contact.bodyB];
// retrieve the actor this particle belongs to:
                    ObiSolver.ParticleInActor paA = solver.particleToActor[particleIndexA];
                    ObiSolver.ParticleInActor paB = solver.particleToActor[particleIndexB];
                    var actorA = paA.actor as ObiRope;
                    var actorB = paB.actor as ObiRope;
                    actorA.GetComponent<RopeController>().CheckContact(actorB);
                }
            }
        }

Code:
[SerializeField] public List<ObiRope> contactList;
        private bool justOnce;
        float t=0;
       
        private void Update()
        {
            t += Time.deltaTime;
            if (t > 10)
            {
                if (contactList.Count>0)
                {
                    contactList.Clear();
                    justOnce = true;
                }
                else
                {
                    _signalBus.Fire(new RopeUncontactSignal());
                    justOnce = true;
                }
                t = 0;
            }
        }

        public void CheckContact(ObiRope rope)
        {
            foreach (var contact in contactList)
            {
                if (contact==rope)
                {
                    return;
                }
                contactList.Add(rope);
                if (justOnce)
                {
                    _signalBus.Fire(new RopeContactSignal());
                    justOnce=false;
                }
            }
        }
Reply
#6
(30-11-2021, 04:36 PM)greyhawk Wrote: I am getting the following error. What am I doing wrong?

Hi!

Contacts can be between a simplex and a collider, or between two simplices. If you subscribe to solver.OnCollision, contact.bodyA will always be a simplex index and contact.bodyB will always be a collider index. See: http://obi.virtualmethodstudio.com/manua...sions.html

Quote:BodyA: Index of the first simplex involved in the contact.

BodyB: Index of the collider / second simplex involved in the contact. In case of simplex-collider contacts, this index can be used to retrieve the actual collider object, see below.

If you subscribe to solver.OnParticleCollision, both bodyA and bodyB will always be simplex indices. For some reason, in your code you're using bodyA and bodyB to try and access the global collider array:

Code:
ObiColliderBase colB = world.colliderHandles[contact.bodyB].owner;
ObiColliderBase colA = world.colliderHandles[contact.bodyA].owner;

Which will almost always cause an out of range exception.

To understand why, think about what would happen if you had 3 colliders and 10 simplices in your scene: if simplices 5 and 9 collide with each other, you will get a contact such that bodyA is 5 and bodyB is 9. You then try to access positions 5 and 9 in the world.colliderHandles array that only has 3 entries (because there's only 3 colliders in the scene), so you're essentially asking Unity to access data that doesn't exist ----> the error is trying to tell you that the index used to access that array is out of range: to access an array of size 3, you can only use indices 0,1 and 2.

This code deals with simplex-simplex collisions, so no need to check for colliders.

Code:
foreach (Oni.Contact contact in e.contacts)
{
               
     int particleIndexA = solver.simplices[contact.bodyA];
     int particleIndexB = solver.simplices[contact.bodyB];

// retrieve the actor this particle belongs to:
     ObiSolver.ParticleInActor paA = solver.particleToActor[particleIndexA];
     ObiSolver.ParticleInActor paB = solver.particleToActor[particleIndexB];
     var actorA = paA.actor as ObiRope;
     var actorB = paB.actor as ObiRope;
     actorA.GetComponent<RopeController>().CheckContact(actorB);
}
Reply
#7
(01-12-2021, 08:32 AM)josemendez Wrote: Hi!

Contacts can be between a simplex and a collider, or between two simplices. If you subscribe to solver.OnCollision, contact.bodyA will always be a simplex index and contact.bodyB will always be a collider index. See: http://obi.virtualmethodstudio.com/manua...sions.html


If you subscribe to solver.OnParticleCollision, both bodyA and bodyB will always be simplex indices. For some reason, in your code you're using bodyA and bodyB to try and access the global collider array:

Code:
ObiColliderBase colB = world.colliderHandles[contact.bodyB].owner;
ObiColliderBase colA = world.colliderHandles[contact.bodyA].owner;

Which will almost always cause an out of range exception.

To understand why, think about what would happen if you had 3 colliders and 10 simplices in your scene: if simplices 5 and 9 collide with each other, you will get a contact such that bodyA is 5 and bodyB is 9. You then try to access positions 5 and 9 in the world.colliderHandles array that only has 3 entries (because there's only 3 colliders in the scene), so you're essentially asking Unity to access data that doesn't exist ----> the error is trying to tell you that the index used to access that array is out of range: to access an array of size 3, you can only use indices 0,1 and 2.

This code deals with simplex-simplex collisions, so no need to check for colliders.

Code:
foreach (Oni.Contact contact in e.contacts)
{
               
     int particleIndexA = solver.simplices[contact.bodyA];
     int particleIndexB = solver.simplices[contact.bodyB];

// retrieve the actor this particle belongs to:
     ObiSolver.ParticleInActor paA = solver.particleToActor[particleIndexA];
     ObiSolver.ParticleInActor paB = solver.particleToActor[particleIndexB];
     var actorA = paA.actor as ObiRope;
     var actorB = paB.actor as ObiRope;
     actorA.GetComponent<RopeController>().CheckContact(actorB);
}


Thanks, i have another problem now.

It's rare, but it says that even if I pull the strings away from each other, they are in contact with each other. not sure, but this happens after partial thinning after another contact. I don't know if it's relevant. Here's the code I'm using right now:

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

namespace _Game.Scripts.Entities.Rope
{
    [RequireComponent(typeof(ObiSolver))]
    public class RopeCutter : MonoBehaviour
    {
        private ObiSolver solver;
        public GameObject[] damagezone;
       
        void Awake(){
            solver = GetComponent<ObiSolver>();
        }

       

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

        void OnDisable(){
            solver.OnCollision -= Solver_OnCollision;
            solver.OnParticleCollision -= Solver_OnParticleCollision;
        }
        void Solver_OnCollision(object sender, ObiSolver.ObiCollisionEventArgs e)
        {
            var world = ObiColliderWorld.GetInstance();

            // just iterate over all contacts in the current frame:
            foreach (Oni.Contact contact in e.contacts)
            {
                // if this one is an actual collision:

                ObiColliderBase col = world.colliderHandles[contact.bodyB].owner;
                if (col != null)
                {
                   // if (col.CompareTag("saw"))
                    //{
                        //TearRope(col, contact);
                    //}
                    if (col.CompareTag("saw"))
                    {
                        DamageRope(contact);
                    }

                }
            }
        }

        private void DamageRope(Oni.Contact contact)
        {
// get the particle index:
            int particleIndex = solver.simplices[contact.bodyA];

// retrieve the actor this particle belongs to:
            ObiSolver.ParticleInActor pa = solver.particleToActor[particleIndex];
            var actor = pa.actor as ObiRope;

// not a rope, stop:
            if (actor == null) return;

            if (actor.GetComponent<RopeController>().GetCanDamage())
            {
                StartCoroutine(actor.GetComponent<RopeController>().SetCanDamage());
                float i = -1;
// check rope elements and tear the one that references this particle:
                foreach (var elm in actor.elements)
                {
                    i++;
                    if (elm.particle1 == particleIndex)
                    {
                        if (actor.GetComponent<RopeController>().run)
                        {
                            var obj = Instantiate(damagezone[actor.GetComponent<RopeController>().GetRopeColorIndex()]);
                            obj.GetComponent<TearedRope>().SetTearedRope(actor, solver, elm, i);
                        }
                        //actor.Tear(elm);
                        break;
                    }
                }
            }
        }

       
        private void Solver_OnParticleCollision(object sender, ObiSolver.ObiCollisionEventArgs e)
        {
            var world = ObiColliderWorld.GetInstance();
            foreach (var contact in e.contacts)
            {
                // this one is an actual collision:
                if (contact.distance < 0.01)
                {
                    var pa = solver.particleToActor[contact.bodyA];
                    var po = solver.particleToActor[contact.bodyB];
                    if (pa != null && po != null)
                    {
                        //if (pa !=po)
                        if (pa.actor != null && po.actor != null)
                        {
                            if (pa.actor.gameObject != po.actor.gameObject)
                            {
                                Debug.Log(po.actor.gameObject + "'dan " + pa.actor.gameObject + "'ya");
                                (pa.actor).GetComponent<RopeController>().CheckContact(po.actor as ObiRope);
                            }
                           
                        }
                    }
                }
            }
        }
    }
}

what is your suggestion?
Reply
#8
(01-12-2021, 02:34 PM)greyhawk Wrote: It's rare, but it says that even if I pull the strings away from each other, they are in contact with each other. not sure, but this happens after partial thinning after another contact. I don't know if it's relevant.

Your code does not check for collisions, it considers all contacts: both speculative and real. Try only considering contacts such that:

Code:
if (contact.distance < 0.01)

From the manual:
http://obi.virtualmethodstudio.com/manua...sions.html

Quote:Obi uses a continuous-collision detection method known as speculative contacts, which can generate contacts even when an actual collision isn´t taking place. If you want to prune all speculative contacts and consider actual collisions only, check for distances below a small threshold (e.g 0.01).

Also, you can check the first code example in that page: it draws speculative and actual contacts using different colors.
Reply