Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Grabbing a random position on a rope at runtime (obirope 5)
#1
Hey, I've had a look over previous forum questions and have seen some answers that i think are related to an older system with pins...

I have a rope that i'm at runtime I'm wanting to be able to grab...   Previously it seemed like you would do this by creating a pin at the point where your hand touched the rope...

My current approach is to try and find the closest particle to where your hand is, create a particle group at that point, and use a particle attachment to make a connection between that point and the transform of your hand.  

I'm abit stuck though, i can't figure out how to make a particle group at runtime, nor figure out how to specify the location of where the particle group should be. I have a convoluted idea that I could create a particle collision and see where the collision point is...

Very curious if someone has a suggestion on if I'm approaching this in the right direction, and if i am -  how to create a particle group at runtime at the point of collision...
Reply
#2
(03-05-2021, 06:16 AM)ink_13 Wrote: Hey, I've had a look over previous forum questions and have seen some answers that i think are related to an older system with pins...

I have a rope that i'm at runtime I'm wanting to be able to grab...   Previously it seemed like you would do this by creating a pin at the point where your hand touched the rope...

My current approach is to try and find the closest particle to where your hand is, create a particle group at that point, and use a particle attachment to make a connection between that point and the transform of your hand.  

I'm abit stuck though, i can't figure out how to make a particle group at runtime, nor figure out how to specify the location of where the particle group should be. I have a convoluted idea that I could create a particle collision and see where the collision point is...

Very curious if someone has a suggestion on if I'm approaching this in the right direction, and if i am -  how to create a particle group at runtime at the point of collision...

#1) you need to determine whether you need two-way coupling at all, or maybe one-way coupling is enough. That is, if the rope should be able to apply forces to the object it is attached to (two-way coupling) or not (one-way coupling).
Two-way coupling is done by adding pin constraints, one-way coupling done by setting the particle mass to infinite and overriding its position.

It has been this way since Obi 1.0. In newer versions, dynamic and static attachments (respectively) were added as a higher-level layer that automatically manage this for you.

If all you want is to grab the rope and move it around, one-way coupling is enough.

#2) you need to consider whether you want to keep track of the attached particles yourself, or if you want to delegate this task to attachments. When dealing with collisions and individual particles, working with particles directly is often easier since you don't have to create particle groups or attachments at all.

So all you need to do is keep track of which particles are in contact with a collider, then set their inverse mass to zero (same as setting the mass to infinite, see: http://obi.virtualmethodstudio.com/tutor...icles.html), and set their position to whatever value you want while they're grabbed. To release them, just revert their inverse mass to whatever it was.

Obi 6 includes a sample script (ObiCollisionGrabber) that does just this. See:

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

/**
* Sample component that makes a collider "grab" any particle it touches (regardless of which Actor it belongs to).
*/
[RequireComponent(typeof(ObiCollider))]
public class ObiContactGrabber : MonoBehaviour
{

    public ObiSolver solver;

    /**
     * Helper class that stores the index of a particle in the solver, its position in the grabber's local space, and its inverse mass previous to being grabbed.
     * This makes it easy to tell if a particle has been grabbed, update its position while grabbing, and restore its mass after being released.
     */
    private class GrabbedParticle : IEqualityComparer<GrabbedParticle>
    {
        public int index;
        public float invMass;
        public Vector3 localPosition;

        public GrabbedParticle(int index, float invMass)
        {
            this.index = index;
            this.invMass = invMass;
        }

        public bool Equals(GrabbedParticle x, GrabbedParticle y)
        {
            return x.index == y.index;
        }

        public int GetHashCode(GrabbedParticle obj)
        {
            return index;
        }
    }

    private ObiSolver.ObiCollisionEventArgs collisionEvent;                                  /**< store the current collision event*/
    private ObiCollider localCollider;                                                           /**< the collider on this gameObject.*/
    private HashSet<GrabbedParticle> grabbedParticles = new HashSet<GrabbedParticle>();          /**< set to store all currently grabbed particles.*/
    private HashSet<ObiActor> grabbedActors = new HashSet<ObiActor>();                 /**< set of softbodies grabbed during this step.*/

    private Matrix4x4 grabber2Solver;
    private Matrix4x4 solver2Grabber;

    private void Awake()
    {
        localCollider = GetComponent<ObiCollider>();
    }

    private void OnEnable()
    {
        if (solver != null)
            solver.OnCollision += Solver_OnCollision;
    }

    private void OnDisable()
    {
        if (solver != null)
            solver.OnCollision -= Solver_OnCollision;
    }

    private void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        // Calculate transform matrix from grabber to world space (Note: if using local space simulation, postmultiply with solver.transform.localToWorldMatrix)
        solver2Grabber = transform.worldToLocalMatrix * solver.transform.localToWorldMatrix;

        // and its inverse:
        grabber2Solver = solver2Grabber.inverse;

        collisionEvent = e;
    }

    private void UpdateParticleProperties()
    {
        // Update rest shape matching of all grabbed softbodies:
        foreach (ObiActor actor in grabbedActors)
        {
            actor.UpdateParticleProperties();
        }
    }

    /**
     * Creates and stores a GrabbedParticle from the particle at the given index.
     * Returns true if we sucessfully grabbed a particle, false if the particle was already grabbed.
     */
    private bool GrabParticle(int index)
    {
        GrabbedParticle p = new GrabbedParticle(index, solver.invMasses[index]);

        // in case this particle has not been grabbed yet:
        if (!grabbedParticles.Contains(p))
        {
            // record the particle's position relative to the grabber, and store it.
            p.localPosition = solver2Grabber.MultiplyPoint3x4(solver.positions[index]);
            grabbedParticles.Add(p);

            // Set inv mass and velocity to zero:
            solver.invMasses[index] = 0;
            solver.velocities[index] = Vector4.zero;

            return true;
        }
        return false;
    }

    /**
     * Grabs all particles currently touching the grabber.
     */
    public void Grab()
    {
        grabbedActors.Clear();
        var world = ObiColliderWorld.GetInstance();

        if (solver != null && collisionEvent != null)
        {
            foreach (Oni.Contact contact in collisionEvent.contacts)
            {
                // this one is an actual collision:
                if (contact.distance < 0.01f)
                {
                    var contactCollider = world.colliderHandles[contact.other].owner;

                    // if the current contact references our collider, proceed to grab the particle.
                    if (contactCollider == localCollider)
                    {
                        // try to grab the particle, if not already grabbed.
                        if (GrabParticle(contact.particle))
                            grabbedActors.Add(solver.particleToActor[contact.particle].actor);
                    }

                }
            }
        }

        UpdateParticleProperties();
    }

    /**
     * Releases all currently grabbed particles. This boils down to simply resetting their invMass.
     */
    public void Release()
    {
        // Restore the inverse mass of all grabbed particles, so dynamics affect them.
        foreach (GrabbedParticle p in grabbedParticles)
            solver.invMasses[p.index] = p.invMass;

        UpdateParticleProperties();
        grabbedActors.Clear();
        grabbedParticles.Clear();
    }

    /**
     * Updates the position of the grabbed particles.
     */
    private void FixedUpdate()
    {
        foreach (GrabbedParticle p in grabbedParticles)
            solver.positions[p.index] = grabber2Solver.MultiplyPoint3x4(p.localPosition);
    }

}
Reply
#3
Thanks very much for the advice - that was an incredibly detailed answer. I updated the program.
Everything looks perfect and works well. Thanks so much!
Reply
#4
Hi!

