Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Attaching a rope and updating position multiple times
#1
Hello,

I've been trying for the last couple of days to get the rope to do what we need and just can't quite get it to play nicely. Hoping you can help.

The use case is as follows:

  • The rope is attached to the ground at one end
  • Initially, the other end is attached to the player
  • At specific places, when the player initiates it, we want the rope to attach to a point on the wall
  • At this point, the rope would have three attachments (the ground, the point on the wall, and the player)
  • This will then happen repeatedly so the rope may eventually have six attachments for example (ground, wall, wall, wall, wall, player)
My first approach was to create a single rope and use a pre-built blueprint and then just update the blueprint as time went on, adding new particle attachments and using the cursor to extend the length of the rope, but I couldn't get it to work.

Second, I tried generating a blueprint with code in case that was the issue, no luck.

My current approach was to generate a new rope for each segment (so each rope would just have two attachments and there would be a series of ropes in a list).

When I try to do this, the first rope attaches as desired properly. The second rope, attaches, but the positions of the particles do no update. So I can see the particles are attached but they are not in the right place.

Here is the code I am using to control the rope:

Code:
public class RopeController : MonoBehaviour
{
    private ObiSolver _solver;
    private ObiRope _rope;
    private ObiRopeBlueprint _blueprint;
    private int _ropeId = -1;

    private string _cp1Name;
    private string _cp2Name;

    public void Init(int ropeId, float ropeLength, float ropeMass,
        float ropeRotationalMass, Transform attachment1, Transform attachment2)
    {
        StartCoroutine(InitCoro(ropeId, ropeLength, ropeMass, ropeRotationalMass, attachment1, attachment2));
    }

    private IEnumerator InitCoro(int ropeId, float ropeLength, float ropeMass,
        float ropeRotationalMass, Transform attachment1, Transform attachment2)
    {
        _ropeId = ropeId;
       
        _solver = GetComponentInParent<ObiSolver>();
        if (_solver == null)
        {
            Debug.LogError("No ObiSolver found in parent");
        }
        _rope = GetComponent<ObiRope>();
        _rope.GetComponent<MeshRenderer>().enabled = false;

        _blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
        _blueprint.resolution = 1f;
        _blueprint.pooledParticles = 100;
        _blueprint.thickness = 0.03f;

        int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0);
        Vector3 posCp1 = Vector3.zero;
        _cp1Name = $"Rope{_ropeId}_CP1";
        Vector3 posCp2 = new Vector3 (-4, 0, 0);
        _cp2Name = $"Rope{_ropeId}_CP2";
        _blueprint.path.Clear();
        _blueprint.path.AddControlPoint(posCp1, Vector3.zero, Vector3.zero, Vector3.up,
            ropeMass, ropeRotationalMass, 1f, filter, Color.white, _cp1Name);
        _blueprint.path.AddControlPoint(posCp2, Vector3.zero, Vector3.zero, Vector3.up,
            ropeMass, ropeRotationalMass, 1f, filter, Color.white, _cp2Name);
        _blueprint.path.FlushEvents();

        yield return _blueprint.Generate();

        _rope.ropeBlueprint = _blueprint;

        SetAttachment(attachment1, _cp1Name);
        SetAttachment(attachment2, _cp2Name);

        yield return null;

        _rope.GetComponent<MeshRenderer>().enabled = true;


        _rope.ropeBlueprint = _blueprint;
    }

    private void SetAttachment(Transform attachmentTarget, string particleGroupName)
    {
        // add a particle attachment to the rope
        var particleAttachment = _rope.gameObject.AddComponent<ObiParticleAttachment>();

        // disable the attachment
        particleAttachment.enabled = false;

        // find the particle group we want to attach to
        var particleGroup = _rope.ropeBlueprint.groups.Find(x => x.name == particleGroupName);

        // set the attachment's particle group to the one we found
        particleAttachment.particleGroup = particleGroup;

        // get the particle index of the first particle in the group
        var particleIndex = particleAttachment.particleGroup.particleIndices[0];

        // set the position of that particle to the attachment target
        _solver.positions[particleIndex] = _solver.transform.InverseTransformPoint(attachmentTarget.position);

        // set the attachment's target to the transform we want to attach to
        particleAttachment.target = attachmentTarget;

        // re-enable the attachment
        particleAttachment.enabled = true;
    }
}


