Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Oncollision detection and cut rope problem
#1
Pregunta 
Greetings, I am working with OnCollision on a  mechanic that will cut the  rope as a result of contact with objects such as saws, knives and similar objects that can cut the rope.

In addition,3 ropes can interact physically with each other. Trying to cut one, the other is cut. Each one has its own blueprint.

How can I fix the bug in the code I wrote?

You can see the script I wrote
Code:
using System.Collections.Generic;
using UnityEngine;
using Obi;

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

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

        void OnDisable(){
            solver.OnCollision -= Solver_OnCollision;
        }
        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:
                if (contact.distance < 0.01);
                {
                    ObiColliderBase col = world.colliderHandles[contact.bodyB].owner;
                    if(col !=null)
                    TearRope(col, contact);
                }
               
            }
        }

        private void TearRope(ObiColliderBase col, Oni.Contact contact)
        {
            if (!col || !col.CompareTag("saw")) return;
            //int particleIndex = solver.simplices[contact.bodyA];
            ObiSolver.ParticleInActor pa = solver.particleToActor[contact.bodyA];
            if (pa == null) return;

            var actor = pa.actor as ObiRope;
            if (!actor) return;
      
            if (pa.indexInActor >= actor.elements.Count) return;

            actor.Tear(actor.elements[pa.indexInActor]);
            actor.RebuildConstraintsFromElements();
        }
       
    }
}


Attached Files Thumbnail(s)
   

.png   Resim2.png (Size: 5.05 KB / Downloads: 41)
Reply
#2
(17-11-2021, 05:36 PM)greyhawk Wrote: Greetings, I am working with OnCollision on a  mechanic that will cut the  rope as a result of contact with objects such as saws, knives and similar objects that can cut the rope.

In addition,3 ropes can interact physically with each other. Trying to cut one, the other is cut. Each one has its own blueprint.

How can I fix the bug in the code I wrote?

You can see the script I wrote
Code:
using System.Collections.Generic;
using UnityEngine;
using Obi;

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

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

        void OnDisable(){
            solver.OnCollision -= Solver_OnCollision;
        }
        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:
                if (contact.distance < 0.01);
                {
                    ObiColliderBase col = world.colliderHandles[contact.bodyB].owner;
                    if(col !=null)
                    TearRope(col, contact);
                }
               
            }
        }

        private void TearRope(ObiColliderBase col, Oni.Contact contact)
        {
            if (!col || !col.CompareTag("saw")) return;
            //int particleIndex = solver.simplices[contact.bodyA];
            ObiSolver.ParticleInActor pa = solver.particleToActor[contact.bodyA];
            if (pa == null) return;

            var actor = pa.actor as ObiRope;
            if (!actor) return;
      
            if (pa.indexInActor >= actor.elements.Count) return;

            actor.Tear(actor.elements[pa.indexInActor]);
            actor.RebuildConstraintsFromElements();
        }
       
    }
}

Hi!

You're using contact.bodyA as the particle index (which it isn't), should be:

Code:
int particleIndex = solver.simplices[contact.bodyA];

then use particleIndex to access solver arrays.

Also, you're using the actor particle index (pa.indexInActor) to access the elements array, which does not make sense and may result in out of bounds access errors - this is why I think you're checking if the index is larger the elements array, which should not be necessary. Elements and particles are different things. To find the element that contains the particle you want, iterate trough the elements. Your TearRope code should look like this:

Code:
private void TearRope(ObiColliderBase col, 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;

// check rope elements and tear the one that references this particle:
foreach (var elm in actor.elements)
{
   if (elm.particle1 == particleIndex)
   {
      actor.Tear(elm);
      actor.RebuildConstraintsFromElements();
      break;
   }
}

}
Reply
#3
(18-11-2021, 09:06 AM)josemendez Wrote: Hi!

You're using contact.bodyA as the particle index (which it isn't), should be:

Code:
int particleIndex = solver.simplices[contact.bodyA];

then use particleIndex to access solver arrays.

Also, you're using the actor particle index (pa.indexInActor) to access the elements array, which does not make sense and may result in out of bounds access errors - this is why I think you're checking if the index is larger the elements array, which should not be necessary. Elements and particles are different things. To find the element that contains the particle you want, iterate trough the elements. Your TearRope code should look like this:

