Obi Official Forum
Help Some questions & requests - Printable Version

+- Obi Official Forum (http://obi.virtualmethodstudio.com/forum)
+-- Forum: Obi Users Category (http://obi.virtualmethodstudio.com/forum/forum-1.html)
+--- Forum: Obi Rope (http://obi.virtualmethodstudio.com/forum/forum-4.html)
+--- Thread: Help Some questions & requests (/thread-3548.html)

Pages: 1 2


Some questions & requests - landosilva - 04-08-2022

Hi! I've bought Obi Rope some years ago but only now I'm getting my hands on it, and I’m having some issues. I will list them below.

  1. How to set rope start and end position?
    I’m spawning ropes at runtime and I felt like the documentation and tutorials are missing some examples on this.
    What I was trying to do is to link the rope in two dynamic objects that has physics. So the attachment works but the rope is misplaced. I can’t figure out how to set the initial position of the rope’s start and end reliably. Here's the code:

    public void Connect(Transform origin, Transform end)

        _attachment1.target = origin;
        _attachment2.target = end;
    }

    public void SetStartPosition(Vector3 position)
    {
        int firstParticle = _rope.elements[0].particle1;
        _rope.solver.positions[firstParticle] = position;
    }

    public void SetLastPosition(Vector3 position)
    {
        int lastParticle = _rope.elements[^1].particle2;
        _rope.solver.positions[lastParticle] = position;
    }

    So basically, I'm spawning a rope inside an existing Solver in the Scene, calling "SetStartPosition", "SetLastPosition" and "Connect" in sequence. It seems to work only right after spawning. If I need to change the link to some other object I can't set the position again it seems.

  2. Rope is too stretchy. How to handle that?
    I don’t want my rope to stretch too much when moving my objects. I saw in the docs that I can increase the amount of substeps in the fixed updater, or decrease Unity’s fixed timestep, but I don’t think I had good results. What am I missing?

  3. How to reset Rope to it’s rest position?
    When I assign the rope to a new position (and when it works, see 1.) it seems to come with the motion from its original position, starting with a lot of movement already, and I don’t want that. How to stop the rope’s forces and return it to the rest state?
    Tried this but didn't get good results:

    private void StopRope()
    {
        foreach (int solverIndex in _rope.solverIndices)
        {
            _rope.solver.velocities[solverIndex] = Vector4.zero;
            _rope.solver.externalForces[solverIndex] = Vector4.zero;
        }
    }

  4. Is it possible to make it influenced by Time.timeScale?
    My game is changing Time.timeScale sometimes, but it doesn’t seem to affect ropes. There is a workaround?

  5. ArgumentOutOfRange ObiRopeCursor.cs:169
    I get this error sometimes when changing ropes length at runtime.
I think that's it for now. Any help is much appreciated.
Thanks!


RE: Some questions & requests - josemendez - 05-08-2022

Hi there!

(04-08-2022, 10:58 PM)landosilva Wrote: How to set rope start and end position?
I’m spawning ropes at runtime and I felt like the documentation and tutorials are missing some examples on this.
What I was trying to do is to link the rope in two dynamic objects that has physics. So the attachment works but the rope is misplaced. I can’t figure out how to set the initial position of the rope’s start and end reliably.

I assume your use case is to re-attach an existing rope to some other object, not creating a new rope between two points, correct? I ask this because re-attaching the rope will not change its rest length: if the two objects are further apart than the length of the rope, the rope will stretch.

Just tried your code, and it works fine for me. Setting the position of the particles and then setting the attachment targets is the correct way to do it. If it works right after instantiating the rope, I don't see any reason for it not to work later. Could you share a bit more about the context in which you're doing this?

Btw, the "scripting ropes" section of the manual has some sample code for getting the rope's start/end positions, setting them is basically the same, you just write into the solver.positions array instead of reading from it. This is what you already have in your code so I guess I'm being Captain Obvious ™Sonrisa
http://obi.virtualmethodstudio.com/manual/6.3/scriptingropes.html


(04-08-2022, 10:58 PM)landosilva Wrote: I don’t want my rope to stretch too much when moving my objects. I saw in the docs that I can increase the amount of substeps in the fixed updater, or decrease Unity’s fixed timestep, but I don’t think I had good results. What am I missing?

With a few substeps (8-10 are usually more than enough) ropes should not stretch at all, unless:

- There's comparatively heavy objects attached to them (for instance, a 100 kg object hanging from a 10 grams rope). Iterative solvers have a hard time dealing with large mass ratios, so keep them low (1:10 max)

- Your attachments are static (as opposed to dynamic), or you're moving the objects by directly setting their transform.position. This will basically bypass physics, since ropes rely on applying forces to rigidbodies. If you use static attachments or set positions directly, objects will ignore forces and ropes will blindly follow the objects they're attached to, stretching if required.

(04-08-2022, 10:58 PM)landosilva Wrote: How to reset Rope to it’s rest position?
When I assign the rope to a new position (and when it works, see 1.) it seems to come with the motion from its original position, starting with a lot of movement already, and I don’t want that. How to stop the rope’s forces and return it to the rest state?

All actors have a Teleport(position, rotation) function. You can call that, or if you want to do it yourself you need to set the particle's previous positions and interpolated positions as well:

Code:
m_Solver.positions[solverIndex] = yourPosition;
m_Solver.prevPositions[solverIndex] = yourPosition;
m_Solver.renderablePositions[solverIndex] = yourPosition;

If you want to reset the particles to their starting position (the one set in the blueprint), you can also call ResetParticles().

(04-08-2022, 10:58 PM)landosilva Wrote: Is it possible to make it influenced by Time.timeScale?
My game is changing Time.timeScale sometimes, but it doesn’t seem to affect ropes.

Obi uses Unity's FixedUpdate() by default, so it is affected by Time.timeScale. In fact there's a sample scene included (RopeAndJoints) that allows you to enter "bullet time" by enabling a checkbox, and it does this by setting Time.timeScale to 0.25 (see Obi/Samples/Common/SampleResources/Scripts/SlowmoToggler.cs)

(04-08-2022, 10:58 PM)landosilva Wrote: ArgumentOutOfRange ObiRopeCursor.cs:169
I get this error sometimes when changing ropes length at runtime.

I haven't received any similar reports, could you provide some steps to reproduce this? The line the error refers to looks like this:

Code:
m_CursorElement = rope.elements[Mathf.Max(0,index - 1)];

"index" is the element index where the cursor is located, and at least in theory it cannot be in a non-existent element.


RE: Some questions & requests - landosilva - 05-08-2022

‘josemendez’ Wrote:I assume your use case is to re-attach an existing rope to some other object, not creating a new rope between two points, correct? I ask this because re-attaching the rope will not change its rest length: if the two objects are further apart than the length of the rope, the rope will stretch.

Just tried your code, and it works fine for me. Setting the position of the particles and then setting the attachment targets is the correct way to do it. If it works right after instantiating the rope, I don’t see any reason for it not to work later. Could you share a bit more about the context in which you’re doing this?

Yes, that’s the case. In fact, what I’m really doing is: I have a dynamic Pooling System of ropes. When I’m spawning a fresh one (coming from Instantiate) it works just fine. But if it is coming from the pool (a disabled object in the scene) the attachment works, but the rope is misplaced.
If I do the usual Instantiate/Destroy it works just fine as well. So I was assuming the problem is with re-attaching ropes, but I should have mention that it is not exactly the case. Sorry for that!  Lengua
Anyway I still don’t know how to solve this, I want to figure out how to handle the issue instead of going with the simplest solution of not using pool, because I might face something similar in future stages of the project.

‘josemendez’ Wrote:- There’s comparatively heavy objects attached to them (for instance, a 100 kg object hanging from a 10 grams rope). Iterative solvers have a hard time dealing with large mass ratios, so keep them low (1:10 max)

Yes, that’s precisely the case. Lengua The ropes are very stretchy because they are attached to heavy objects.

‘josemendez’ Wrote:All actors have a Teleport(position, rotation) function. You can call that, or if you want to do it yourself you need to set the particle’s previous positions and interpolated positions as well:
If you want to reset the particles to their starting position (the one set in the blueprint), you can also call ResetParticles().

Tried everything but it’s still not working when it comes from the pool.

‘josemendez’ Wrote:Obi uses Unity’s FixedUpdate() by default, so it is affected by Time.timeScale.

If you see in the official Unity’s documentation about changing Time.timeScale: https://docs.unity3d.com/ScriptReference/Time-timeScale.html
They do like this:

Code:
if (Time.timeScale == 1.0f)
    Time.timeScale = 0.7f;
else
    Time.timeScale = 1.0f;
// Adjust fixed delta time according to timescale
// The fixed delta time will now be 0.02 real-time seconds per frame
Time.fixedDeltaTime = this.fixedDeltaTime * Time.timeScale;

And that’s what I’m doing as well, because only changing the Time.timeScale without changing Time.fixedDeltaTime makes the physics based objects have a not very smooth slow motion.
And it seems that doing so makes the ObiRope simulation operates in “normal time”. Is that correct? There is a workaround for it?

‘josemendez’ Wrote:I haven’t received any similar reports, could you provide some steps to reproduce this? The line the error refers to looks like this:

Yes, this is what I’m doing:

Code:
_ropeReturnTween = DOVirtual.Float(_rope.Length, 0f, 0.1f, UpdateRopeLength);
void UpdateRopeLength(float value) => _rope.SetLength(value);

The only thing I have inside the SetLength is:

Code:
_cursor.ChangeLength(Mathf.Min(length, _maxLength));

So basically, a tween transition from the rope’s restLength to 0 with a duration of 0.1 seconds. Sometimes, not always, doing it triggers this mentioned ArgumentOutOfRange exception.

Thank you very much for the fast reply!


RE: Some questions & requests - josemendez - 05-08-2022

(05-08-2022, 03:34 PM)landosilva Wrote: When I’m spawning a fresh one (coming from Instantiate) it works just fine. But if it is coming from the pool (a disabled object in the scene) the attachment works, but the rope is misplaced.If I do the usual Instantiate/Destroy it works just fine as well. So I was assuming the problem is with re-attaching ropes, but I should have mention that it is not exactly the case.
Hi again!

Not sure what you mean by "misplaced": during simulation, ropes don't really have a position/orientation (for obvious reasons, each particle can be in a completely different position). How can it be misplaced? do you mean the initial transform? are the particles not attached to the positions you set?

(05-08-2022, 03:34 PM)landosilva Wrote: Yes, that’s precisely the case. Lengua The ropes are very stretchy because they are attached to heavy objects.

A simple solution is to increase the mass of the rope particles. You can set mass per-control point in the path editor.
Note that iterative, maximal coordinates-based engines (which account for pretty much all realtime engines) have issues with large mass ratios. You can check this in Unity by simply placing a 10000 kg box on top of a 1 kg box and watch it explode Sonrisa.

If you want resilience against arbitrarily large mass ratios, you must use either a direct solver, or a reduced-coordinates approach (Featherstone's ABA is a good candidate). Both are considerably more expensive than Obi. Unity provides a built in reduced-coordinates solver in the form of articulations.


(05-08-2022, 03:34 PM)landosilva Wrote: Tried everything but it’s still not working when it comes from the pool.

I'm still not sure what you mean by "not working": you mention the attachment itself works ok but the rope is misplaced, can you show an example of this?

(05-08-2022, 03:34 PM)landosilva Wrote: If you see in the official Unity’s documentation about changing Time.timeScale: https://docs.unity3d.com/ScriptReference/Time-timeScale.html
They do like this:

Code:
if (Time.timeScale == 1.0f)
    Time.timeScale = 0.7f;
else
    Time.timeScale = 1.0f;
// Adjust fixed delta time according to timescale
// The fixed delta time will now be 0.02 real-time seconds per frame
Time.fixedDeltaTime = this.fixedDeltaTime * Time.timeScale;

This is just a terrible idea, regardless of it being in Unity's official docs. What this does is it scales down the duration of the physics step so that the amount of steps per frame are constant. This will alter the physical behavior of objects depending on the timescale, which is something you want to avoid: when in slow motion, joints will become stiffer, contacts snappier, etc. My advice would be to only do this when recording an in-game video for instance, but never during gameplay.

What you typically want is for physical behavior to remain constant regardless of the timescale, and interpolate render state in-between.

(05-08-2022, 03:34 PM)landosilva Wrote: And that’s what I’m doing as well, because only changing the Time.timeScale without changing Time.fixedDeltaTime makes the physics based objects have a not very smooth slow motion.

That's what physics state interpolation is for! Sonrisa both Unity rigidbodies and Obi solvers have an interpolation option that smoothes out physics. When you reduce the timescale, in-game time goes slower, however real-world time does not so the physics engine is stepped less often. In order to render something during the frames where the physics engine is not updated, the engine can smoothly interpolate between the current and the previous physics state.

See:
http://obi.virtualmethodstudio.com/manual/6.3/obisolver.html
https://docs.unity3d.com/ScriptReference/Rigidbody-interpolation.html

If you want to learn more about fixed timestepping and interpolation, I can recommend this article:
https://gafferongames.com/post/fix_your_timestep/

(05-08-2022, 03:34 PM)landosilva Wrote: Yes, this is what I’m doing:

Code:
_ropeReturnTween = DOVirtual.Float(_rope.Length, 0f, 0.1f, UpdateRopeLength);
void UpdateRopeLength(float value) => _rope.SetLength(value);

The only thing I have inside the SetLength is:

Code:
_cursor.ChangeLength(Mathf.Min(length, _maxLength));

So basically, a tween transition from the rope’s restLength to 0 with a duration of 0.1 seconds. Sometimes, not always, doing it triggers this mentioned ArgumentOutOfRange exception.

Thanks! will try to reproduce it and get back to you.


RE: Some questions & requests - landosilva - 06-08-2022

Hey! Thank you again for the awesome support! Gran sonrisa

‘josemendez’ Wrote:How can it be misplaced? do you mean the initial transform? are the particles not attached to the positions you set?
(…)
I’m still not sure what you mean by “not working”: you mention the attachment itself works ok but the rope is misplaced, can you show an example of this?

I will explain in the images below. I have one cowboy character that has a rope. This rope is attached to both the cowboy and the ellipse in the end.
Note that in the first image, it’s a new fresh instantiated rope and everything is alright. The second image, it’s the exact same rope, and even though it’s attached to both the cowboy and the ellipse, because it follow their movement, is now misplaced. Think of that as I moved the “Local Position” of an object inside a parent and now I’m moving the parent around.
I don’t think it is something with my code because the new ropes are fine, the problem is with the ones coming from the pool, and they pass through exactly the same code. For some reason the positions are not properly set.

1. https://ibb.co/KhjvpWc
2. https://ibb.co/fGcswwN

‘josemendez’ Wrote:If you want resilience against arbitrarily large mass ratios, you must use either a direct solver, or a reduced-coordinates approach (Featherstone’s ABA is a good candidate). Both are considerably more expensive than Obi. Unity provides a built in reduced-coordinates solver in the form of articulations.

I will take a look on that. Thank you very much for sharing!

‘josemendez’ Wrote:This is just a terrible idea, regardless of it being in Unity’s official docs.
(…)
What you typically want is for physical behavior to remain constant regardless of the timescale, and interpolate render state in-between.

Well, that’s make total sense actually. I will try another approach for my slow motions then.

Thank you very much! I will definitely leave a review in the Asset Store later!  Gran sonrisa


RE: Some questions & requests - josemendez - 08-08-2022

(06-08-2022, 02:59 PM)landosilva Wrote: I don’t think it is something with my code because the new ropes are fine, the problem is with the ones coming from the pool, and they pass through exactly the same code. For some reason the positions are not properly set.

Thanks! I understand the problem now. How are you getting ropes out of the pool? I've quickly tried a pooling scheme but it works fine for me, here's the pool script:

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

public class RopePool : MonoBehaviour
{
    public List<GameObject> pool;
    public GameObject source;

    public Transform originTransform;
    public Transform endTransform;

    void Start()
    {
        pool = new List<GameObject>();
        GameObject tmp;

        for (int i = 0; i < 5; i++)
        {
            tmp = Instantiate(source);
            tmp.transform.parent = source.transform.parent;
            tmp.SetActive(false);
            pool.Add(tmp);
        }
    }

    GameObject GetPooledRope()
    {
        for (int i = 0; i < 5; i++)
        {
            if (!pool[i].activeInHierarchy)
            {
                return pool[i];
            }
        }
        return null;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            var obj = GetPooledRope();
            if (obj != null)
            {
                obj.SetActive(true);
                var rope = obj.GetComponent<ObiRope>();
                var attach = obj.GetComponent<RopeAttach>();

                attach.SetStartPosition(rope.solver.transform.InverseTransformPoint(originTransform.position));
                attach.SetLastPosition(rope.solver.transform.InverseTransformPoint(endTransform.position));
                attach.Connect(originTransform, endTransform);
            }
        }
    }    
}

And the script to attach the rope:

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

public class RopeAttach : MonoBehaviour
{
    ObiRope rope;
    public ObiParticleAttachment attachment1;
    public ObiParticleAttachment attachment2;

    void Awake()
    {
        rope = GetComponent<ObiRope>();
    }

    public void Connect(Transform origin, Transform end)
    {
        attachment1.target = origin;
        attachment2.target = end;
    }

    public void SetStartPosition(Vector3 position)
    {
        int firstParticle = rope.elements[0].particle1;
        rope.solver.positions[firstParticle] = position;
    }

    public void SetLastPosition(Vector3 position)
    {
        int lastParticle = rope.elements[rope.elements.Count - 1].particle2;
        rope.solver.positions[lastParticle] = position;
    }
}

The only thing I think could be causing trouble is passing the positions to SetStartPosition and SetLastPosition, all actor properties in Obi are expressed in solver space (the solver's local space, that is) so if you're passing world space positions and moving the solver in-between adding ropes, a problem similar to the one you describe would take place. You need to pass positions expressed in solver space, you can use InverseTransformPoint as I do in the code above. See:
http://obi.virtualmethodstudio.com/manual/6.3/scriptingparticles.html

Quote:Each ObiSolver has an array for each one of these properties, that stores the current data for all particles in use by any actor managed by the solver. All spatial properties (positions, orientations, velocities, vorticities, etc) are expressed in the solver's local space.



RE: Some questions & requests - landosilva - 08-08-2022

Hey! Thank you again!

I tested your code with some modifications to be more similar to what I have and I'm having the same problem!
The key to reproduce the issue is:
  1. Use the same rope that was previously attached to something. In your example you are using new ones instead of reusing them.
  2. Move the attachments around.
Here's a video using your example:
https://streamable.com/675qmg

Here's a Unity Package with my modifications:
https://drive.google.com/file/d/1myw95HjidMh3eBGnpYPdorW8mfa-2tme/view?usp=sharing

Please let me know if I'm doing something that I shouldn't!


RE: Some questions & requests - josemendez - 08-08-2022

(08-08-2022, 09:46 AM)landosilva Wrote: Hey! Thank you again!

I tested your code with some modifications to be more similar to what I have and I'm having the same problem!
The key to reproduce the issue is:
  1. Use the same rope that was previously attached to something. In your example you are using new ones instead of reusing them.
  2. Move the attachments around.
Here's a video using your example:
https://streamable.com/675qmg

Here's a Unity Package with my modifications:
https://drive.google.com/file/d/1myw95HjidMh3eBGnpYPdorW8mfa-2tme/view?usp=sharing

Please let me know if I'm doing something that I shouldn't!

Hi!

Attachments do not attach the rope to a point, they attach it to a target object. If you do this:

Code:
sameTargetItAlreadyHas.position = RandomPos;
rope.solver.positions[index] = sameTargetItAlreadyHas.position;
attachment.target = sameTargetItAlreadyHas;

The attachment is always active and its target object never actually changes. This line:
Code:
rope.solver.positions[index] = sameTargetItAlreadyHas.position;

Will have no effect since the -still active- attachment will regard it as a violation of the attachment constraint: "hey, something or someone has moved the rope away from where it should be attached!" and immediately enforce the constraint by setting the particle position back to its original value. Since the target object had moved around while the rope was inactive, but attachments always interpret positions as relative to the target, the rope is kept attached at its original point relative to the target's new position. From the manual:

Quote:It's important to realize that attachments do not move the particle group to the position of the target object, nor the target object to the position of the particle group. Moving the particle group to the target's position would collapse all particles into a single point, and would result in a kickback effect once the particles spring back to their original positions. Moving the target could cause a whole palette of issues depending on your game logic.

Instead, particles in the group are attached at their current position relative to the target object.


The solution is simple: disable the attachment when the rope is unused, re-enable the attachment when the rope gets reused. This way, the attachment knows that the it should re-attach the rope in its new position relative to the target object.

In the RopeAttach.cs script, add a new Disconnect() method and modify Connect():

Code:
public void Connect(Transform origin, Transform end)
    {
        attachment1.target = origin;
        attachment2.target = end;
        attachment1.enabled = true;
        attachment2.enabled = true;
    }

    public void Disconnect()
    {
        attachment1.enabled = false;
        attachment2.enabled = false;
    }

In RopePool.cs Update() method, add a new line at the else clause:

Code:
else
{
      //disconnect the rope before deactivating it.
      currentRope.GetComponent<RopeAttach>().Disconnect();

      currentRope.SetActive(false);
      currentRope.transform.SetParent(null);
      currentRope = null;
}



RE: Some questions & requests - landosilva - 08-08-2022

(08-08-2022, 10:54 AM)josemendez Wrote: The solution is simple: disable the attachment when the rope is unused, re-enable the attachment when the rope gets reused. This way, the attachment knows that the it should re-attach the rope in its new position relative to the target object.

Hi!

Indeed! This solution did the job! The rope is now being (re)attached properly to the new positions! Gran sonrisa
Sorry for all the trouble, now that I know it was quite obvious that I had to do it. 

Just one last request, since we are here: how can I avoid this spring effect when attaching to the new positions? I want the rope to have no motion after the attachment. This happens with both the new rope and the one being retached, so would be nice to know!

https://streamable.com/mq6r58

Thank you very much!


RE: Some questions & requests - josemendez - 08-08-2022

(08-08-2022, 02:09 PM)landosilva Wrote: Just one last request, since we are here: how can I avoid this spring effect when attaching to the new positions? I want the rope to have no motion after the attachment. This happens with both the new rope and the one being retached, so would be nice to know!

https://streamable.com/mq6r58

Thank you very much!

Hi!

You're teleporting the ends of the rope to new positions, so naturally the rope reacts physically to this (imagine a rope being shaken by its ends).

If you wanted to get rid of this, you would need to teleport all particles in the rope to a new position and not just the ends (placing them in a straight line from Origin transform to End transform is a good idea). Also set all particle velocities to zero to remove any unwanted velocity from the previous use of the rope.

kind regards,