Search Unity

Bezier Solution [Open Source]

Discussion in 'Assets and Asset Store' started by yasirkula, Nov 12, 2016.

  1. gabry90

    gabry90

    Joined:
    Dec 16, 2015
    Posts:
    29
    @yasirkula i get an exception if i use segment like this: BezierSpline.Segment segment = BezierSplineComponent.GetSegmentAt(.5f);

    Exception:

    ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: index
    System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource) (at <695d1cc93cca45069c528c15c9fdd749>:0)
    System.ThrowHelper.ThrowArgumentOutOfRangeException () (at <695d1cc93cca45069c528c15c9fdd749>:0)
    System.Collections.Generic.List`1[T].get_Item (System.Int32 index) (at <695d1cc93cca45069c528c15c9fdd749>:0)
    BezierSolution.BezierSpline.GetSegmentAt (System.Single normalizedT) (at Assets/UnyXEngine/Core/Runtime/Lib/BezierSolution/BezierSpline.cs:848)
    ...

    Please can you investigate?

    EDIT: mmm...i dont' understand why endPoints on BezierSpline component is empty....this is cause exception
    RESOLVED: bezier spline is inner a deactivated gameobject :D
     
    Last edited: May 14, 2021
    yasirkula likes this.
  2. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Yes the spline is initialized in Awake. But perhaps adding
    [SerializeField]
    attribute to BezierSpline.endPoints variable would remove this requirement.
     
  3. rodriafz

    rodriafz

    Joined:
    Jan 17, 2021
    Posts:
    1
    How would one go about setting a 2D sprite to face the direction of the path its following?
     
  4. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    You can try
    sprite.transform.right = spline.GetTangent(normalizedT);
     
  5. KOemxe

    KOemxe

    Joined:
    May 1, 2018
    Posts:
    35
    @yasirkula

    Hey there, long time!

    I still appreciate this asset of yours. I wanted to ask a question about the spline walker.

    How would you suggest coding the spline walker so that, say, it keeps moving in front of a chasing object at whatever speed the chasing object is moving?
     
  6. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Does the chasing object also move along the spline? If so, does it use BezierWalker, as well?
     
  7. KOemxe

    KOemxe

    Joined:
    May 1, 2018
    Posts:
    35
    Admittedly, no, it doesn't move along the spline (and intentionally so - I don't want to make the computer look like they don't do natural movements for a racer)

    I can't say it uses the BezierWalker itself.
     
  8. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    As far as I understand, you always want the bezier walker to be 5 units ahead of the chaser object in Z+ axis. In this case, the only solution I can think of is not using a bezier walker but instead calling BezierSpline's FindNearestPointToLine function to move that object in Update. For example:
    Code (CSharp):
    1. public BezierSpline spline;
    2. public Transform chaser;
    3. public float distance = 5f;
    4. public float accuracy = 250f; // The higher this value, the better. But CPU cost will increase proportionally
    5.  
    6. void LateUpdate()
    7. {
    8.     Vector3 chaserAheadPos = chaser.position + new Vector3( 0f, 0f, distance );
    9.     Vector3 lineStart = new Vector3( chaserAheadPos.x - 5f, chaserAheadPos.y, chaserAheadPos.z );
    10.     Vector3 lineEnd = new Vector3( chaserAheadPos.x + 5f, chaserAheadPos.y, chaserAheadPos.z );
    11.    
    12.     Vector3 pointOnLine;
    13.     float normalizedT;
    14.     Vector3 position = spline.FindNearestPointToLine( lineStart, lineEnd, out pointOnLine, out normalizedT, accuracy );
    15.    
    16.     transform.position = position;
    17.    
    18.     // Alternatively, lerp the position. While lerping, you can also try decreasing accuracy to boost performance
    19.     //transform.position = Vector3.Lerp( transform.position, position, 10f * Time.deltaTime );
    20.    
    21.     transform.rotation = Quaternion.Lerp( transform.rotation, Quaternion.LookRotation( spline.GetTangent( normalizedT ) ), 10f * Time.deltaTime );
    22. }
     
  9. ChainsawFilms

    ChainsawFilms

    Joined:
    May 28, 2016
    Posts:
    18
    This is great, thank you.

    Is there a way to call an event when you reach a bezier point? Thank you.
     
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Unfortunately not. You could continuously check the value of spline.GetSegmentAt(normalizedT).point1 and raise the event when the returned point changes but if the object moves very fast or the game lags, it can skip some points (i.e. it only happens if the object passes multiple bezier points in a single frame).
     
  11. ChainsawFilms

    ChainsawFilms

    Joined:
    May 28, 2016
    Posts:
    18
    Thank you, that works in the tests I've just ran perfectly for what I was hoping to achieve :)

    Have a cup of coffee on me :D
     
    yasirkula likes this.
  12. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Thank you for your support!
     
  13. Delphoxite

    Delphoxite

    Joined:
    Mar 25, 2020
    Posts:
    5
    Hi yasirkula. First of all, thank you so much for this, it's a wonderful asset!

    Just a question: Is there any way to use BezierWalkerWithTime while keeping a constant speed? I have different curves, side by side, where the objects must walk at a constant speed (not necessarily the same speed between each curve, as they have different properties) and arrive at the end in x seconds. Is it possible?

    Again, thank you.
     
  14. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Yes! Simple enable High Quality option.
     
  15. Delphoxite

    Delphoxite

    Joined:
    Mar 25, 2020
    Posts:
    5
    Hi! First of all, thanks for your awnser.
    I already tried the high quality option but it still seems a bit odd. I don't know how to describe it, so I made a gif.
     

    Attached Files:

    Last edited: Jul 4, 2021
  16. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    In the gif, it looks like the selected object abruptly slows down and speeds up but it might also be the case if the gif wasn't recorded with stable framerate. Could you explain the issue? What are your BezierWalker values?
     
  17. Delphoxite

    Delphoxite

    Joined:
    Mar 25, 2020
    Posts:
    5
    Yes, that is exactly the problem, it's not related to the gif, it happens in the editor as well. The selected object is where the problem is most visible but it happens with basically all objects, to different degrees.
     

    Attached Files:

  18. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
  19. Delphoxite

    Delphoxite

    Joined:
    Mar 25, 2020
    Posts:
    5
    Even with the default value (10) the problem still happens :(

    I had to leave the computer but when I get back I'll try the second option and come back here with the result.
     
    yasirkula likes this.
  20. Delphoxite

    Delphoxite

    Joined:
    Mar 25, 2020
    Posts:
    5
    So I tried changing the resolution parameter to 1 and it seems to work and all the objects are in sync, but it's still not at a constant speed, especially towards the end where they slow down slightly.
     
  21. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    You need to increase those parameters, not decrease them.
     
  22. gabry90

    gabry90

    Joined:
    Dec 16, 2015
    Posts:
    29
    @yasirkula

    when i call MoveAlongSpline(ref float normalizedT, float deltaMovement, int accuracy = 3) method with parameters like:
    normalizedT = 0f
    deltaMovement = 0.09932098

    the result is a Infinity normalizedT.... why? (I see that is happens caused division by zero in BezierSpline.cs row: 918 from GetTangent(0).magnitude = 0)

    You can check please?

    [RESOLVED]
    mmmm...ok...i found the problem... i have a point with proceding and followin points at 0 with warning panel... it would be useful not to return an infinite value anyway, maybe some safety check or at most to clamp the result before the division so as not to divide by zero :)
     
    Last edited: Aug 19, 2021
    yasirkula likes this.
  23. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Thank you for bringing it to my attention.
     
  24. gabry90

    gabry90

    Joined:
    Dec 16, 2015
    Posts:
    29
    @yasirkula how to get exact normalizedT at some spline point position?
     
  25. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    You can call spline.FindNearestPointTo. Note that it doesn't have pinpoint accuracy but you can increase its accuracy until you get acceptable results.
     
  26. gabry90

    gabry90

    Joined:
    Dec 16, 2015
    Posts:
    29
    but this method return a Vector3, i need the reverse function, so vector3 -> normalizeT
     
  27. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    It takes an optional
    out float normalizedT
    parameter.
     
    gabry90 likes this.
  28. gabry90

    gabry90

    Joined:
    Dec 16, 2015
    Posts:
    29
    @yasirkula how to get spline position point by normalizedT? GetPoint() return wrong value. I have a spline and if i pass 0.5f for middle position the result position is not middle, but approx about 30% the lenght of spline! Why?

    I use GetSegmentAt(0.5f) and after segment.GetPoint()

     
    Last edited: Oct 27, 2021
  29. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    That's how splines work. You can see the mathematical reasoning behind that in this video. What you can do is, calling
    spline.GetSegmentAt(spline.evenlySpacedPoints.GetNormalizedTAtPercentage(0.5f))
    instead.
     
    gabry90 likes this.
  30. WillemMahyFM

    WillemMahyFM

    Joined:
    Aug 27, 2021
    Posts:
    7
    @ yasirkula, first of all, a big thank you for your magnificent asset! Well made!

    I have a question:

    Would it be possible to get from a BezierSpline the exact (or at least reliable approximations of) t values for the endpoints with which it has been initialized?

    Why am I asking?
    I have the following use case:

    GIVEN
    A dense and noisy polyline (OriginalPolyline).
    OriginalPolyline is generated from tracking the position of a VR controller.
    As a consequense, it has a non-uniform, variable, point density/segment length.

    I WANT TO
    Generate a smooth polyline SmoothPolyline from OriginalPolyline with comparable point density.


    My approach:
    1) Generate a simplified polyline EndpointsPolyline from OriginalPolyline, using Unity's built-in LineUtility.Simplify() function.
    2) Initialize a BezierSpline Spline with the points from EndpointsPolyline.
    3) Get the t value for each endpoint, using Spline.FindNearestPointTo(endpoint.position).
    4) Generete intermediate t values between each successive pair of endpoint t values, to get the same number of t values in each spline segment, as there were points in the corresponding section of OriginalPolyline.
    5) Use this extended t values list to generate the points for SmoothPolyline from Spline.


    The problem:
    The BezierSpline.FindNearestPointTo(Vector3 position) call is an iterative approximation.
    It quite often gives a wrong t value, where it seems to returns t values for points on parts of the spline that are near the actual endpoint, but much later (or earlier) in the polyline. (polyline self near-intersections)
    This results in the successive values in the t values list acquired in step (3) to have t values jumping up and down sometimes.
    (Note: I am using the default parameter values everywhere)
     
    yasirkula likes this.
  31. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    When a spline has 3 points, these points' t values are: 0f, 0.5f and 1f. As you can see, these t values don't take into account the distance between each point at all (because of which, I've added evenlySpacedPoints and pointCache helpers to the spline but these seem unrelated to your problem).

    P.S. But if the spline is looped, then these 3 points' t values will be: 0f, 0.33f, 0.66f (and GetPoint(1f) will return the first point again).
     
  32. WillemMahyFM

    WillemMahyFM

    Joined:
    Aug 27, 2021
    Posts:
    7
    A big shoutout @ yasirkula, for the swift response and clear explanation - everything works as expected now!
    Is there a way to show our gratitude (in a monetary way) for making your unity asset available, and your helpful support?
     
    yasirkula likes this.
  33. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Thank you very much! You can support me that way via the "support me" link in my signature.
     
  34. fairtree

    fairtree

    Joined:
    Jun 11, 2012
    Posts:
    84
    Hi there,
    I'd like to show the different points on the inspector and be able to change their position from there.
    Any way to do that?
     
  35. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    You can't see them all at once for the time being. You'd need to modify the source to achieve this.
     
  36. fairtree

    fairtree

    Joined:
    Jun 11, 2012
    Posts:
    84
    yasirkula likes this.
  37. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Hi, Timeline is probably modifying the underlying private fields whereas the component refreshes itself when its public properties are invoked. You can call the component's Refresh function at each frame while the animation is running to force update the mesh.
     
  38. Somnesis

    Somnesis

    Joined:
    Feb 27, 2020
    Posts:
    32
    Hi!

    I'm getting no handles now for curves that I create. This seems to be a new thing. Maybe since upgrading Unity.

    See here: https://cln.sh/NlRz34

    Unity version: 2021.2.7
    Bezier version: 2.3.1

    Thanks! :)
     
  39. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    You need to enable gizmos via the top-right button in Scene window.
     
  40. AndresOrtiz

    AndresOrtiz

    Joined:
    Apr 30, 2019
    Posts:
    3
    Hi, I love the assets and it works great, but I ran into problems when trying to export to UWP
    upload_2022-5-27_4-5-30.png
     
  41. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    I see, I'll need to check for UWP compatibility.
     
  42. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    @AndresOrtiz Can you build the game after deleting BezierSolution to verify that BezierSolution is to blame here?
     
  43. AndresOrtiz

    AndresOrtiz

    Joined:
    Apr 30, 2019
    Posts:
    3
    Hello, sorry for not warning, but BezierSolution was not to blame. I spent so much time solving it that I forgot to write to you later.
     
    yasirkula likes this.
  44. mklasson

    mklasson

    Joined:
    Oct 30, 2018
    Posts:
    28
    Thank you for your great assets, yasirkula!

    I notice in a lot of places, e.g. GetPoint, GetTangent, you have:
    Code (CSharp):
    1. while( normalizedT < 0f )
    2.     normalizedT += 1f;
    3. while( normalizedT >= 1f )
    4.     normalizedT -= 1f;
    This can (obviously) lead to serious performance problems if you're not careful with the normalizedT you pass in. You might say you should make sure to pass in a normalized value, which is a fine argument. Seeing as you're still allowing and handling non-normalized values though, might I suggest something like:

    normalizedT = ((normalizedT % 1.0f) + 1.0f) % 1.0f;
    instead?

    Maybe this is rarely a problem in practice, but it feels like a potentially head-scratchy bug to figure out if you do run into it. I did so accidentally while benchmarking:

    MoveAlongSpline(ref splineT, Time.deltaTime * speed)
    and noticing it got significantly slower as time went on. As I never actually altered splineT myself I also wasn't normalizing it.
     
    yasirkula likes this.
  45. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Hi! I believe you're right! I shall benchmark your solution to see how it fares when compared to the current solution and then I'll update the plugin when I get the chance.
     
    mklasson likes this.
  46. mikeohc

    mikeohc

    Joined:
    Jul 1, 2020
    Posts:
    215
    Hi there, I'm trying to accomplish similar function. It's a track that takes a gameobject and moves it onto another track.
    I want to accomplish the following:
    1) Each static track has a spline.
    2) The dynamic track (can spin) adds a point to the static track when aligned.
    3) cylinder can move onto dynamic track.
    4) cylinder can be rotated and attached to another static track.

    FindNearestPointTo() doesn't get the cylinder to stay in place. If I have a BezierAttachment attached to the cylinder, it jumps before it can retrieve the nearest point.
    Code (CSharp):
    1. _spline = spline;
    2.  _spline.InsertNewPointAt(2);
    3.  _spline[2].position = transform.position;
    4.  
    5. _spline.FindNearestPointTo(_power.transform.position, out float normalizedT);
    The normalizedT should return ~0.4 but since the moment the spline inserts a new point, gameobject jumps. So how would you FindNearestPointTo with the traversing object?
    splinenormalizedT-jump.gif
    splinenormalizedT-nospline.gif
     
  47. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    @mikeohc BezierAttachment is supposed to update the object's position in LateUpdate actually. Is there a hidden _spline.Refresh function before FindNearestPointTo, because that'd refresh BezierAttachment immediately and cause this issue. I'd recommend putting a Debug.Log inside BezierAttachment.RefreshInternal to see where it's being triggered from. If it's indeed called immediately after inserting a new point, then we'll at least see the stacktrace.

    In the worst case that we can't prevent this immediate-update, then the only solution that comes to mind is fetching the attachments' position prior to adding a new point to the spline.
     
    mikeohc likes this.
  48. mikeohc

    mikeohc

    Joined:
    Jul 1, 2020
    Posts:
    215
    I think I found the issue. It's because when the dynamic track snaps to the static track, I am invoking an event to call FindNearestPointTo(). But if I call FindNearestPointTo() within the same script before invoking, I can retrieve the correct NormalizedT.

    Thanks for the help!
     
    yasirkula likes this.
  49. coolguy_platinum

    coolguy_platinum

    Joined:
    Mar 4, 2019
    Posts:
    15
    hey thanks for the asset. how would you exactly set things up to make a character grind?
     
  50. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Hi, I don't know the exact steps, but after setting up a spline, I'd probably call spline.FindNearestPointTo to find the nearest point on the spline to the character and start grinding from there. While grinding, I'd call spline.MoveAlongSpline or spline.GetPoint at each frame.