Code:
private void TearRope(ObiColliderBase col, 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;

// check rope elements and tear the one that references this particle:
foreach (var elm in actor.elements)
{
   if (elm.particle1 == particleIndex)
   {
      actor.Tear(elm);
      actor.RebuildConstraintsFromElements();
      break;
   }
}

}
I made the changes you mentioned, but I couldn't get the result I wanted. What I want is that when I bring the object (in my case it's a saw) to the rope it cuts the rope from that area. I have three rope in the environment. In the problem I have, when I take the saw to the rope, it cuts the wrong rope. moreover, it does not cut from the area I want.
There is something I'm doing wrong but I can't see what it is. Where do you think I should check?
Reply
#4
(18-11-2021, 10:03 AM)greyhawk Wrote: I made the changes you mentioned, but I couldn't get the result I wanted. What I want is that when I bring the object (in my case it's a saw) to the rope it cuts the rope from that area. I have three rope in the environment. In the problem I have, when I take the saw to the rope, it cuts the wrong rope. moreover, it does not cut from the area I want.
There is something I'm doing wrong but I can't see what it is. Where do you think I should check?

These modifications work perfectly for me, can you share the full script you're using including these changes?
Reply
#5
(18-11-2021, 12:38 PM)josemendez Wrote: These modifications work perfectly for me, can you share the full script you're using including these changes?

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

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

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

        void OnDisable(){
            solver.OnCollision -= Solver_OnCollision;
        }
        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:
                if (contact.distance < 0.01);
                {
                    ObiColliderBase col = world.colliderHandles[contact.bodyB].owner;
                    if(col !=null)
                        TearRope(col, contact);
                }
               
            }
        }

        private void TearRope(ObiColliderBase col, Oni.Contact contact)
        {
            if (!col || !col.CompareTag("saw")) return;
// 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;

// check rope elements and tear the one that references this particle:
            foreach (var elm in actor.elements)
            {
                if (elm.particle1 == particleIndex)
                {
                    actor.Tear(elm);
                    actor.RebuildConstraintsFromElements();
                    break;
                }
            }

        }
       
    }
}

What am i doing wrong?
Reply
#6
Its weird and coincidental but I'm too facing the same issue.

In my case I am using only one rope,  
but when on collision, it cuts somewhere other than contact point.
Reply
#7
(18-11-2021, 01:00 PM)greyhawk Wrote: What am i doing wrong?

if (contact.distance < 0.01);  //<----semicolon

You're placing a semicolon right after the if condition. This means the condition is empty and the code inside the brackets will always execute. Your code is equivalent to this:

Code:
if (contact.distance < 0.01)
{
}

// do this no matter what:
ObiColliderBase col = world.colliderHandles[contact.bodyB].owner;
if(col !=null)
   TearRope(col, contact);

So anytime there's any contact, a rope will tear.

Removing the semicolon, it works as intended:
Reply
#8
On a side note: this code assumes no actor in your solver is using surface collisions. If you're using surface collisions, you must get the appropriate particle index from the simplices array. Otherwise your code will get completely incorrect indices.

See "Retrieving the particle involved in a contact" in the manual:

http://obi.virtualmethodstudio.com/manua...sions.html
Reply
#9
(18-11-2021, 01:41 PM)josemendez Wrote: On a side note: this code assumes no actor in your solver is using surface collisions. If you're using surface collisions, you must get the appropriate particle index from the simplices array. Otherwise your code will get completely incorrect indices.

See "Retrieving the particle involved in a contact" in the manual:

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

You are my savior today.  Sonrisa  It is working.


Can you share the work in the video?
Reply
#10
(18-11-2021, 02:34 PM)greyhawk Wrote: You are my savior today.  Sonrisa  It is working.

I'm glad to hear that! Sonrisa

(18-11-2021, 02:34 PM)greyhawk Wrote: Can you share the work in the video?

That's just the included "RopeCutting" sample scene with your script applied to the solver. Also disabled surface collisions in all ropes.
Reply