Obi Official Forum

Full Version: Attaching to Rope, Jitter.
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I've been looking at several solutions to connecting something / object to a rope. 

I ended up using this: 

Code:
pathSmoother = ropeComp.GetComponent<ObiPathSmoother>();
ObiPathFrame section = pathSmoother.GetSectionAt(ropePosition);
 m_Character.transform.position = pathSmoother.transform.TransformPoint(section.position);

to move the character to the rope position.  But my rope is attached to a boat and I'm getting a lot of jitter, moving forward and back on the rope, think it's because of stretching. 
I also Tried LateUpdate, FixedUpdate.   But still pops / jitters ups and down the rope. 

Would Pin Constraint be more stable?  
I couldn't find any code examples. 

Maybe because I don't understand how to read the doc correctly - Hahaha!
any ideas welcome Thanks!
(07-12-2022, 06:39 PM)idmah Wrote: [ -> ]I've been looking at several solutions to connecting something / object to a rope. 

I ended up using this: 

Code:
pathSmoother = ropeComp.GetComponent<ObiPathSmoother>();
ObiPathFrame section = pathSmoother.GetSectionAt(ropePosition);
 m_Character.transform.position = pathSmoother.transform.TransformPoint(section.position);

to move the character to the rope position.  But my rope is attached to a boat and I'm getting a lot of jitter, moving forward and back on the rope, think it's because of stretching. 
I also Tried LateUpdate, FixedUpdate.   But still pops / jitters ups and down the rope. 

Hi!

