Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Growing a rope attached to an object
#1
I have a rope attached statically via ObiParticleAttachment to an immobile object at the beginning, and a movable object (my hand in VR) at the end.

The initial scripted attach works great and the rope behaves as expected. However I can't figure out how to properly grow or shrink via the cursor.

The cursor is set to Cursor Mu 1, Source Mu 1, Direction checked. The rope does grow and shrink. However it seems that the ObiParticleAttachment becomes invalidated because of this. I attempted to repair the attachment every time the cursor is changed but this also fails -- the rope hangs limp as though unattached.

Code:
public class RopeController : MonoBehaviour
{
    public float speed = 0.1f;
    public GameObject handObject;
    private bool _addedAttachment;
    private ObiParticleAttachment _attachment;
    private ObiRopeCursor _cursor;
    private ObiParticleGroup _group;
    private ObiRope _rope;
    private InputAction _ropeDecrease;
    private InputAction _ropeIncrease;

    private void Start()
    {
        _ropeIncrease = InputSystem.actions.FindActionMap("Player").FindAction("Rope Increase");
        _ropeDecrease = InputSystem.actions.FindActionMap("Player").FindAction("Rope Decrease");
        _cursor = GetComponent<ObiRopeCursor>();
        _rope = _cursor.GetComponent<ObiRope>();
    }

    private void Update()
    {
        if (!_addedAttachment && _rope.solver.positions.count > 0)
        {
            var lastParticleInitId = _rope.elements[^1].particle2;
            _group = ScriptableObject.CreateInstance<ObiParticleGroup>();
            _group.particleIndices.Add(lastParticleInitId);

            _rope.solver.positions[lastParticleInitId] =
                _rope.solver.transform.InverseTransformPoint(handObject.transform.position);

            _attachment = gameObject.AddComponent<ObiParticleAttachment>();
            _attachment.attachmentType = ObiParticleAttachment.AttachmentType.Static;
            _attachment.particleGroup = _group;
            _attachment.target = handObject.transform;

            _addedAttachment = true;
        }

        if (!_addedAttachment || (!_ropeIncrease.IsPressed() && !_ropeDecrease.IsPressed()))
            return;

        if (_ropeIncrease.IsPressed())
            _cursor.ChangeLength(speed * Time.deltaTime);
        if (_ropeDecrease.IsPressed())
            _cursor.ChangeLength(-speed * Time.deltaTime);

        _rope.RebuildConstraintsFromElements();

        var lastParticleId = _rope.elements[^1].particle2;
        _group.particleIndices.Clear();
        _group.particleIndices.Add(lastParticleId);
        _rope.solver.positions[lastParticleId] =
            _rope.solver.transform.InverseTransformPoint(handObject.transform.position);
        _attachment.particleGroup = _group;
        _attachment.target = handObject.transform;
    }
}

The solver doesn't seem to be initialized in Start which is why I moved the initial attach logic to Update.

If there is a better way to do this please let me know. I just want to grow/shrink the rope from the end but still have it stuck to my hand.



I think I may have figured it out... I changed the attach at the bottom to this:

Code:
       var lastParticleId = _rope.elements[^1].particle2;
        _group.particleIndices.Clear();
        _group.particleIndices.Add(lastParticleId);
        _rope.solver.positions[lastParticleId] =
            _rope.solver.transform.InverseTransformPoint(handObject.transform.position);
        _attachment.enabled = false;
        _attachment.target = null;
        _attachment.target = handObject.transform;
        _attachment.enabled = true;

The main changes were to disable, target null, then set target, and re-enable. Is that the intended way to do this?

Also I'm unsure how RebuildConstraintsFromElements() fits into the picture... should this be called every time after cursor.ChangeLength is called?
Reply
#2
Hi!

(18-03-2025, 12:20 AM)sinoth Wrote: I have a rope attached statically via ObiParticleAttachment to an immobile object at the beginning, and a movable object (my hand in VR) at the end.

The initial scripted attach works great and the rope behaves as expected. However I can't figure out how to properly grow or shrink via the cursor.

The cursor is set to Cursor Mu 1, Source Mu 1, Direction checked. The rope does grow and shrink. However it seems that the ObiParticleAttachment becomes invalidated because of this. I attempted to repair the attachment every time the cursor is changed but this also fails -- the rope hangs limp as though unattached.

There's two common pitfalls when mixing cursors and attachments:

The cursor's SourceMu determines the place along the rope where particle properties are sampled to create new particles at CursorMu. If the particle at SourceMu is attached to something, new particles generated by the cursor will also be attached.

On the other hand, if you use a cursor to shrink the rope and it's set up in a way that it points towards an attachment it may destroy the particle that's attached, effectively invalidating the attachment (as the attached particle no longer exists). This seems to be your case.

A simple solution to both issues is to make sure the cursor is pointing in the right direction, and making sure Source Mu is not placed where the attachment is.

(18-03-2025, 12:20 AM)sinoth Wrote: The solver doesn't seem to be initialized in Start which is why I moved the initial attach logic to Update.

Solvers are initialized whenever an actor is added to a previously empty solver. This may be during the actor's Start(), or at any point in time if you activate the actor later. Similarly, solvers clean up their resources once the last actor in them is removed. You can use the solver's OnInitialize and OnTeardown to get a callback for both events respectively.

Actors have a isLoaded boolean that lets you know whether the actor has been loaded by the solver. They also have OnBlueprintLoaded/OnBlueprintUnloaded callback events for the same purpose.

You can check the code in ObiParticleAttachment as reference, it uses OnBlueprintLoaded to initialize the attachment whenever the actor gets loaded into a solver.


(18-03-2025, 12:20 AM)sinoth Wrote: I think I may have figured it out... I changed the attach at the bottom to this:

Code:
       var lastParticleId = _rope.elements[^1].particle2;
        _group.particleIndices.Clear();
        _group.particleIndices.Add(lastParticleId);
        _rope.solver.positions[lastParticleId] =
            _rope.solver.transform.InverseTransformPoint(handObject.transform.position);
        _attachment.enabled = false;
        _attachment.target = null;
        _attachment.target = handObject.transform;
        _attachment.enabled = true;

The main changes were to disable, target null, then set target, and re-enable. Is that the intended way to do this?

Yes, see: https://obi.virtualmethodstudio.com/manu...ments.html

(18-03-2025, 12:20 AM)sinoth Wrote: Also I'm unsure how RebuildConstraintsFromElements() fits into the picture... should this be called every time after cursor.ChangeLength is called?

RebuildConstraintsFromElements is automatically called by cursors every time length is changed. No need to worry about it.

If you were manually altering the rope's structure by re-linking/deleting/adding elements, then you'd need to call it yourself.

kind regards,
Reply