Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Issues with handles and scripted particles
#1
I'm trying to make a script which snaps rope particles within a radius to their initial position on the spline, so that it places itself nicely when plugs are attached. I'm exposing the curve variable separately so that you can have two separate (identical) cords fitting to the same spot. The script works as expected when snapping to the ropes initial position, but when using the replacement cord with an overriding curve (which has a different starting position), it works well enough except with one small kink - some particles which are attached to handles will move to an area close to their starting location for the duration of the coroutine. What's even weirder is that snapping one plug into place can cause the cord on a completely different rope object (which is still a part of same cord) to exhibit this behavior with its particle handles. Once the coroutine finishes, the particles with handles will return to their expected position. 

Before you say anything - yes, this script will not work properly if you call SnapNearbyParticles twice with overlapping radii. But other than that, what am i doing wrong?



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

public class RopeParticleSnapping : MonoBehaviour {

    protected ObiRope rope;
    public ObiCurve targetCurve;
    protected Dictionary<int, float> particleMasses = new Dictionary<int, float>();


    // Use this for initialization
    void Start()
    {
        rope = GetComponent<ObiRope>();

        if (rope != null && targetCurve == null)
            targetCurve = rope.ropePath;

        //float mu = rope.ropePath.GetMuAtLenght(interParticleDistance * i);
        //positions[i] = transform.InverseTransformPoint(rope.ropePath.transform.TransformPoint(rope.ropePath.GetPositionAt(mu)));

        /*
         * int numSegments = rope.UsedParticles - (closed ? 0:1);
if (numSegments > 0)
interParticleDistance = rope.RestLength/(rope.UsedParticles-1);
else 
interParticleDistance = 0;
         * 
         * 
         * */
    }

    public IEnumerator MoveParticles(Vector3 target, float radius)
    {
        rope.Solver.RequireRenderablePositions();
        yield return null;

        Dictionary<int, Vector3> affectedParticles = new Dictionary<int, Vector3>();

        Debug.DrawRay(target, Vector3.up*0.1f, Color.red, 10.0f);

        for (int i = 0; i < rope.positions.Length; i++)
        {
            float mu = targetCurve.GetMuAtLenght(rope.InterparticleDistance * i);
            Vector3 startPosition = targetCurve.transform.TransformPoint(targetCurve.GetPositionAt(mu));
            Debug.DrawRay(startPosition, Vector3.up*0.1f, Color.green, 10.0f);
            if (Vector3.Distance(startPosition, target) < radius)
            {
                Debug.Log("Particle " + i + " inside distance!");

                affectedParticles[i] = startPosition;
                rope.velocities[i] = Vector3.zero;
                if (!particleMasses.ContainsKey(i)) // We don't want to risk overwriting the stored mass with 0, in case of overlap!
                    particleMasses[i] = rope.invMasses[i];
                rope.invMasses[i] = 0.0f;
            }
        }
        rope.PushDataToSolver(ParticleData.INV_MASSES | ParticleData.VELOCITIES);

        while (affectedParticles.Count > 0)
        {
            List<int> remove = new List<int>();
            foreach(KeyValuePair<int, Vector3> particle in affectedParticles)
            {
                if (Vector3.Distance(rope.GetParticlePosition(particle.Key), particle.Value) < 0.001f)
                {
                    remove.Add(particle.Key);
                } else
                {
                    rope.positions[particle.Key] = rope.transform.InverseTransformPoint(Vector3.Lerp(rope.GetParticlePosition(particle.Key), particle.Value, Time.deltaTime*10.0f));
                }
            }
            foreach (int i in remove)
                affectedParticles.Remove(i);

            rope.PushDataToSolver(ParticleData.POSITIONS);
            yield return null;
        }
        rope.Solver.RelinquishRenderablePositions();
    }

    public IEnumerator UnsnapParticles(Vector3 target, float radius)
    {
        rope.Solver.RequireRenderablePositions();
        yield return null;

        for (int i = 0; i < rope.positions.Length; i++)
        {
            float mu = targetCurve.GetMuAtLenght(rope.InterparticleDistance * i);
            Vector3 startPosition = targetCurve.transform.TransformPoint(targetCurve.GetPositionAt(mu));

            if (Vector3.Distance(startPosition, target) < radius)
            {
                Debug.Log("Releasing particle!");
                particleMasses.TryGetValue(i, out rope.invMasses[i]);
                rope.velocities[i] = Vector3.zero;
            }
        }
        rope.PushDataToSolver(ParticleData.VELOCITIES | ParticleData.INV_MASSES);
        rope.Solver.RelinquishRenderablePositions();
    }

    public void SnapNearbyParticles(Vector3 target, float radius)
    {
        StartCoroutine(MoveParticles(target, radius));
    }