This code simply places an object at a point along the rope. It involves no actual physical simulation of the attachment, so it’s completely unaffected by when you update the rope (late update, fixed update, etc). As long as this is called after rope simulation every frame, it should move the object to a point in the rope with no jittering at all (unless you rope is extremely overstretched and you’re using sequential evaulation of distance constraints, as irregular tension patterns will appear - see “evaluation mode” in the solver page: http://obi.virtualmethodstudio.com/manua...olver.html but this is a pretty bad problem on itself)

Could you share a video/animation that shows the jittering issue, as well as your complete code?

Quote:Would Pin Constraint be more stable?  
I couldn't find any code examples. 

Pin constraints are meant for a completely different use case: two-way coupled attachment to a rigidbody. Unless this is what you are after (for instance if your character is physically simulated as well) there’s no point in using pin constraints. You can find a code example in the “scripting constraints” section of the manual:

http://obi.virtualmethodstudio.com/manua...aints.html
Here it is. Mainly when I move the rope or it stretches.
Sorry the YouTube embedding seem to not be working for me. 

Youtube-Link

The code is extremely messy and a jumble of things I randomly  tried. Apologies!
I was going to clean up the code when I got it all working.  

Oh by the way running on OSx 12.6 Apple Silicon. too. 

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

public class FindClimbObiRope : MonoBehaviour
{
    [SerializeField] private GameObject m_Character;
    [SerializeField] private Transform handRPos;
    [SerializeField] private Transform handLPos;

    private int m_ActorParticles = 0;
    private int m_MountParticle = 0;
    private int m_CurrentParticleOnRopeLength = 0;
    private GameObject m_DetectedObject;
    private bool ClimbingWithRightHand = true;
    private ObiActor ropeComp;
    public ObiRope myRope;
    public Vector3 foundNearest;
    private Vector3 stickItPosition;
    private Vector3 oldPosition;
    public bool stuckOnRope;
    private ObiPathSmoother pathSmoother;
    private float currentPositionOnPath; // float 0-1

    // Start is called before the first frame update
    void Start()
    {

        ropeComp = myRope.GetComponent<ObiActor>();
        pathSmoother = ropeComp.GetComponent<ObiPathSmoother>();
        if (ropeComp == null && pathSmoother != null)
        {
            Debug.Log("No rope found");

        }
        stuckOnRope = false;


    }

    void LateUpdate()
    {
        int indexToRopeParticle = 0;
        // scann realtime all the time.
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            if (stuckOnRope == false)
            {
                indexToRopeParticle = FindClosestRopeParticle();
                if (currentPositionOnPath>=0 && currentPositionOnPath<=1) // indexToRopeParticle != 0
                {
                    oldPosition = m_Character.transform.position;
                    //Debug.DrawLine(m_Character.transform.position, ropeComp.GetParticlePosition(ropeComp.solverIndices[indexToRopeParticle]), Color.magenta);
                    // Debug.Log("Closest Particle is:" + indexToRopeParticle + " at " + ropeComp.solver.positions[indexToRopeParticle]);
                    foundNearest = ropeComp.GetParticlePosition(indexToRopeParticle);
                    // Real world position of particle //
                    stickItPosition = ropeComp.GetParticlePosition(ropeComp.solverIndices[indexToRopeParticle]);
                    stuckOnRope = true;

                }
                else
                {
                    Debug.Log("Nothing found");
                }
            }
            else if (stuckOnRope)
            {
                m_Character.transform.position = oldPosition;
                stuckOnRope = false;
           }

        }
        if (stuckOnRope && (currentPositionOnPath >= 0 && currentPositionOnPath <= 1))
        {
            if (Input.GetKeyDown(KeyCode.LeftCurlyBracket)) {
                currentPositionOnPath -= 0.01f;
                if(currentPositionOnPath <0)
                {
                    currentPositionOnPath = 0.0f;
                }
            }
            if (Input.GetKeyDown(KeyCode.RightCurlyBracket))
            {
                currentPositionOnPath += 0.01f;
                if (currentPositionOnPath > 1)
                {
                    currentPositionOnPath = 1.0f;
                }
            }
            // Snap Character to Rope Particle //

            StickToRope(currentPositionOnPath);

        }
    }

    private void RightHand()
    {
        ClimbingWithRightHand = true;
        Debug.Log("using the right hand to climb.");
    }

    private void LeftHand()
    {

        ClimbingWithRightHand = false;
        Debug.Log("using the left hand to climb.");
    }

    public int FindClosestRopeParticle()
    {
        //Get the full length of the rope.
        m_ActorParticles = ropeComp.solverIndices.Length;
        Debug.Log("Finding Closest");
        var closestIndexDistance = 8.0f; // max jump distance
        int closestIndex = 0;
        currentPositionOnPath = -1.0f;

        if (handRPos == null || handLPos == null)
        {
            Debug.LogError("Hand positions haven't been assigned on the rope climb ability.");
            return closestIndex;
        }
        Vector3 handPos = ClimbingWithRightHand ? handRPos.position : handLPos.position;
        Debug.Log("Hand pos:" + handLPos.transform.position);
       
        currentPositionOnPath = 100.0f;
        float mu = 0;
        while (mu < 1)
        {
            mu += 0.01f;
            ObiPathFrame sections = pathSmoother.GetSectionAt(mu);//currentPositionOnPath
            var pos = pathSmoother.transform.TransformPoint(sections.position);
            float distance = Vector3.Distance(m_Character.transform.position, pos);
            if (distance < closestIndexDistance)
            {
                closestIndexDistance = distance;
                //closestIndex = solverIndex;
                currentPositionOnPath = mu;
                // Debug.Log("current Postion:" + posPathFrame.position+" index:"+ currentPositionOnPath);
            }
        }
        Debug.Log("Closest:" + ropeComp.GetParticlePosition(ropeComp.solverIndices[closestIndex]));
        // *** will eventually return currentPositionOnPath  ***
        return closestIndex;
    }

    void StickToRope(float ropePosition)
    {
       
        pathSmoother = ropeComp.GetComponent<ObiPathSmoother>();
        ObiPathFrame section = pathSmoother.GetSectionAt(ropePosition);
        m_Character.transform.position = pathSmoother.transform.TransformPoint(section.position);
         
    }
}
Hi!

The code has some important problems, let's go over them one at a time:

(08-12-2022, 04:34 AM)idmah Wrote: [ -> ]currentPositionOnPath += 0.01f;

