Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Rope bumping
#1
Hi, I wanted to achieve the visual effect of pumping liquid or air up a rope. To do this, I wrote a script -


public class RopeBump : MonoBehaviour
{

    [SerializeField]
    private ObiRopeBlueprint _obiRopeBlueprint;


    [SerializeField, Range(0.5f, 2f)]
    private float _bumpSpeed = 1;


    [SerializeField]
    private int _framesSkipAnimation;


    private bool _isBumping;


    [Button]
    private async UniTask BumpAsync(float value, bool reverse)
    {
      if (_isBumping)
          return;

      _isBumping = true;
      var thicknesses = _obiRopeBlueprint.path.thicknesses;

      int start = reverse ? thicknesses.Count - 1 : 0;
      int end = reverse ? -1 : thicknesses.Count;
      int step = reverse ? -1 : 1;

      for (int i = start; i != end; i += step)
      {
          float cachedThickness = thicknesses[i];
          float direction = cachedThickness < value ? 1f : -1f;

          while (true)
          {
            thicknesses[i] += _bumpSpeed * direction;

            if ((direction == 1f && thicknesses[i] >= value) || (direction == -1f && thicknesses[i] <= cachedThickness))
            {
                if (direction == -1f)
                  break;

                direction = -1f;
            }

            _obiRopeBlueprint.path.FlushEvents();
            await UniTask.DelayFrame(_framesSkipAnimation);
          }
      }

      for (int i = 0; i < thicknesses.Count; i++)
          thicknesses[i] = 1;

      _isBumping = false;
    }


    private void OnEnable()
    {
      for (int i = 0; i < _obiRopeBlueprint.path.thicknesses.Count; i++)
      {
          _obiRopeBlueprint.path.thicknesses[i] = 1;
          _obiRopeBlueprint.path.FlushEvents();
      }
    }


    private void OnDisable()
    {
      for (int i = 0; i < _obiRopeBlueprint.path.thicknesses.Count; i++)
      {
          _obiRopeBlueprint.path.thicknesses[i] = 1;
          _obiRopeBlueprint.path.FlushEvents();
      }
    }


https://dropmefiles.com/H4YYi
In this script I achieved the desired visual, but there were some difficulties, namely -
1 - FPS sag, most likely because of the call FlushEvents() after changing thicknesses[i].
2 - when moving the object, to which the rope is tied, and pumping the rope - the anchor point shifts.

Maybe there is some easier or more obvious way to accomplish the same thing? For example, pick up the rendering of particles and change their thickness asynchronously there?  Or something else?
Reply
#2
Hi,

You're essentially re-creating the rope from scratch every frame by rebuilding its blueprint path. This will of course obliterate performance.

Blueprints are only intended to define the initial shape and properties of the rope, as they're assets stored in disk (similar to audio files or animation clips) so creating/editing them is considerably costly. At runtime, you're supposed to deal exclusively with particles. I'd also suggest taking a look at Obi's architectural design, to better understand how actors and blueprints relate.

There's a sample scene included with the asset ("HosePump") that does this very thing you're trying to do: simulating water/air passing trough the rope by changing its thickness at runtime. Maybe it can be helpful for you?

kind regards,
Reply
#3
(31-07-2023, 10:46 AM)josemendez Wrote: Hi,

You're essentially re-creating the rope from scratch every frame by rebuilding its blueprint path. This will of course obliterate performance.

Blueprints are only intended to define the initial shape and properties of the rope, as they're assets stored in disk (similar to audio files or animation clips) so creating/editing them is considerably costly. At runtime, you're supposed to deal exclusively with particles. I'd also suggest taking a look at Obi's architectural design, to better understand how actors and blueprints relate.

There's a sample scene included with the asset ("HosePump") that does this very thing you're trying to do: simulating water/air passing trough the rope by changing its thickness at runtime. Maybe it can be helpful for you?

kind regards,
Oh, how quickly you responded. Thank you so much, yes that's just what I need. I will definitely give you 5 stars for this now in the asset store).

I will of course take the logic from this script, I knew there was some other way through solver.principalRadii.


If it will be useful for someone, here is a script for starting and waiting for one rope ride with UniTask and DoTween. The interval between radiuses of one element and speed are regulated - _animationDuration and _animationInterval respectively.


public class RopePump : MonoBehaviour
{

    [SerializeField]
    private ObiRope _obiRope;


    [SerializeField, Range(0.001f, 1f)]
    private float _animationDuration = 0.01f;


    [SerializeField, Range(0.001f, 1f)]
    private float _animationInterval = 0.01f;


    [SerializeField, Range(0.1f, 1f)]
    public float baseThickness = 0.1f;


    [SerializeField, Range(0.1f, 1f)]
    public float bulgeThickness = 0.3f;


    private bool _isPumpingNow;


    private void Start()
    {
      _obiRope = GetComponent<ObiRope>();
    }


    [Button]
    private async UniTask RopePumpAnimation(bool reverse)
    {
      if (_isPumpingNow)
          return;

      _isPumpingNow = true;
      List<UniTask> tasks = new List<UniTask>();
      float totalDelay = 0;

      if (reverse)
      {
          for (int i = _obiRope.solverIndices.Length; i > 0; --i)

          {
            tasks.Add(PumpRopeWithDelay(i, totalDelay));
            totalDelay += _animationInterval;
          }
      }
      else
      {
          for (int i = 0; i < _obiRope.solverIndices.Length; ++i)

          {
            tasks.Add(PumpRopeWithDelay(i, totalDelay));
            totalDelay += _animationInterval;
          }
      }

      await UniTask.WhenAll(tasks);
      _isPumpingNow = false;
    }


    private async UniTask PumpRopeWithDelay(int index, float delayTime)
    {
      await UniTask.Delay(TimeSpan.FromSeconds(delayTime));

      await DOTween.To(() => (Vector3)_obiRope.solver.principalRadii[index], x => _obiRope.solver.principalRadii[index] = x, Vector3.one * bulgeThickness, _animationDuration);

      await DOTween.To(() => (Vector3)_obiRope.solver.principalRadii[index], x => _obiRope.solver.principalRadii[index] = x, Vector3.one * baseThickness, _animationDuration);
    }



    [Button]
    private void Reset()
    {
      for (int i = 0; i < _obiRope.solverIndices.Length; ++i)
      {
          int solverIndex = _obiRope.solverIndices[i];
          _obiRope.solver.principalRadii[solverIndex] = Vector3.one * baseThickness;
      }

      _isPumpingNow = false;
    }
}
Reply