17-11-2021, 05:36 PM (This post was last modified: 17-11-2021, 05:40 PM by greyhawk.
Edit Reason: Subscribe and receive email notification of new replies
)
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 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;
18-11-2021, 09:06 AM (This post was last modified: 18-11-2021, 09:07 AM by josemendez.)
(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 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;
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:
// 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;
}
}
18-11-2021, 10:03 AM (This post was last modified: 18-11-2021, 10:11 AM by greyhawk.)
(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:
// 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?
(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?
(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 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;
}
}
18-11-2021, 01:24 PM (This post was last modified: 18-11-2021, 01:47 PM by josemendez.)
(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);
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:
(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: