Search Unity

How to move an object along a Spline (SpriteShape)

Discussion in '2D' started by TimBur, Feb 29, 2020.

  1. TimBur

    TimBur

    Joined:
    Jan 17, 2013
    Posts:
    35
    Is there a way to get detailed information about the shape of a Spline in a SpriteShapeController? Not just info about the control points, but the specifics of the curved parts that run in-between the control points?

    I have some objects that need to follow a fixed, curving path. I've drawn this curved path, in the Editor, using an open-ended SpriteShape. I had thought to write a script that would read path info from the Spline, and then move my objects along that path. While I understand how to get data on the control points (GetPosition, GetLeftTangent, GetRightTangent), I don't see a way to get info on the parts of the spline in-between the control points. Are there hidden, interpolated points that sit between the control points? Can I get the positions of those interpolated points as a Vector3 array? If not, is there some other way to get the full path info out of a Spline, so that I can procedurally make my objects follow a SpriteShape path?
     
    RemDust, Slakkedis, akyyewale and 2 others like this.
  2. nmerkas

    nmerkas

    Joined:
    Mar 14, 2015
    Posts:
    7
  3. ed_s

    ed_s

    Unity Technologies

    Joined:
    Apr 17, 2015
    Posts:
    165
    @Venkify can you offer any insight?
     
  4. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    @Venkify, I would very very much like to find out as well.
     
  5. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    Please take a look at Sprinker.cs from the Extras available with the package. Here is a quick sample code :

    Code (CSharp):
    1. // The following code instantiates a prefab in a random location between control points 1 and 2.
    2. SpriteShapeController ssc = GetComponent<SpriteShapeController>();
    3. Spline spl = ssc.spline;
    4. var i = 2;
    5. var go = GameObject.Instantiate(m_Prefab);
    6.  
    7. float2 _p0 = new float2( spl.GetPosition(i - 1).x, spl.GetPosition(i - 1).y );
    8. float2 _p1 = new float2( spl.GetPosition(i).x, spl.GetPosition(i).y );
    9. float2 _rt = _p0 + new float2( spl.GetRightTangent(i - 1).x, spl.GetRightTangent(i - 1).y );
    10. float2 _lt = _p1 + new float2( spl.GetLeftTangent(i).x, spl.GetLeftTangent(i).y );
    11.  
    12. float r = Random.Range(0.5f, 0.95f);
    13. float2 bp = BezierPoint(_rt, _p0, _p1, _lt, r);
    14. go.transform.position = new Vector3( bp.x, bp.y, 0);
     
    Eristen and Slakkedis like this.
  6. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Oh. Ok,.. but my goodness. I think there should br a simple api that accepts either relative distance of 0 to 1 or absolute distance and return position...
     
    synthc and Slakkedis like this.
  7. Slakkedis

    Slakkedis

    Joined:
    Nov 29, 2017
    Posts:
    6
    I second that notion, interpolation would be great!
     
  8. TimBur

    TimBur

    Joined:
    Jan 17, 2013
    Posts:
    35
    +10
    That example is super helpful. Still, the point of a Spline class is to let us do spline stuff without having to write our own math.
     
    Slakkedis likes this.
  9. Slakkedis

    Slakkedis

    Joined:
    Nov 29, 2017
    Posts:
    6
    I'm having a bit of a struggle getting access to "float2" and the BezierPoint, is there something I need to import?
     
  10. TheLongNight

    TheLongNight

    Joined:
    Jun 18, 2019
    Posts:
    2
    Unity.Mathematics for float2
    UnityEngine.U2D for BezierUtility.BezierPoint

    BezierUtility takes Vector3, so it becomes:
    Code (CSharp):
    1.  go.transform.position = BezierUtility.BezierPoint(
    2.            new Vector3(_p0.x, _p0.y, 0),
    3.            new Vector3(_rt.x, _rt.y, 0),
    4.            new Vector3(_lt.x, _lt.y, 0),
    5.            new Vector3(_p1.x, _p1.y, 0),
    6.            r
    7. );
     
  11. Brogan89

    Brogan89

    Joined:
    Jul 10, 2014
    Posts:
    244
    I made an extension method that may help. It allows you to get the local position along the spline based on a value of 0 - 1. It could probably be a bit cleaner but it works for me, you can see it working here. The circle sprite is a child of the spline.

    cableanimation.gif


    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.U2D;
    3.  
    4. public static class SplineEx
    5. {
    6.     /// <summary>
    7.     /// Returns the local position along the spline based on progress 0 - 1.
    8.     /// Good for lerping an object along the spline.
    9.     /// <para></para>
    10.     /// Example: transform.localPosition = spline.GetPoint(0.5f)
    11.     /// </summary>
    12.     /// <param name="spline"></param>
    13.     /// <param name="progress">Value from 0 - 1</param>
    14.     /// <returns></returns>
    15.     public static Vector2 GetPoint(this Spline spline, float progress)
    16.     {
    17.         var length = spline.GetPointCount();
    18.         var i = Mathf.Clamp(Mathf.CeilToInt((length - 1) * progress), 0, length - 1);
    19.  
    20.         var t = progress * (length - 1) % 1f;
    21.         if (i == length - 1 && progress >= 1f)
    22.             t = 1;
    23.  
    24.         var prevIndex = Mathf.Max(i - 1, 0);
    25.  
    26.         var _p0 = new Vector2(spline.GetPosition(prevIndex).x, spline.GetPosition(prevIndex).y);
    27.         var _p1 = new Vector2(spline.GetPosition(i).x, spline.GetPosition(i).y);
    28.         var _rt = _p0 + new Vector2(spline.GetRightTangent(prevIndex).x, spline.GetRightTangent(prevIndex).y);
    29.         var _lt = _p1 + new Vector2(spline.GetLeftTangent(i).x, spline.GetLeftTangent(i).y);
    30.  
    31. return BezierUtility.BezierPoint(
    32.    new Vector2(_rt.x, _rt.y),
    33.    new Vector2(_p0.x, _p0.y),
    34.    new Vector2(_p1.x, _p1.y),
    35.    new Vector2(_lt.x, _lt.y),
    36.    t
    37. );
    38.     }
    39. }
    40.  
     
    Last edited: Apr 2, 2023
  12. myazuid

    myazuid

    Joined:
    Jan 29, 2019
    Posts:
    26
    Thank you very much for this! Really helpful.
     
    Brogan89 likes this.
  13. Brogan89

    Brogan89

    Joined:
    Jul 10, 2014
    Posts:
    244
    FYI, in `com.unity.2d.spriteshape: 7.0.7` they've swapped the params in `BezierUtility.BezierPoint` so it now needs to be
    Code (CSharp):
    1.  
    2. return BezierUtility.BezierPoint(
    3.    new Vector2(_rt.x, _rt.y),
    4.    new Vector2(_p0.x, _p0.y),
    5.    new Vector2(_p1.x, _p1.y),
    6.    new Vector2(_lt.x, _lt.y),
    7.    t
    8. );
    9.  
    10.  
    The rest of the code is the same. I will edit the initial script for anyone in future who come across this thread.
     
    Last edited: Apr 2, 2023
    Eristen and Venkify like this.
  14. scientful

    scientful

    Joined:
    Apr 18, 2023
    Posts:
    1
    Is there a way to approximate the time of an object along the Spline path?
     
  15. Brogan89

    Brogan89

    Joined:
    Jul 10, 2014
    Posts:
    244
    I guess getting the length of spline divide by the speed?
     
    scientful likes this.
  16. ZimM

    ZimM

    Joined:
    Dec 24, 2012
    Posts:
    963
    Unfortunately, none of those methods take into account the Height parameter of control points. It's really puzzling why this isn't an integral part of the package, but instead we have to collect pieces of code from examples and forums for such basic functionality...
     
    scientful likes this.
  17. Venkify

    Venkify

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    644
    In recent version (10.0.0) we have added a script to make placement of objects easier on the SpriteShape through SpriteShapeObjectPlacement component. Please find it under com.unity.2d.spriteshape\Runtime\SpriteShapeObjectPlacement.cs

    Hopefully this also helps as a reference for object movement / placement etc..

    We will consider adding support for this in an upcoming version. Thanks for posting it.
     
    Walter_Hulsebos and enhawk like this.
  18. ZimM

    ZimM

    Joined:
    Dec 24, 2012
    Posts:
    963
    @Venkify Can you please consider adding general purpose methods for querying the spline? Position along the spline at any distance (not just control points), normal and tangent vectors, closest point on the spline? That would be much more useful in practice than the rigid implementation in SpriteShapeObjectPlacement. Main problem of SpriteShape, for me at least, is that it is very hard to customize and build things on top of it, since you practically have no data to operate on, it's a black box. I have to write all the spline calculations on my own.

    On that subject, Unity already has a Spline package that does everything I've mentioned and beyond:
    https://docs.unity3d.com/Packages/com.unity.splines@2.2

    Is there a possibility that SpriteShape will utilize it at some point? That would literally solve all the problems...
     
    Last edited: May 8, 2023
    coding_crow likes this.
  19. enhawk

    enhawk

    Joined:
    Aug 22, 2013
    Posts:
    833
    Is it possible to back-port this to LTS?
    We are using 2021.3 and the version can't be changed because of other factors.

    This would be very handy in LTS! :)