Posts: 23
Threads: 4
Joined: Feb 2021
Reputation:
0
(28-04-2025, 08:07 AM)josemendez Wrote: contact.bodyA is not a particle index. To get the index of the particle in the actor, use the solver.simplices array and then look up solver.particleInActor to get the index of that particle in the rope. See "Retrieving the particle involved in a contact" in the manual: https://obi.virtualmethodstudio.com/manu...sions.html
If you're altering the topology of the rope in any way (extending/retracting/cutting the rope) the above approach using particles won't work. The reason is that there's no guarantee new particles (added when extending the rope) will be added at the end/start of the rope, so their indices may be unordered. You must use elements instead as explained in the manual: https://obi.virtualmethodstudio.com/manu...ropes.html
The approach is similar: get a particle index from the contact, then iterate over all elements until you find the one that references that particle. Once you have the index of the desired element, divide it by the number of elements in the rope to get the normalized coordinate.
Simply reloading the original rope blueprint (by assigning it to rope.ropeBlueprint) should do it.
kind regards,
I wrote such a code according to the document you gave me but the result is negative, what should I fix?
Code: public void OnRopeContactEnter(ObiSolver solver, Oni.Contact contact)
{
var world = ObiColliderWorld.GetInstance();
var col = world.colliderHandles[contact.bodyB].owner;
if (col != null && col.GetComponent<Item>() != null && !col.GetComponent<Item>().ColliderTriggerControl())
{
if (!GameManager.instance.usedItems.Contains(col.GetComponent<Item>()))
{
GameManager.instance.UsedItemsAdd(col.GetComponent<Item>());
int particleIndex = solver.simplices[contact.bodyA];
ObiSolver.ParticleInActor pa = solver.particleToActor[particleIndex];
if (rope != null)
{
var elements = rope.elements;
int elementIndex = -1;
for (int i = 0; i < elements.Count; ++i)
{
var element = elements[i];
if (element.particle1 == pa.indexInActor || element.particle2 == pa.indexInActor)
{
elementIndex = i;
break;
}
}
if (elementIndex != -1)
{
float normalizedCoordinate = (float)elementIndex / elements.Count;
Debug.Log("Normalized rope position: " + normalizedCoordinate);
}
else
{
Debug.LogWarning("Element not found for particle " + particleIndex);
}
}
else
{
Debug.LogWarning("Rope reference is missing!");
}
}
Posts: 6,467
Threads: 27
Joined: Jun 2017
Reputation:
413
Obi Owner:
28-04-2025, 09:30 AM
(This post was last modified: 28-04-2025, 09:31 AM by josemendez.)
(28-04-2025, 09:13 AM)0hsyn1 Wrote: I wrote such a code according to the document you gave me but the result is negative, what should I fix?
Elements contain indices of particles in the solver, not the actor. Here's the fixed code:
Code: public void OnRopeContactEnter(ObiSolver solver, Oni.Contact contact)
{
var world = ObiColliderWorld.GetInstance();
var col = world.colliderHandles[contact.bodyB].owner;
if (col != null && col.GetComponent<Item>() != null && !col.GetComponent<Item>().ColliderTriggerControl())
{
if (!GameManager.instance.usedItems.Contains(col.GetComponent<Item>()))
{
GameManager.instance.UsedItemsAdd(col.GetComponent<Item>());
int particleIndex = solver.simplices[contact.bodyA];
if (rope != null)
{
var elements = rope.elements;
int elementIndex = -1;
for (int i = 0; i < elements.Count; ++i)
{
var element = elements[i];
if (element.particle1 == particleIndex || element.particle2 == particleIndex)
{
elementIndex = i;
break;
}
}
if (elementIndex != -1)
{
float normalizedCoordinate = (float)elementIndex / elements.Count;
Debug.Log("Normalized rope position: " + normalizedCoordinate);
}
else
{
Debug.LogWarning("Element not found for particle " + particleIndex);
}
}
else
{
Debug.LogWarning("Rope reference is missing!");
}
}
Posts: 23
Threads: 4
Joined: Feb 2021
Reputation:
0
(28-04-2025, 09:30 AM)josemendez Wrote: Elements contain indices of particles in the solver, not the actor. Here's the fixed code:
Code: public void OnRopeContactEnter(ObiSolver solver, Oni.Contact contact)
{
var world = ObiColliderWorld.GetInstance();
var col = world.colliderHandles[contact.bodyB].owner;
if (col != null && col.GetComponent<Item>() != null && !col.GetComponent<Item>().ColliderTriggerControl())
{
if (!GameManager.instance.usedItems.Contains(col.GetComponent<Item>()))
{
GameManager.instance.UsedItemsAdd(col.GetComponent<Item>());
int particleIndex = solver.simplices[contact.bodyA];
if (rope != null)
{
var elements = rope.elements;
int elementIndex = -1;
for (int i = 0; i < elements.Count; ++i)
{
var element = elements[i];
if (element.particle1 == particleIndex || element.particle2 == particleIndex)
{
elementIndex = i;
break;
}
}
if (elementIndex != -1)
{
float normalizedCoordinate = (float)elementIndex / elements.Count;
Debug.Log("Normalized rope position: " + normalizedCoordinate);
}
else
{
Debug.LogWarning("Element not found for particle " + particleIndex);
}
}
else
{
Debug.LogWarning("Rope reference is missing!");
}
}
This piece of code didn't help either :/
I also encountered a problem with the rope reset part. If the rope is wrapped around an object, it does not reset, but I can reset it when it is idle.
Posts: 6,467
Threads: 27
Joined: Jun 2017
Reputation:
413
Obi Owner:
(28-04-2025, 09:54 AM)0hsyn1 Wrote: This piece of code didn't help either :/
Would you mind specifying what results you're getting, and how they differ from what you'd need to get?
(28-04-2025, 09:54 AM)0hsyn1 Wrote: I also encountered a problem with the rope reset part. If the rope is wrapped around an object, it does not reset, but I can reset it when it is idle.
That doesn't really make sense: a rope is just a list of positions/velocities, it doesn't matter if the positions are wrapped around and object or not. Would it be possible for you to share the code you're using for this?
Posts: 23
Threads: 4
Joined: Feb 2021
Reputation:
0
28-04-2025, 10:39 AM
(This post was last modified: 28-04-2025, 10:46 AM by 0hsyn1.)
As seen in the picture (I shared it before), when I enter the value I obtained into "M" in the ObiRopeAttach script, the red object remains where it is seen, the area I want is the blue area, that is, the point of contact (it can be any place in contact).
If I can get this "M" value right, I want to make a gameObject move from that point to the end of the rope.
Reset code;
Code: [SerializeField] ObiRope rope;
[SerializeField] ObiRopeBlueprint blueprint;
[SerializeField] ObiRopeExtrudedRenderer ropeRenderer;
[SerializeField] ObiRopeCursor cursor;
public void RopeReset()
{
rope.ropeBlueprint = blueprint;
ropeEnd.transform.position = ropeEndStartPos;
}
Posts: 6,467
Threads: 27
Joined: Jun 2017
Reputation:
413
Obi Owner:
28-04-2025, 11:15 AM
(This post was last modified: 28-04-2025, 11:16 AM by josemendez.)
(28-04-2025, 10:39 AM)0hsyn1 Wrote: As seen in the picture (I shared it before), when I enter the value I obtained into "M" in the ObiRopeAttach script, the red object remains where it is seen, the area I want is the blue area, that is, the point of contact (it can be any place in contact).
If I can get this "M" value right, I want to make a gameObject move from that point to the end of the rope.
Are you using surface collisions by any chance? the code used to get particle indices from a contact is different when using them, as explained in " Retrieving the particle involved in a contact". If you're using surface collisions, the code should look like this:
Code: // retrieve the offset and size of the simplex in the solver.simplices array:
int simplexStart = solver.simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
// starting at simplexStart, iterate over all particles in the simplex:
for (int i = 0; i < simplexSize; ++i)
{
int particleIndex = solver.simplices[simplexStart + i];
// do something with each particle, for instance get its position:
var position = solver.positions[particleIndex];
}
instead of just:
Code: // get the particle index directly, as all simplices are guaranteed to have size 1:
int particleIndex = solver.simplices[contact.bodyA];
(28-04-2025, 10:39 AM)0hsyn1 Wrote: Reset code;
Code: public void RopeReset()
{
rope.ropeBlueprint = blueprint;
ropeEnd.transform.position = ropeEndStartPos;
}
This should successfully reset the rope. What are you getting instead?
Posts: 23
Threads: 4
Joined: Feb 2021
Reputation:
0
28-04-2025, 11:52 AM
(This post was last modified: 28-04-2025, 07:05 PM by 0hsyn1.)
(28-04-2025, 11:15 AM)josemendez Wrote: Are you using surface collisions by any chance? the code used to get particle indices from a contact is different when using them, as explained in "Retrieving the particle involved in a contact". If you're using surface collisions, the code should look like this:
Code: // retrieve the offset and size of the simplex in the solver.simplices array:
int simplexStart = solver.simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
// starting at simplexStart, iterate over all particles in the simplex:
for (int i = 0; i < simplexSize; ++i)
{
int particleIndex = solver.simplices[simplexStart + i];
// do something with each particle, for instance get its position:
var position = solver.positions[particleIndex];
}
How can I find the colliding particle from this code? That is, how can I detect (particle/all particle)?
Is the simplexStart value here my index that is experiencing the collision?
When I count particles visually I get a different result.
Surface-based uncheck
I solved the reset process as follows;
Code: public void RopeReset()
{
ropeEnd.transform.position = ropeEndStartPos;
rope.ropeBlueprint = null;
rope.ropeBlueprint = blueprint;
}
Posts: 6,467
Threads: 27
Joined: Jun 2017
Reputation:
413
Obi Owner:
29-04-2025, 07:12 AM
(This post was last modified: 29-04-2025, 07:17 AM by josemendez.)
(28-04-2025, 11:52 AM)0hsyn1 Wrote:
How can I find the colliding particle from this code? That is, how can I detect (particle/all particle)?
Is the simplexStart value here my index that is experiencing the collision?
No, "particleIndex" is as the comment in the code points out. "simplexStart" is the first entry in the simplices array, and "simplexSize" the size of the simplex. So when a simplex collides, you get a start and a size. The indices of the colliding particles are stored in solver.simplices[simplexStart] to solver.simplices[simplexStart+simplexSize].
(28-04-2025, 11:52 AM)0hsyn1 Wrote: Surface-based uncheck
If none of your ropes use surface-based collisions, all simplices in the solver have size 1 and it's not needed to iterate over all particles in each simplex. You can just do what you were doing before:
Code: int particleIndex = solver.simplices[contact.bodyA];
I just tried the script and it works perfectly for me. I'm attaching the version of the script that I used to perform the test, maybe you can spot any difference with what you're doing?
Code: using UnityEngine;
using Obi;
public class RopeContactNormalized : MonoBehaviour
{
public ObiRope rope;
// Start is called before the first frame update
void Start()
{
rope.solver.OnCollision += OnRopeContactEnter;
}
public void OnRopeContactEnter(ObiSolver solver, ObiNativeContactList contacts)
{
var world = ObiColliderWorld.GetInstance();
float normalizedCoordinate = -1;
foreach (var contact in contacts)
{
if (contact.distance > 0.001f) continue;
int particleIndex = solver.simplices[contact.bodyA];
var col = world.colliderHandles[contact.bodyB].owner;
if (rope != null)
{
var elements = rope.elements;
int elementIndex = -1;
for (int i = 0; i < elements.Count; ++i)
{
var element = elements[i];
if (element.particle1 == particleIndex || element.particle2 == particleIndex)
{
elementIndex = i;
break;
}
}
if (elementIndex != -1)
{
normalizedCoordinate = (float)elementIndex / elements.Count;
Debug.Log("Normalized rope position: " + normalizedCoordinate);
break;
}
else
{
Debug.LogWarning("Element not found for particle " + particleIndex);
}
}
else
{
Debug.LogWarning("Rope reference is missing!");
}
}
if (normalizedCoordinate >= 0)
{
var section = rope.GetComponent<ObiPathSmoother>().GetSectionAt(normalizedCoordinate);
Debug.DrawRay(section.position, section.normal, Color.red);
}
}
}
Posts: 23
Threads: 4
Joined: Feb 2021
Reputation:
0
(29-04-2025, 07:12 AM)josemendez Wrote: No, "particleIndex" is as the comment in the code points out. "simplexStart" is the first entry in the simplices array, and "simplexSize" the size of the simplex. So when a simplex collides, you get a start and a size. The indices of the colliding particles are stored in solver.simplices[simplexStart] to solver.simplices[simplexStart+simplexSize].
If none of your ropes use surface-based collisions, all simplices in the solver have size 1 and it's not needed to iterate over all particles in each simplex. You can just do what you were doing before:
Code: int particleIndex = solver.simplices[contact.bodyA];
I just tried the script and it works perfectly for me. I'm attaching the version of the script that I used to perform the test, maybe you can spot any difference with what you're doing?
Code: using UnityEngine;
using Obi;
public class RopeContactNormalized : MonoBehaviour
{
public ObiRope rope;
// Start is called before the first frame update
void Start()
{
rope.solver.OnCollision += OnRopeContactEnter;
}
public void OnRopeContactEnter(ObiSolver solver, ObiNativeContactList contacts)
{
var world = ObiColliderWorld.GetInstance();
float normalizedCoordinate = -1;
foreach (var contact in contacts)
{
if (contact.distance > 0.001f) continue;
int particleIndex = solver.simplices[contact.bodyA];
var col = world.colliderHandles[contact.bodyB].owner;
if (rope != null)
{
var elements = rope.elements;
int elementIndex = -1;
for (int i = 0; i < elements.Count; ++i)
{
var element = elements[i];
if (element.particle1 == particleIndex || element.particle2 == particleIndex)
{
elementIndex = i;
break;
}
}
if (elementIndex != -1)
{
normalizedCoordinate = (float)elementIndex / elements.Count;
Debug.Log("Normalized rope position: " + normalizedCoordinate);
break;
}
else
{
Debug.LogWarning("Element not found for particle " + particleIndex);
}
}
else
{
Debug.LogWarning("Rope reference is missing!");
}
}
if (normalizedCoordinate >= 0)
{
var section = rope.GetComponent<ObiPathSmoother>().GetSectionAt(normalizedCoordinate);
Debug.DrawRay(section.position, section.normal, Color.red);
}
}
}
ObiNativeContactList I don't have the definition, it gives a warning.
I'm listening to the events of the script ObiContactEventDispatcher
Posts: 6,467
Threads: 27
Joined: Jun 2017
Reputation:
413
Obi Owner:
29-04-2025, 11:08 AM
(This post was last modified: 29-04-2025, 11:09 AM by josemendez.)
(29-04-2025, 11:00 AM)0hsyn1 Wrote: ObiNativeContactList I don't have the definition, it gives a warning.
ObiNativeContactList exists in the Obi namespace. Unless you're using a rather old version of the asset, it should be found just fine.
(29-04-2025, 11:00 AM)0hsyn1 Wrote: I'm listening to the events of the script [color=#333333][size=small]ObiContactEventDispatcher
Ok, so you're not using contact callbacks directly then? Which event are you listening for? Keep in mind that this component reports the contact that caused the event, not the particle currently in contact. So if you eg. listen for the "collision enter" event and then move the object in contact with the rope, the contact won't be updated.
kind regards,
|