This will cause a large amount of jitter right off the bat. You're moving the object a constant distance every frame, but this is not what you want: you want it to move at a constant speed.

Frames have different duration: sometimes you get 50 frames in a second, sometimes you get 40, sometimes 63, etc... moving the same distance each frame means sometimes you'll move 50 units per second, sometimes 40, sometimes 63, and so on, this will cause the character to jump forward and backward along the rope as you move it. Worse still: on computers that run the game faster, your character will move faster, and on computers that have trouble running the game, the character will move slower.

Since each frame has a different duration, you can calculate the distance you want to move each frame by knowing that velocity = space / time, so space = time * velocity:

Code:
currentPositionOnPath += Time.deltaTime * velocity; // velocity units are % of rope per second.

Note this is the basic way to move objects at a constant speed in any software, not just when using Obi/Unity.

(08-12-2022, 04:34 AM)idmah Wrote: [ -> ]FindClosestRopeParticle()

This method doesn't find the closest particle in the rope, most of the time, it will return either a random point in the rope or throw an out of bounds exception. The problem is that you're iterating over path frames, getting the index of the closest one, then using its index to get the position of a particle. In general the number of rendered path frames is not the same as the number of particles: there may be less path frames (if the rope is decimated)  or more (if the rope is smoothed), so this code might access data outside the particle arrays. Also path frames are not sorted the same way particles are, so if you happen to fall within the solverIndices array it may return a random index.

To get the closest particle in the rope, iterate trough its active particles directly:

Code:
for (int i = 0; i < rope.activeParticleCount; ++i)
{
            // get position of particle i in world space:
            ar pos = pathSmoother.transform.TransformPoint(ropeComp.solver.positions[ropeComp.solverIndices[i]]);

            // compare it to the character's position
            float distance = Vector3.Distance(m_Character.transform.position, pos);
            if (distance < closestIndexDistance)
            {
                  // update closest particle
            }
}

With these corrections, movement along the rope is buttery smooth as long as the rope is not decimated (path smoother decimation set to zero). This is important because if your rope is decimated, the number and location of path frames will adaptively change as the rope curvature changes (more frames where curvature is larger, less frames where the rope is straight), so you no longer can use path frames to position the object along the rope. If this is your case, you must use elements instead. Here's how the StickToRope function would look like when using elements:

Code:
void StickToRope (float ropePosition)
    {
        // calculate length along the rope in meters:
        var rope = ropeComp as ObiRope;
        float length = rope.CalculateLength() * ropePosition;

        // iterate trough elements, subtracting the length of each element until we find the one that
        // contains the point we are interested in:
        for (int i = 0; i < rope.elements.Count; ++i)
        {
            var pos1 = rope.solver.transform.TransformPoint(rope.solver.positions[rope.elements[i].particle1]);
            var pos2 = rope.solver.transform.TransformPoint(rope.solver.positions[rope.elements[i].particle2]);
            var elmLength = Vector3.Distance(pos1, pos2);

            if (length - elmLength < 0)
            {
                // found the element we were looking for, just interpolate its ends:
                m_Character.transform.position = Vector3.Lerp(pos1, pos2, length / elmLength);
                return;
            }
            length -= elmLength;
        }
    }




let me know if you need further help,

kind regards
(08-12-2022, 10:02 AM)josemendez Wrote: [ -> ]Hi!

The code has some important problems, let's go over them one at a time:


This will cause a large amount of jitter right off the bat. You're moving the object a constant distance every frame, but this is not what you want: you want it to move at a constant speed.

Frames have different duration: sometimes you get 50 frames in a second, sometimes you get 40, sometimes 63, etc... moving the same distance each frame means sometimes you'll move 50 units per second, sometimes 40, sometimes 63, and so on, this will cause the character to jump forward and backward along the rope as you move it. Worse still: on computers that run the game faster, your character will move faster, and on computers that have trouble running the game, the character will move slower.

