Obi Official Forum
Help Are there any examples of a rigidbody climbing an ObiRope? - 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 Are there any examples of a rigidbody climbing an ObiRope? (/thread-4133.html)



Are there any examples of a rigidbody climbing an ObiRope? - gnovos - 17-03-2024

I went through the ExtendableGrapplingHook example, but that is "climbing" by changing the length or the rope, which isn't quite what I'm looking for.  I'm looking more for something that just moves a rigidbody steadily up a rope while maintaining the two-way forces between the two.  I have a few ideas how I could accomplish this, but if there's already an example somewhere to save me a lot of trial and error, I would be very grateful.


RE: Are there any examples of a rigidbody climbing an ObiRope? - josemendez - 19-03-2024

(17-03-2024, 07:48 AM)gnovos Wrote: I went through the ExtendableGrapplingHook example, but that is "climbing" by changing the length or the rope, which isn't quite what I'm looking for.  I'm looking more for something that just moves a rigidbody steadily up a rope while maintaining the two-way forces between the two.  I have a few ideas how I could accomplish this, but if there's already an example somewhere to save me a lot of trial and error, I would be very grateful.

Hi!

Unfortunately not, there are no samples of this. Other users have made similar stuff in the past, so someone may be willing to share their code?

The core idea is to create a pin constraint at runtime and update its position at runtime, moving it along the rope's elements. Conceptually it is quite simple, though it's math-heavy and coding it can be a bit of a steep learning curve if this is your first time using Obi.

kind regards


RE: Are there any examples of a rigidbody climbing an ObiRope? - gnovos - 19-03-2024

(19-03-2024, 08:37 AM)josemendez Wrote: Hi!

Unfortunately not, there are no samples of this. Other users have made similar stuff in the past, so someone may be willing to share their code?

The core idea is to create a pin constraint at runtime and update its position at runtime, moving it along the rope's elements. Conceptually it is quite simple, though it's math-heavy and coding it can be a bit of a steep learning curve if this is your first time using Obi.

kind regards

That was enough of a hint to help me get it working.  Here is my solution:


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

public class ObiClimber : MonoBehaviour
{
    public ObiRope rope = default;

    public Vector3 Offset = default;

    public float ClimbSpeed = 1f;

    int CurrentRopeElement = -1;

    ObiCollider obiCollider;

    ObiPinConstraintsBatch batch = new ObiPinConstraintsBatch();

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

    bool started = false;
    private void Start()
    {
        StartClimber();
        started = true;
    }

    public void OnEnable()
    {
        if (started)
        {
            StartClimber();
        }
    }

    private void OnDisable()
    {
        StopClimber();
    }

    public void StopClimber()
    {
        if (climber != null)
        {
            StopCoroutine(climber);
        }
    }

    public void StartClimber()
    {
        StopClimber();

        CurrentRopeElement = FindNearestRopeElement(transform.position + Offset);

        climber = StartCoroutine(Climber());
    }

    public int FindNearestRopeElement(Vector3 pos)
    {
        int closest = -1;
        var closestDist = float.MaxValue;

        for (int i = 0; i < rope.elements.Count; ++i)
        {
            var e = rope.elements[i];
            var p2 = rope.solver.positions[e.particle2];
            var dist = Mathf.Abs(pos.y - p2.y);
            if (dist < closestDist)
            {
                closestDist = dist;
                closest = i;
            }
        }

        return closest;
    }

    Coroutine climber;
    IEnumerator Climber()
    {
        while (isActiveAndEnabled && CurrentRopeElement-- > 0)
        {
            var at = rope.elements[CurrentRopeElement];

            var currentDist = 0f;

            while (currentDist < 1f)
            {
                Vector3 p = rope.solver.positions[at.particle1];
                Vector3 p2 = rope.solver.positions[at.particle2];
                var current = Vector3.Lerp(p2, p, currentDist);
                var offset = p2 - current;
                SetConstraint(at, offset);
                currentDist += Time.deltaTime * ClimbSpeed;
                yield return null;
            }


            yield return null;
        }
    }

    public void SetConstraint(ObiStructuralElement element, Vector3 offset)
    {
        var pinConstraints = rope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
        pinConstraints.Clear();
        batch.Clear();
        batch.AddConstraint(rope.solverIndices[element.particle2], obiCollider, offset + Offset, Quaternion.identity, 0, 0, float.PositiveInfinity);
        batch.activeConstraintCount = 1;
        pinConstraints.AddBatch(batch);
        rope.SetConstraintsDirty(Oni.ConstraintType.Pin);
    }

    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(transform.position + Offset, 0.15f);
    }
}

This will steadily climb a rigidbody+ObiCollider+ObiRigidbody up an ObiRope (currently only up, assuming the top element is 0).  I recommend "freeze rotation" on the rigidbody during the climb, as otherwise you might get a lot of spinning that is hard to control. Be sure either the ObiCollider category doesn't collide with the rope, or else the offset is far enough way so they don't collide or the forces will cause feedback that goes out of control.  I'm probably doing something inefficiently in this code, but it works for my needs.  Feel free to clean this up and use it in an official example if that would be of any value.  Otherwise I'm just going to post it here for posterity in case others come along needing a similar solution.