Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Spawning a rope along a line the player draws
#1
Pregunta 
Hello, I am trying to create a game where the player can draw a rope in realtime by dragging the cursor around.  My goal is:

  1. Let the player drag the cursor around a 2D plane, spawning a rope as they go (as if drawing a line with a pen)
  2. The rope should spawn in a way that it matches the drawn path as closely as the simulation settings allow.
  3. The rope should be a live simulation while it is being drawn (I don't want to draw a spline and then convert it into a rope once the cursor is released).
   

Below is a code snippet from my first approach, but it doesn't seem to work. Here is a YouTube video of it in action.

Code:
void FixedUpdate()
{
    // _dragAndDrop is an object that follows the cursor if isDragging is TRUE
    if(_dragAndDrop.isDragging)
    {
        // Calculate the distance between _tipTransform and _dragAndDrop.transform
        float distance = Vector3.Distance(_tipRigidbody.transform.position, _dragAndDrop.transform.position);

        // Add that amount of distance to the end of the rope
        _cursor.cursorMu = 1.0f;
        _cursor.sourceMu = 0.0f;
        _cursor.ChangeLength(_rope.restLength + distance);

        // Tip rigidbody is attached to the end or the rope.               
        _tipRigidbody.transform.position = _dragAndDrop.transform.position;
    }
}

The code sort of works, but it seems to spawn too many particles. And then the rope just explodes. It would appear that changing the length in any way causes at least one new particle to be added. 
Is there a better way to achieve this?
Reply
#2
Hi!

Don't set the cursorMu before changing length. This will force the cursor to reset the amount of length already added to zero, which may cause particles to be added immediately one after another.

Another problem is that you're not accounting for the rope's simulation, the newly added particle(s) should be placed in a straight line from the tip of the rope to the mouse cursor. Otherwise the rope is free to move anywhere and this might cause a feedback loop where the simulation itself increases the distance from the mouse if _tipRigidbody is attached to the rope.

You're doing this in FixedUpdate, which means that some frames you might be increasing the length twice or more times, some frames you might not be increasing it at all. It may also update the length before or after ObiFixedUpdater(), so this might happen before or after simulation. A much better alternative would be to do this in LateUpdate().

let me know if I can be of further help!

cheers,
Reply
#3
Wrote a sample script for you. I'm using the solver's OnSubstep callback instead of LateUpdate, because I'm setting the first two particle positions every simulation step in order for the rope to roughly follow the direction of the cursor instead of increasing its length in whatever the current direction of the tip is.

Also I'm raycasting against the XZ plane which might not be how your interaction system works, but the important parts should be easy to port to your system:

Code:
using UnityEngine;
using Obi;

[RequireComponent(typeof(ObiRope))]
public class DrawRope : MonoBehaviour
{
    public float directionSmoothing = 0.9f;

    public ObiRopeCursor cursor;
    ObiRope rope;
    Vector3 lastPoint;
    Vector3 avgDir;

    private void Start()
    {
        rope = GetComponent<ObiRope>();
        rope.solver.OnSubstep += Solver_OnBeginStep;
    }
    private void OnDestroy()
    {
        rope.solver.OnSubstep -= Solver_OnBeginStep;
    }

    private void Solver_OnBeginStep(ObiSolver solver, float stepTime)
    {
        var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Plane p = new Plane(Vector3.up, 0);

        if (p.Raycast(ray, out float enter))
        {
            var point = ray.GetPoint(enter);

            if (Input.GetMouseButtonDown(0))
            {
                lastPoint = point;
            }
            if (Input.GetMouseButton(0))
            {
                float distance = Vector3.Distance(lastPoint, point);
                cursor.ChangeLength(rope.restLength + distance);

// Average cursor movement direction:
                if (distance > 0.0001f)
                    avgDir = Vector3.Lerp(Vector3.Normalize(lastPoint - point), avgDir, directionSmoothing);

// Update the first two particles so that they lie in the direction drawn by the cursor:
                var part1 = rope.elements[0].particle1;
                var part2 = rope.elements[0].particle2;

                rope.solver.positions[part1] = rope.solver.transform.InverseTransformPoint(point);
                rope.solver.velocities[part1] = Vector3.zero;
               
                rope.solver.positions[part2] = rope.solver.transform.InverseTransformPoint(point + avgDir * rope.elements[0].restLength);
                rope.solver.velocities[part2] = Vector3.zero;

                lastPoint = point;
            }
        }
    }
}

Video of results:
Reply