Since each frame has a different duration, you can calculate the distance you want to move each frame by knowing that velocity = space / time, so space = time * velocity:

Code:
currentPositionOnPath += Time.deltaTime * velocity; // velocity units are % of rope per second.

Note this is the basic way to move objects at a constant speed in any software, not just when using Obi/Unity.


This method doesn't find the closest particle in the rope, most of the time, it will return either a random point in the rope or throw an out of bounds exception. The problem is that you're iterating over path frames, getting the index of the closest one, then using its index to get the position of a particle. In general the number of rendered path frames is not the same as the number of particles: there may be less path frames (if the rope is decimated)  or more (if the rope is smoothed), so this code might access data outside the particle arrays. Also path frames are not sorted the same way particles are, so if you happen to fall within the solverIndices array it may return a random index.

To get the closest particle in the rope, iterate trough its active particles directly:

Code:
for (int i = 0; i < rope.activeParticleCount; ++i)
{
            // get position of particle i in world space:
            ar pos = pathSmoother.transform.TransformPoint(ropeComp.solver.positions[ropeComp.solverIndices[i]]);

            // compare it to the character's position
            float distance = Vector3.Distance(m_Character.transform.position, pos);
            if (distance < closestIndexDistance)
            {
                  // update closest particle
            }
}

With these corrections, movement along the rope is buttery smooth as long as the rope is not decimated (path smoother decimation set to zero). This is important because if your rope is decimated, the number and location of path frames will adaptively change as the rope curvature changes (more frames where curvature is larger, less frames where the rope is straight), so you no longer can use path frames to position the object along the rope. If this is your case, you must use elements instead. Here's how the StickToRope function would look like when using elements:

Code:
void StickToRope (float ropePosition)
    {
        // calculate length along the rope in meters:
        var rope = ropeComp as ObiRope;
        float length = rope.CalculateLength() * ropePosition;

        // iterate trough elements, subtracting the length of each element until we find the one that
        // contains the point we are interested in:
        for (int i = 0; i < rope.elements.Count; ++i)
        {
            var pos1 = rope.solver.transform.TransformPoint(rope.solver.positions[rope.elements[i].particle1]);
            var pos2 = rope.solver.transform.TransformPoint(rope.solver.positions[rope.elements[i].particle2]);
            var elmLength = Vector3.Distance(pos1, pos2);

            if (length - elmLength < 0)
            {
                // found the element we were looking for, just interpolate its ends:
                m_Character.transform.position = Vector3.Lerp(pos1, pos2, length / elmLength);
                return;
            }
            length -= elmLength;
        }
    }




let me know if you need further help,

kind regards

Thanks I've got a lot to learn. 

So things that are puzzling, 
[Image: view?usp=sharing]

Code:
var pos = pathSmoother.transform.TransformPoint(ropeComp.solver.positions[ropeComp.solverIndices[i]]);

Seem to still be returning local coordinates. 
Code:
Hand pos:(-0.11, 4.48, -3.79)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:135)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:0 position:(0.92, 3.46, 0.14) Magnatued:4.188713
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:0 At pos:(0.92, 3.46, 0.14)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:1 position:(0.96, 3.47, 0.09) Magnatued:4.15387
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:1 At pos:(0.96, 3.47, 0.09)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:2 position:(1.50, 3.23, 0.08) Magnatued:4.370043
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:3 position:(2.04, 3.00, 0.11) Magnatued:4.697079
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:4 position:(2.60, 2.79, 0.21) Magnatued:5.113725
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:5 position:(3.16, 2.66, 0.35) Magnatued:5.57721
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:6 position:(3.72, 2.65, 0.48) Magnatued:6.021061
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:7 position:(4.28, 2.73, 0.58) Magnatued:6.433954
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:8 position:(4.83, 2.91, 0.62) Magnatued:6.808742
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:9 position:(5.37, 3.13, 0.62) Magnatued:7.164061
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:10 position:(5.95, 3.36, 0.56) Magnatued:7.544006
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:11 position:(6.52, 3.61, 0.50) Magnatued:7.948319
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:12 position:(7.08, 3.90, 0.44) Magnatued:8.36546
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:13 position:(7.62, 4.22, 0.38) Magnatued:8.790097
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:14 position:(8.03, 4.57, 0.33) Magnatued:9.118522
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:15 position:(8.39, 4.96, 0.26) Magnatued:9.431477
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:16 position:(8.72, 5.38, 0.18) Magnatued:9.724949
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:17 position:(9.37, 5.73, 0.03) Magnatued:10.29199
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:18 position:(10.01, 5.35, -0.04) Magnatued:10.82423
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