and these are instantiated from a system class as follows:

Code:
public class RopeSystemIncremental : MonoBehaviour
{
    [SerializeField] private Material _ropeMat;
    [SerializeField] private Transform _groundAttachmentTra;
    [SerializeField] private Transform _playerAttachmentTra;
    [SerializeField] private Transform _TESTINGgrabbableAttachmentTra;
    [SerializeField] private RopeController _ropePF;

    private float _defaultRopeLength = 4f;
    private float _ropeMass = 0.1f;
    private float _ropeRotationalMass = 0.1f;
    private ObiSolver _solver;

    private List<RopeController> _ropes = new List<RopeController>();

    private void Awake()
    {
        _solver = GetComponentInChildren<ObiSolver>();
    }

    [ContextMenu("FirstOne")]
    private void FirstOne()
    {
        InstantiateRope(_groundAttachmentTra, _playerAttachmentTra);
    }

    [ContextMenu("SecondOne")]
    private void SecondOne()
    {
        InstantiateRope(_TESTINGgrabbableAttachmentTra, _playerAttachmentTra);
    }

    private RopeController InstantiateRope(Transform attachment1, Transform attachment2)
    {
        var ropeCont = Instantiate(_ropePF, Vector3.zero, Quaternion.identity, _solver.transform);
        _ropes.Add(ropeCont);
        ropeCont.gameObject.name = "Rope " + _ropes.Count;
        ropeCont.Init(_ropes.Count, _defaultRopeLength, _ropeMass, _ropeRotationalMass, attachment1, attachment2);
        return ropeCont;
    }
}

Been hitting my head against the wall with this for a while now! Would really appreciate your help. Many thanks in advance.
Reply
#2
I should also add that I'm happy to solve this either with multiple ropes to create the whole path, or one single rope that is being extended, though I had quite a bit of trouble with getting it to behave properly with the cursor for changing length - and to not make the whole rope have a big wobble when adding more to it.
Reply
#3
Hi!

This bit is wrong:

Code:
// get the particle index of the first particle in the group
var particleIndex = particleAttachment.particleGroup.particleIndices[0];

// set the position of that particle to the attachment target
_solver.positions[particleIndex] = _solver.transform.InverseTransformPoint(attachmentTarget.position);

You're using the particle index in the blueprint as if it were a particle index in the solver. Once you add more than 1 rope to the solver, these cease to be the same, so your code is effectively always setting the position of particles in the first rope.

To retrieve the index of a particle in the solver, you must use the actor's solverIndices array. This maps from actor/blueprint index to solver index. See: http://obi.virtualmethodstudio.com/manua...icles.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 (see architecture), 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.

Should be like this:

Code:
// set the position of that particle to the attachment target
_solver.positions[_rope.solverIndices[particleIndex]] = _solver.transform.InverseTransformPoint(attachmentTarget.position);
Reply
#4
That got it! Thanks so much Sonrisa

Could I ask another quick question here - it's about the updating of positions of attachments.

Is there a way to prevent the rope pinging into the new position and have it wobbling around after the change? Is it to do with changing the masses or something like that? I just want to change the attachment and have the rope immediately be settled into it's new position.
Reply
#5
I've solved this by specifically placing the particles roughly where I want them before switching the rope renderer back on so there's less of a spring
Reply
#6
(28-02-2024, 02:06 PM)stateofplay Wrote: I've solved this by specifically placing the particles roughly where I want them before switching the rope renderer back on so there's less of a spring
Do you have code?
Reply