Obi Official Forum
Help Adding attachments at runtime to a pre-existing instance of a rope in 5.X - Printable Version

+- Obi Official Forum (https://obi.virtualmethodstudio.com/forum)
+-- Forum: Obi Users Category (https://obi.virtualmethodstudio.com/forum/forum-1.html)
+--- Forum: Obi Rope (https://obi.virtualmethodstudio.com/forum/forum-4.html)
+--- Thread: Help Adding attachments at runtime to a pre-existing instance of a rope in 5.X (/thread-2224.html)



Adding attachments at runtime to a pre-existing instance of a rope in 5.X - eliza - 06-05-2020

I have been trying to figure out how to do this for a few hours now and I don't feel any closer than where I started.

I have a scene which is planned to have many ropes placed via the scene editor, many of them potentially sharing the same rope blueprints. Currently, my scene only has a single rope which has a static attachment to a floating cube. During runtime, I am attempting to add a pin constraint to an ObiCollider that collides with a given rope. I tried to adapt the grappling hook code into a method which is called when a rope collision event is triggered, and have verified that it is being called with the correct object references.

Code:
 private bool hasConstraint = false;

 public void OnRopeCollision(ObiRope rope, int ropeIndex) {
   if (hasConstraint) {
     return;
   }

   hasConstraint = true;
   var blueprint = rope.ropeBlueprint;
   var pinConstraints = blueprint.GetConstraintsByType(Oni.ConstraintType.Pin)
                            as ObiConstraints<ObiPinConstraintsBatch>;
   ObiPinConstraintsBatch batch = pinConstraints.GetFirstBatch();
   batch.AddConstraint(ropeIndex, _obiCollider, transform.localPosition, Quaternion.identity);
   batch.activeConstraintCount++;
    rope.ropeBlueprint = blueprint;
   _obiCollider.Phase = 1;
 }

I suspect the problem has to do with the way that the blueprint is instantiated versus the grappling hook sample, but I am really at a loss on how to debug this. Also, given that in the future, many ropes would potentially share the same blueprint, my intuition tells me that even if it did work, every single rope in the scene would become pinned to the ObiCollider.

Please let me know if you need any more information! Thank you for your help.


RE: Adding attachments at runtime to a pre-existing instance of a rope in 5.X - josemendez - 06-05-2020

Hi there,

You're adding constraints to a blueprint. Existing instances of the blueprint won't be updated if you modify it, so if your ropes are already there in the scene, modifying the blueprint will have no effect at all. Blueprints behave exactly like prefabs, as both are assets: once instantiated at runtime, the instances retain no memory of what prefab they were instantiated from, so modifying the prefab will have no effect on the existing instances. Only on instances you create after modifying it. Same for blueprints. See:
http://obi.virtualmethodstudio.com/tutorials/bigpicture.html

Note this is not a Obi-only thing, it's how the concept of prefabs/instances (around which a big part of Unity is built) works. It's very important that you understand it.

When an actor is instantiated in a solver, new indices are assigned to its particles, and these indices are stored in the actor's solverIndices array:
http://obi.virtualmethodstudio.com/tutorials/scriptingparticles.html

Quote:Actor particle indices run from 0 to the amount of particles in that actor. However, since particle data for an actor might be scattered across the solver arrays, you need to map from actor index to solver index. You can map from actor particle indices to solver particle indices using the actor's solverIndices array.

So even if multiple rope instances share the same blueprint, at runtime their particles will have different solver indices (the ropeIndex you pass into your function), and they won't all be pinned to the same collider if that's what you meant.

Solution: instead of adding new pins to the blueprint, add them to the actor itself.

Also, I see you're passing transform.localPosition as the pin offset. This is quite likely not what you want. The offset is the position of the pin constraint, expressed in the collider's local space. However transform.localPosition is the position of whatever object contains the OnRopeCollision() method, expressed in its parent's local space, so two completely different things. I can't be certain for sure this is not intentional (in the grappling hook sample code it is, since we use the position of the hook launcher as the constraint position for the character), but in your context it looks like a mistake to me.


RE: Adding attachments at runtime to a pre-existing instance of a rope in 5.X - eliza - 11-05-2020

Thank you for your help--I would have replied sooner, but I didn't realize I didn't have email notifications enabled Triste


The information you provided was really valuable to me. However, I'm still not certain how to actually add a constraint to an actor entirely through code, especially given that I don't seem to see much documentation on the Obi API for 5.X. I noticed (by looking at the source code directly) that GetConstraintsByType is also a method on ObiActor...


Code:
 private bool hasConstraint = false;

 public void OnRopeCollision(ObiRope rope, int ropeIndex) {
   if (hasConstraint) {
     return;
   }

   hasConstraint = true;
   var pinConstraints =
       rope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
   ObiPinConstraintsBatch batch = pinConstraints.GetFirstBatch();
   batch.AddConstraint(ropeIndex, _obiCollider, new Vector3(), Quaternion.identity);
   batch.activeConstraintCount++;
   _obiCollider.Phase = 1;
 }

Still no change in behavior, but it seems at least more correct.

Specifically, the arguments for the method are:
Code:
     ObiSolver.ParticleInActor pa = _solver.particleToActor[contact.particle];
     var rope = pa.actor as ObiRope;
     int ropeIndex = pa.indexInActor;