    public void UnsnapNearbyParticles(Vector3 target, float radius)
    {
        StartCoroutine(UnsnapParticles(target, radius));
    }
}
Reply
#2
(17-04-2018, 02:18 PM)khalvr Wrote: I'm trying to make a script which snaps rope particles within a radius to their initial position on the spline, so that it places itself nicely when plugs are attached. I'm exposing the curve variable separately so that you can have two separate (identical) cords fitting to the same spot. The script works as expected when snapping to the ropes initial position, but when using the replacement cord with an overriding curve (which has a different starting position), it works well enough except with one small kink - some particles which are attached to handles will move to an area close to their starting location for the duration of the coroutine. What's even weirder is that snapping one plug into place can cause the cord on a completely different rope object (which is still a part of same cord) to exhibit this behavior with its particle handles. Once the coroutine finishes, the particles with handles will return to their expected position. 

Before you say anything - yes, this script will not work properly if you call SnapNearbyParticles twice with overlapping radii. But other than that, what am i doing wrong?



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

public class RopeParticleSnapping : MonoBehaviour {

    protected ObiRope rope;
    public ObiCurve targetCurve;
    protected Dictionary<int, float> particleMasses = new Dictionary<int, float>();


    // Use this for initialization
    void Start()
    {
        rope = GetComponent<ObiRope>();

        if (rope != null && targetCurve == null)
            targetCurve = rope.ropePath;

        //float mu = rope.ropePath.GetMuAtLenght(interParticleDistance * i);
        //positions[i] = transform.InverseTransformPoint(rope.ropePath.transform.TransformPoint(rope.ropePath.GetPositionAt(mu)));

        /*
         * int numSegments = rope.UsedParticles - (closed ? 0:1);
if (numSegments > 0)
interParticleDistance = rope.RestLength/(rope.UsedParticles-1);
else 
interParticleDistance = 0;
         * 
         * 
         * */
    }

    public IEnumerator MoveParticles(Vector3 target, float radius)
    {
        rope.Solver.RequireRenderablePositions();
        yield return null;

        Dictionary<int, Vector3> affectedParticles = new Dictionary<int, Vector3>();

        Debug.DrawRay(target, Vector3.up*0.1f, Color.red, 10.0f);

        for (int i = 0; i < rope.positions.Length; i++)
        {
            float mu = targetCurve.GetMuAtLenght(rope.InterparticleDistance * i);
            Vector3 startPosition = targetCurve.transform.TransformPoint(targetCurve.GetPositionAt(mu));
            Debug.DrawRay(startPosition, Vector3.up*0.1f, Color.green, 10.0f);
            if (Vector3.Distance(startPosition, target) < radius)
            {
                Debug.Log("Particle " + i + " inside distance!");

                affectedParticles[i] = startPosition;
                rope.velocities[i] = Vector3.zero;
                if (!particleMasses.ContainsKey(i)) // We don't want to risk overwriting the stored mass with 0, in case of overlap!
                    particleMasses[i] = rope.invMasses[i];
                rope.invMasses[i] = 0.0f;
            }
        }
        rope.PushDataToSolver(ParticleData.INV_MASSES | ParticleData.VELOCITIES);

        while (affectedParticles.Count > 0)
        {
            List<int> remove = new List<int>();
            foreach(KeyValuePair<int, Vector3> particle in affectedParticles)
            {
                if (Vector3.Distance(rope.GetParticlePosition(particle.Key), particle.Value) < 0.001f)
                {
                    remove.Add(particle.Key);
                } else
                {
                    rope.positions[particle.Key] = rope.transform.InverseTransformPoint(Vector3.Lerp(rope.GetParticlePosition(particle.Key), particle.Value, Time.deltaTime*10.0f));
                }
            }
            foreach (int i in remove)
                affectedParticles.Remove(i);

            rope.PushDataToSolver(ParticleData.POSITIONS);
            yield return null;
        }
        rope.Solver.RelinquishRenderablePositions();
    }

    public IEnumerator UnsnapParticles(Vector3 target, float radius)
    {
        rope.Solver.RequireRenderablePositions();
        yield return null;

        for (int i = 0; i < rope.positions.Length; i++)
        {
            float mu = targetCurve.GetMuAtLenght(rope.InterparticleDistance * i);
            Vector3 startPosition = targetCurve.transform.TransformPoint(targetCurve.GetPositionAt(mu));

            if (Vector3.Distance(startPosition, target) < radius)
            {
                Debug.Log("Releasing particle!");
                particleMasses.TryGetValue(i, out rope.invMasses[i]);
                rope.velocities[i] = Vector3.zero;
            }
        }
        rope.PushDataToSolver(ParticleData.VELOCITIES | ParticleData.INV_MASSES);
        rope.Solver.RelinquishRenderablePositions();
    }

    public void SnapNearbyParticles(Vector3 target, float radius)
    {
        StartCoroutine(MoveParticles(target, radius));
    }

    public void UnsnapNearbyParticles(Vector3 target, float radius)
    {
        StartCoroutine(UnsnapParticles(target, radius));
    }
}

Hi!

Let me try out your script and I'll get back to you asap.
Reply
#3
I solved it - the issue was that i was pushing the positions of all the particles when i had only generated positions for the ones i wanted to be affected. I figured that positions which weren't changed wouldn't get their positions overwritten by the obsolete data, but in hindsight, it was pretty obvious. Changed it to using the low-level solver functions and now it works like a charm.
Reply