I'm looking for the exact same thing but I can't seem to get it working. 
The sample script you refer to is no longer available and I tried using the one you posted but doesn't seem to work

I'm using ObiRope 6.5.2
Any chance you know if this feature is updated? Sonrisa

Kind regards.
-----------------------
EDIT:

I got the script to work by changing the Grab() method with bodyA/bodyB that i saw in the documentation.
I made a toggle in the editor so when its enabled the Grab() will be called and it grabs the cable Gran sonrisa (I considered this a win already haha).
Though when disabling the toggle so Grab() is no longer called keeps the particle that was grabbed in same position, the Release() method doesn't seem to work or be called.
Even when I make a separate toggle for Release() the particle stays in put. 

Another issue I noticed is that when I test the Grab when the cable is not extended with the cursor it works just fine. 
But when I test the Grab after the cable is extended it doesn't always seem to work as I would hope it would. The particle gets grabbed but when I move around the particle/cable doesn't follow the object that grabbed the rope perfectly. It lags behind... not sure how to explain it. 

I feel like I'm almost there, I'm already happy to see that the Grab() works even though its not yet as I hoped it to be.
Reply
#5
(04-05-2023, 01:28 PM)5G_Zendmast Wrote: Hi!

I'm looking for the exact same thing but I can't seem to get it working. 
The sample script you refer to is no longer available and I tried using the one you posted but doesn't seem to work


Hi!

This script is included with the asset, it's named ObiContactGrabber and can be found at /Obi/Scripts/Common/Utils/ObiContactGrabber.cs. You're supposed to call the Grab() and Release() methods manually, whenever you want to grab/release the particles that are in contact with the collider.

Just tested it in 6.5.2 and it works fine for me, let me know if you need further help.

kind regards,
Reply
#6
(09-05-2023, 11:33 AM)josemendez Wrote: Hi!

This script is included with the asset, it's named ObiContactGrabber and can be found at /Obi/Scripts/Common/Utils/ObiContactGrabber.cs. You're supposed to call the Grab() and Release() methods manually, whenever you want to grab/release the particles that are in contact with the collider.

Just tested it in 6.5.2 and it works fine for me, let me know if you need further help.

kind regards,

Hello Jose,

Thanks for your reply.
I don't know how I missed that.... But yes I found the script now haha.
I find it difficult to understand this one as it's not quite working the way I hoped. 

Whilst testing the script the Grab() and Release() method work, so that's good! Sonrisa
What's not quite working the way I hoped is the location where grabbing occurs. 
In the attached screenshots, the script is on the object touching the rope, when I enable Grab() it picks a particle way up instead of where it touches the cable. 
I have no idea why it does this, do you perhaps recognize this behavior?

Thanks for your reply.

Kind regards, 

Patrick


Attached Files Thumbnail(s)
       
Reply
#7
(16-05-2023, 02:09 PM)5G_Zendmast Wrote: Whilst testing the script the Grab() and Release() method work, so that's good! Sonrisa
What's not quite working the way I hoped is the location where grabbing occurs. 
In the attached screenshots, the script is on the object touching the rope, when I enable Grab() it picks a particle way up instead of where it touches the cable. 
I have no idea why it does this, do you perhaps recognize this behavior?

Hi Patrick,

Is your rope using surface collisions? in that case, collision callbacks work with simplices (See "Retrieving the particle involved in a contact": http://obi.virtualmethodstudio.com/manua...sions.html), in this case if you're using the old ObiContactGrabber code in this thread instead of the one included with Obi, it won't work correctly.
Reply