Closest:1 position on path:0.05263158
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:170)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

But The probe /  m_Character is in the centre of the rope,  and it keeps finding the first particle in the list. Not sure why. Local vs World co-ordinates? 
it was a direct copy paste of your code. 

but if I use: 
Code:
pos = ropeComp.GetParticlePosition(ropeComp.solverIndices[i]);

output is this: 

Code:
Hand pos:(-0.11, 4.48, -3.79)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:135)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:0 position:(0.55, 2.91, 0.31) Magnatued:4.433905
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:0 At pos:(0.55, 2.91, 0.31)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:1 position:(0.50, 2.93, 0.27) Magnatued:4.384596
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:1 At pos:(0.50, 2.93, 0.27)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:2 position:(0.30, 2.65, -0.28) Magnatued:3.976799
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:2 At pos:(0.30, 2.65, -0.28)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:3 position:(0.16, 2.36, -0.85) Magnatued:3.629177
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:3 At pos:(0.16, 2.36, -0.85)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:4 position:(0.11, 2.12, -1.46) Magnatued:3.32097
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:4 At pos:(0.11, 2.12, -1.46)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:5 position:(0.13, 1.99, -2.09) Magnatued:3.024831
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:5 At pos:(0.13, 1.99, -2.09)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:6 position:(0.22, 2.01, -2.72) Magnatued:2.714578
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:6 At pos:(0.22, 2.01, -2.72)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:7 position:(0.34, 2.17, -3.31) Magnatued:2.401109
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:7 At pos:(0.34, 2.17, -3.31)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:8 position:(0.46, 2.44, -3.87) Magnatued:2.119863
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:8 At pos:(0.46, 2.44, -3.87)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:9 position:(0.57, 2.77, -4.39) Magnatued:1.930964
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:9 At pos:(0.57, 2.77, -4.39)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:10 position:(0.63, 3.20, -4.93) Magnatued:1.86834
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

current Postion:10 At pos:(0.63, 3.20, -4.93)
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:162)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:11 position:(0.66, 3.61, -5.47) Magnatued:2.038882
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:12 position:(0.66, 4.01, -6.02) Magnatued:2.409439
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:13 position:(0.64, 4.37, -6.60) Magnatued:2.910936
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:14 position:(0.60, 4.60, -7.13) Magnatued:3.417766
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:15 position:(0.56, 4.80, -7.67) Magnatued:3.954642
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:16 position:(0.52, 4.94, -8.24) Magnatued:4.515073
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:17 position:(0.45, 4.78, -8.99) Magnatued:5.239684
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

point:18 position:(0.39, 4.25, -9.62) Magnatued:5.860329
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

Closest:10 position on path:0.5263158
UnityEngine.Debug:Log (object)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:170)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:55)

Which looks correct to me.  Just wondering if there is some disconnect with my logic or it's a bug or something OSx unity..
Not even sure how to test this.  Sorry for being so dumb, working hard to get smarter.  Hahaha!
(09-12-2022, 08:36 PM)idmah Wrote: [ -> ]So exploring the idea that TransformPoint is giving me troubles. 

So wrote found this and  modified it. 
Code:
public Vector3 TransFormToWorld(Vector3 localSpacePosition)
    {
        // assumes localScale is correct thing to look at?? //
       
        Matrix4x4 m = Matrix4x4.TRS (ropeComp.solver.transform.position, ropeComp.solver.transform.rotation, ropeComp.solver.transform.localScale);
        Vector3 worldPos = m.MultiplyPoint3x4(localSpacePosition);
       Debug.Log("Convert: " + localSpacePosition + " World" + worldPos+ " Vs TransformPoint:"+ pathSmoother.transform.TransformPoint(localSpacePosition));

        return worldPos;

    }
  
Which outputs: 
Code:
Convert: (0.19, -1.33, 29.97) World(0.45, 4.25, -9.63) Vs TransformPoint:(10.00, 5.31, 0.08)
UnityEngine.Debug:Log (object)
FindClimbObiRope:TransFormToWorld (UnityEngine.Vector3) (at Assets/Scripts/FindClimbObiRope.cs:219)
FindClimbObiRope:FindClosestRopeParticle () (at Assets/Scripts/FindClimbObiRope.cs:152)
FindClimbObiRope:LateUpdate () (at Assets/Scripts/FindClimbObiRope.cs:59)
Where localSpacePosition = ropeComp.solver.positions[ropeComp.solverIndices[i]]
And seem?! to give me the correct closest point I think. Right?? 
(09-12-2022, 08:36 PM)idmah Wrote: [ -> ]
Code:
var pos = pathSmoother.transform.TransformPoint(ropeComp.solver.positions[ropeComp.solverIndices[i]]);

The important thing to remember is that particle data in Obi is always expressed in the solver's local space. This will return a position in world space as long as the rope and the solver spaces overlap (that is, the rope's local position and rotation are zero w.r.t. the solver). Your original FindClosestRopeParticle() function did it like this, so I assumed this was the case.

If it isn't, to return the position in world space regardless of the relationship between the rope and the solver you should do:

Code:
ropeComp.solver.transform.TransformPoint(ropeComp.solver.positions[ropeComp.solverIndices[i]]);

This is pretty much equivalent to:

Code:
pos = ropeComp.GetParticlePosition(ropeComp.solverIndices[i]);

It's easy to see why by looking at the implementation of GetParticlePosition():

Code:
/// <summary> 
        /// Given a solver particle index, returns the position of that particle in world space.
        /// </summary> 
        /// <param name="solverIndex"> Index of the particle in the solver arrays.</param> 
        /// <returns>
        /// The position of a given particle in world space.
        /// </returns>
        public Vector3 GetParticlePosition(int solverIndex)
        {
            if (isLoaded)
                return m_Solver.transform.TransformPoint(m_Solver.renderablePositions[solverIndex]);
            return Vector3.zero;
        }

If the actor is loaded, it transforms the particle position from the solver's local space to world space and returns it. The only difference is that GetParticlePosition() returns an interpolated position in case solver interpolation is enabled (that's what "renderablePositions" are, as opposed to just "positions" which are not interpolated between frames). Otherwise it's identical to ropeComp.solver.transform.TransformPoint(ropeComp.solver.positions[ropeComp.solverIndices[i]]);

Quote:So exploring the idea that TransformPoint is giving me troubles.
So wrote found this and  modified it.

No need to reimplement transform.TransformPoint(). Your function basically builds the transform's localToWorldMatrix and multiplies the input point by it, which is the exact same thing Unity does internally.

This:

Code:
var worldPos = transform.TransformPoint(localPos);

this:

Code:
var worldPos = transform.localToWorldMatrix.MultiplyPoint3x4(localPos);

and this:

Code:
Matrix4x4 localToWorld = Matrix4x4.TRS (transform.position, transform.rotation, transform.lossyScale);
var worldPos = localToWorld.MultiplyPoint3x4(localPos);

Are the same thing, in increasingly complicated forms (note: the last one should use lossyScale instead of localScale, although that's not 100% accurate scaling in all cases since correct composite scale can only be expressed in matrix form). Juggling data between vector spaces is something you'll find yourself doing very often when making games in any engine, so I would advise to keep working on it until you're reasonably sure you understand it perfectly. Keep up the good work! Sonrisa