Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Bezier Solution [Open Source]

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

  1. siempus

    siempus

    Joined:
    Nov 29, 2017
    Posts:
    9
    Yes, that's what I mean by resetting them. Here's what you need to do to reproduce it in the demo scene:

    1) In the hierarchy, choose the face-shaped spline. Then in inspector, uncheck the "Loop".
    2) Then unfold the spline in the hierarchy to expose all its points. Then choose all its points and do the same thing: set the Z positions into 0. This will make all the points in one plane.
    3) Now, select the first point, and then set the "Preceding Control Point" and "Following Control Point" into (0,0,0) in the inspector window.
    4) Back to hierarchy, select "CubeConstantSpeed". Then set the "Travel Mode" into "Ping Pong". This step is not necessary but make things easier to see the problem later.
    5) Now start playing.

    I hope I describe the step correctly. Sorry for the late reply and thank you for your quick reply.
     
  2. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    Thank you for the detailed steps, I was able to reproduce the issue. However, I'm not sure how I should approach this problem. It is caused by this function:

    Code (CSharp):
    1. public Vector3 MoveAlongSpline( ref float normalizedT, float deltaMovement, int accuracy = 3 )
    2. {
    3.     // Credit: https://gamedev.stackexchange.com/a/27138
    4.  
    5.     float constant = deltaMovement / ( ( loop ? endPoints.Count : endPoints.Count - 1 ) * accuracy );
    6.     for( int i = 0; i < accuracy; i++ )
    7.         normalizedT += constant / GetTangent( normalizedT ).magnitude; // Problematic line
    8.  
    9.     return GetPoint( normalizedT );
    10. }
    GetTangent returns 0 if controls points are set to (0,0,0). Then normalizedT immediately becomes Infinity. I think I can force the control points to have a minimum magnitude of 0.1 which would resolve this issue. What do you think?
     
    siempus likes this.
  3. siempus

    siempus

    Joined:
    Nov 29, 2017
    Posts:
    9
    I personally like the code as it because if I work in 3d, I can still have (0,0,0) control points. But I'm sure people out there will stumble on the same issue sooner or later

    Setting up minimum control point magnitude will solve this problem and I'm okay with that. However, If possible, I rather solve the issue by manipulating the "GetTangent( normalizedT ).magnitude" to avoid infinity. Because the infinity issue seems only affecting this "MoveAlongSpline" function. This will leave everything else intact.

    I don't know much about math and this Tangent stuff surely beyond my grasp. Sorry if I say something ridiculous.
     
  4. siempus

    siempus

    Joined:
    Nov 29, 2017
    Posts:
    9
    Or, you can simply put "yellow warning icon" on control point inspector about that specific issue. Just like what Unity does:

    Screen Shot 2019-07-07 at 07.25.07.png
     
  5. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    These are really nice recommendations, thank you! I'll see what I can do.

    P.S. Updated the plugin on GitHub to show a warning message as you've suggested.
     
    Last edited: Jul 7, 2019
    siempus likes this.
  6. siempus

    siempus

    Joined:
    Nov 29, 2017
    Posts:
    9
    You're very welcome. Your plugin is one of the best there is and I'm sure anybody is willing to help you to improve it.
     
    yasirkula likes this.
  7. EatHeat

    EatHeat

    Joined:
    May 12, 2015
    Posts:
    5
    I just came upon this plugin while dealing with moving a Direction Light in a path - https://forum.unity.com/threads/moving-directional-light-in-sunpath.717725/. This has helped me achieve what I was trying to do. However, I am stuck at a certain point.

    I am trying to move a sun on two separate splines with a Slider. Here is an image for reference. Spline 1 is the topSpline and Spline 2 is the bottomSpline. Initially the Sun is on Spline 2 and then I need to move it to Spline 1 at runtime.

    sunpath2.jpg

    Here is the code that I am using -

    Code (CSharp):
    1.  
    2.     //Spline Changing Button
    3.     public Button splineButton;
    4.     private int counter = 0;
    5.  
    6.     private Transform cachedTransform;
    7.  
    8.     private bool isBottomSpline;
    9.     public BezierSpline splineBottom;
    10.     public BezierSpline splineTop;
    11.  
    12.     public float progress = 0f;
    13.  
    14.     //public BezierSpline Spline { get { return spline; } }
    15.  
    16.     public float NormalizedT
    17.     {
    18.         get { return progress; }
    19.         set { progress = value; }
    20.     }
    21.  
    22.     public float movementLerpModifier = 10f;
    23.     public float rotationLerpModifier = 10f;
    24.  
    25.     public bool lookForward = true;
    26.  
    27.     private bool isGoingForward = true;
    28.     public bool MovingForward { get { return isGoingForward; } }
    29.  
    30.     public UnityEvent onProgressChanged = new UnityEvent();
    31.  
    32.     private void Awake()
    33.     {
    34.         cachedTransform = transform;
    35.     }
    36.  
    37.     void Start()
    38.     {
    39.         isBottomSpline = true;
    40.     }
    41.  
    42.     private void Update()
    43.     {
    44.         if (isBottomSpline)
    45.         {
    46.             cachedTransform.position = Vector3.Lerp(cachedTransform.position, splineBottom.GetPoint(progress), movementLerpModifier * Time.deltaTime);
    47.             bool movingForward = MovingForward;
    48.  
    49.             //Rest of the code from BezierWalkerWithSpeed
    50.             Debug.Log("1 + isBottomSpline = " + isBottomSpline);
    51.         }
    52.         else
    53.         {
    54.             cachedTransform.position = Vector3.Lerp(cachedTransform.position, splineTop.GetPoint(progress), movementLerpModifier * Time.deltaTime);
    55.             bool movingForward = MovingForward;
    56.  
    57.             //Rest of the code from BezierWalkerWithSpeed
    58.             Debug.Log("2 + isBottomSpline = " + isBottomSpline);
    59.         }
    60.  
    61.     }
    62.  
    63.     public void MoveSunAlongPath(Slider slider)
    64.     {
    65.         progress = slider.value;
    66.     }
    67.  
    68.     public void toggleSpline()
    69.     {
    70.         counter++;
    71.         if (counter % 2 == 0)
    72.         {
    73.             isBottomSpline = true;
    74.             Debug.Log("3 + isBottomSpline = " + isBottomSpline);
    75.         }
    76.         else
    77.         {
    78.             isBottomSpline = false;
    79.             Debug.Log("4 + isBottomSpline = " + isBottomSpline);
    80.         }
    81.     }
    When the Sun is on Spline 2, it is working as intended. Now the issue is that when I am trying to change the value of isBottomSpline to false at runtime, the Sun is not moving onto Spline 1. But when I made isBottomSpline public and changed it to false through inspector, it moved onto Spline 1 at runtime. Also, Debug Log "2" is only getting called when I am changing the value to false through the inspector. What am I doing wrong?
     
  8. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    When do you call toggleSpline? I've tested the script in my project and assigned the toggleSpline function to a button in my canvas. Clicking the button did toggle between the two splines.
     
  9. EatHeat

    EatHeat

    Joined:
    May 12, 2015
    Posts:
    5
    I have assigned the toggleSpline to a Button as well but clicking the Button does not change the spline. Debug.Log("4") gets called but Debug.Log("2") does not. I have attached a screenshot of my Inspector. Here is the complete code for my Update() method.

    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if (isBottomSpline == true)
    4.         {
    5.             cachedTransform.position = Vector3.Lerp(cachedTransform.position, splineBottom.GetPoint(progress), movementLerpModifier * Time.deltaTime);
    6.             bool movingForward = MovingForward;
    7.             if (lookForward)
    8.             {
    9.                 Quaternion targetRotation;
    10.                 if (movingForward)
    11.                     targetRotation = Quaternion.LookRotation(splineBottom.GetTangent(progress));
    12.                 else
    13.                     targetRotation = Quaternion.LookRotation(-splineBottom.GetTangent(progress));
    14.  
    15.                 cachedTransform.rotation = Quaternion.Lerp(cachedTransform.rotation, targetRotation, rotationLerpModifier * Time.deltaTime);
    16.             }
    17.  
    18.             if (movingForward)
    19.             {
    20.                 if (progress >= 1f)
    21.                 {
    22.                     progress = 1f;
    23.                 }
    24.                 onProgressChanged.Invoke();
    25.             }
    26.             else
    27.             {
    28.                 if (progress <= 0f)
    29.                 {
    30.                     progress = 0f;
    31.                 }
    32.                 onProgressChanged.Invoke();
    33.             }
    34.             Debug.Log("1 + isBottomSpline = " + isBottomSpline);
    35.         }
    36.         else
    37.         {
    38.             cachedTransform.position = Vector3.Lerp(cachedTransform.position, splineTop.GetPoint(progress), movementLerpModifier * Time.deltaTime);
    39.             bool movingForward = MovingForward;
    40.             if (lookForward)
    41.             {
    42.                 Quaternion targetRotation;
    43.                 if (movingForward)
    44.                     targetRotation = Quaternion.LookRotation(splineTop.GetTangent(progress));
    45.                 else
    46.                     targetRotation = Quaternion.LookRotation(-splineTop.GetTangent(progress));
    47.  
    48.                 cachedTransform.rotation = Quaternion.Lerp(cachedTransform.rotation, targetRotation, rotationLerpModifier * Time.deltaTime);
    49.             }
    50.  
    51.             if (movingForward)
    52.             {
    53.                 if (progress >= 1f)
    54.                 {
    55.                     progress = 1f;
    56.                 }
    57.                 onProgressChanged.Invoke();
    58.             }
    59.             else
    60.             {
    61.                 if (progress <= 0f)
    62.                 {
    63.                     progress = 0f;
    64.                 }
    65.                 onProgressChanged.Invoke();
    66.             }
    67.             Debug.Log("2 + isBottomSpline = " + isBottomSpline);
    68.         }
    69.  
    70.     }
    On another note, I have attached my Directional Light to the Sun. Is it possible to rotate it while moving in a manner that it keeps on facing the center of the model (0,0,0)?
     
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    Here's the complete script I'm using:

    Code (CSharp):
    1. using BezierSolution;
    2. using UnityEngine;
    3. using UnityEngine.Events;
    4. using UnityEngine.UI;
    5.  
    6. public class SplineTest : MonoBehaviour
    7. {
    8.     //Spline Changing Button
    9.     public Button splineButton;
    10.     private int counter = 0;
    11.  
    12.     private Transform cachedTransform;
    13.  
    14.     private bool isBottomSpline;
    15.     public BezierSpline splineBottom;
    16.     public BezierSpline splineTop;
    17.  
    18.     public float progress = 0f;
    19.  
    20.     //public BezierSpline Spline { get { return spline; } }
    21.  
    22.     public float NormalizedT
    23.     {
    24.         get { return progress; }
    25.         set { progress = value; }
    26.     }
    27.  
    28.     public float movementLerpModifier = 10f;
    29.     public float rotationLerpModifier = 10f;
    30.  
    31.     public bool lookForward = true;
    32.  
    33.     private bool isGoingForward = true;
    34.     public bool MovingForward { get { return isGoingForward; } }
    35.  
    36.     public UnityEvent onProgressChanged = new UnityEvent();
    37.  
    38.     private void Awake()
    39.     {
    40.         cachedTransform = transform;
    41.     }
    42.  
    43.     void Start()
    44.     {
    45.         isBottomSpline = true;
    46.     }
    47.  
    48.     private void Update()
    49.     {
    50.         if( isBottomSpline == true )
    51.         {
    52.             cachedTransform.position = Vector3.Lerp( cachedTransform.position, splineBottom.GetPoint( progress ), movementLerpModifier * Time.deltaTime );
    53.             bool movingForward = MovingForward;
    54.             if( lookForward )
    55.             {
    56.                 Quaternion targetRotation;
    57.                 if( movingForward )
    58.                     targetRotation = Quaternion.LookRotation( splineBottom.GetTangent( progress ) );
    59.                 else
    60.                     targetRotation = Quaternion.LookRotation( -splineBottom.GetTangent( progress ) );
    61.  
    62.                 cachedTransform.rotation = Quaternion.Lerp( cachedTransform.rotation, targetRotation, rotationLerpModifier * Time.deltaTime );
    63.             }
    64.  
    65.             if( movingForward )
    66.             {
    67.                 if( progress >= 1f )
    68.                 {
    69.                     progress = 1f;
    70.                 }
    71.                 onProgressChanged.Invoke();
    72.             }
    73.             else
    74.             {
    75.                 if( progress <= 0f )
    76.                 {
    77.                     progress = 0f;
    78.                 }
    79.                 onProgressChanged.Invoke();
    80.             }
    81.             Debug.Log( "1 + isBottomSpline = " + isBottomSpline );
    82.         }
    83.         else
    84.         {
    85.             cachedTransform.position = Vector3.Lerp( cachedTransform.position, splineTop.GetPoint( progress ), movementLerpModifier * Time.deltaTime );
    86.             bool movingForward = MovingForward;
    87.             if( lookForward )
    88.             {
    89.                 Quaternion targetRotation;
    90.                 if( movingForward )
    91.                     targetRotation = Quaternion.LookRotation( splineTop.GetTangent( progress ) );
    92.                 else
    93.                     targetRotation = Quaternion.LookRotation( -splineTop.GetTangent( progress ) );
    94.  
    95.                 cachedTransform.rotation = Quaternion.Lerp( cachedTransform.rotation, targetRotation, rotationLerpModifier * Time.deltaTime );
    96.             }
    97.  
    98.             if( movingForward )
    99.             {
    100.                 if( progress >= 1f )
    101.                 {
    102.                     progress = 1f;
    103.                 }
    104.                 onProgressChanged.Invoke();
    105.             }
    106.             else
    107.             {
    108.                 if( progress <= 0f )
    109.                 {
    110.                     progress = 0f;
    111.                 }
    112.                 onProgressChanged.Invoke();
    113.             }
    114.             Debug.Log( "2 + isBottomSpline = " + isBottomSpline );
    115.         }
    116.  
    117.     }
    118.  
    119.     public void MoveSunAlongPath( Slider slider )
    120.     {
    121.         progress = slider.value;
    122.     }
    123.  
    124.     public void toggleSpline()
    125.     {
    126.         counter++;
    127.         if( counter % 2 == 0 )
    128.         {
    129.             isBottomSpline = true;
    130.             Debug.Log( "3 + isBottomSpline = " + isBottomSpline );
    131.         }
    132.         else
    133.         {
    134.             isBottomSpline = false;
    135.             Debug.Log( "4 + isBottomSpline = " + isBottomSpline );
    136.         }
    137.     }
    138. }
    Clicking the button does change the value of isBottomSpline for me. Unless there is an exception, its value must change, it is a very simple and straightforward function. I'm guessing that toggleSpline isn't getting called at all, you can see if this is the case by putting a Debug.Log at its first line. Maybe the button is somehow losing the functions registered to its On Click event at runtime? Or maybe the button you configure from the Inspector isn't the button you are clicking?

    The easiest way to rotate the sun would be to put a GameObject at 0,0,0 and call sunTransform.LookAt(objectAtCenter.transform) in Update.
     
  11. EatHeat

    EatHeat

    Joined:
    May 12, 2015
    Posts:
    5
    Thank you for the quick reply and sorry if I was not clear with my issue in the earlier post. The value of isBottomSpline changes for me as well and toggleSpline() is also getting called. So the problem is not with toggleSpline() getting called. My issue is that even when isBottomSpline is changed to false at runtime, the Sun is not shifting onto the topSpline in the Update() method. All my debugs except
    Debug.Log( "2 + isBottomSpline = " + isBottomSpline );
    are getting called.
     
  12. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    That's really weird. Are you sure that you didn't type
    if( isBottomSpline = true )
    instead of
    if( isBottomSpline == true )
    in Update? The code I've sent you works without any issues, can you test it on a new scene?
     
  13. EatHeat

    EatHeat

    Joined:
    May 12, 2015
    Posts:
    5
    After a bit of trial and error, I decided to make isBottomSpline static and moved toggleSpline() and isBottomSpline to a separate class. Then I called isBottomSpline from the Update() method of my SunpathControl class and I have no idea why but it worked flawlessly. Funny thing is it is the exact code that I posted above just moved to a separate class.

    Also thank you for the tip regarding rotating the sun. That worked perfectly as well. :)
     
    yasirkula likes this.
  14. Will_Dev

    Will_Dev

    Joined:
    May 24, 2017
    Posts:
    24
    Hi. I wanted to ask if the autoconstructspline method is expensive to use in runtime.
     
  15. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    I haven't tried to call it frequently at runtime. If you test it on your own, please share your findings here, as well :)
     
  16. bigChris

    bigChris

    Joined:
    Jul 5, 2017
    Posts:
    8
    I'm instantiating 12 splines at run time without issue.
     
    yasirkula likes this.
  17. bigChris

    bigChris

    Joined:
    Jul 5, 2017
    Posts:
    8
    Hello Yasirkula,

    What a wonderfully useful framework!!

    Greatly simplified, all in code, consider Spline_1 set to loop. Then StartSpline, essentially a “spur”, that has it’s endpoint set to a point on Spline_1 at Spline_1’s NormalizedT = to 1/12th (0.8333333…) and swapping the onPathCompleted Event to the next Event (a lot more happens involving a lot more splines). This all works well - objects travel down the spline(s) and smoothly transition to the next spline as needed…

    The only issue is when the object actually reaches the end of the StartSpline and transitions to Spline_1, (and all other spline transition points) at that moment, the call:

    targetRotation = Quaternion.LookRotation(spline.GetTangent(progress));


    …causes a “twitch” in the object, as

    StartSpline.GetTangent(progress)

    and

    Spline_1.GetTangent(progress)

    are so different. The spline change takes effect in FixedUpdate (superfast moving objects), so I’m thinking what is needed is to somehow change StartSpline so that at points approaching Spline_1, the difference in tangent values become much less and minimizes the “twitch”.

    I think the solution is to get a reference to the endpoint in StartSpline and modify it such that the points tangent value matches up with Spline_1 point at NormalizedT = 0.8333333… I’ve looked at the “Shape the spline” section on your GitHub repository, as well as the functions defined within BezierPoint.cs and BezierSpline.cs, and can’t figure out how to do this in code.

    Any thoughts greatly appreciated. Thanks again!!
     
  18. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    GetTangent returns the following value at the final point of a non-looping spline:

    Code (CSharp):
    1. if( normalizedT >= 1f )
    2. {
    3.     int index = endPoints.Count - 1;
    4.     return 3f * ( endPoints[index].position - endPoints[index].precedingControlPointPosition );
    5. }
    So it is affected only by the relative position of the final point's preceding control point. As you can't change the final point's position itself in your case, you need to adjust the position of StartSpline's final point's preceding control point. So, you can try something like this:

    Code (CSharp):
    1. BezierSpline startSpline = ...;
    2. Vector3 spline1TangentAtIntersection = ...;
    3. int index = startSpline.Count - 1;
    4. startSpline[index].precedingControlPointPosition = startSpline[index].position - spline1TangentAtIntersection / 3f;
     
  19. bigChris

    bigChris

    Joined:
    Jul 5, 2017
    Posts:
    8
    That worked!!

    Code (CSharp):
    1. Vector3 nextSplineTangentAtIntersection = allSplines[k].GetTangent(0.08333333333333333f);
    2. int index = startSpline.Count - 1;
    3. startSpline[index].precedingControlPointPosition = paths[k][1].waypointLocation -
    4.     nextSplineTangentAtIntersection / 3f;
    where k is the for loop iterator,
    0.83333... is the NormalizdT (progress) at startSpline to nextSpline transition point (1/12th of the way down nextSpline),
    and paths[k][1].waypointLocation is simply the Vector3 that precisely denotes the transition point from which we subtract the Vector3 nextSplineTangentAtIntersection / 3f. When all of that is set to the precedingControlPointPosition, the startSpline endpoint 1 is essentially rotated so that its tangent matches the nextsplines Tangent and we get a nice no twitch transition!
    Thanks again!
     
    yasirkula likes this.
  20. KOemxe

    KOemxe

    Joined:
    May 1, 2018
    Posts:
    33
    Hey @yasirkula,

    This seems super promising, but have you seen and know a possible implementation of leading a race car with this kind of spline locomotive? I've been struggling to come up with a good way to "lead" a little bit ahead of my race car to follow a similar structure moving between ends of curves as waypoints, but so far it seems I have either my object move too far or not far enough ahead of the car...
     
  21. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    I see. You can try this:
    • Call bezierSpline.FindNearestPointTo for player's race car to find its approximate position (normalizedT) on the spline
    • Call bezierSpline.MoveAlongSpline with the calculated normalizedT to calculate the point on the spline that is approximately deltaMovement meters ahead of normalizedT
    • Both of these functions have an optional accuracy parameter for more accurate results
     
    KOemxe likes this.
  22. GaZnoDrone

    GaZnoDrone

    Joined:
    Mar 3, 2015
    Posts:
    28
    Hey guys,
    I am sorry if its ther in the documents.
    I am using bezierWalkerWithSpeed.
    I have made some modification just for my game.

    on update

    if(m_bBallKicked)
    Execute( Time.deltaTime );

    no when i stop it by making m_bBallKicked = false

    now how do I reset it from the first point now
     
  23. GaZnoDrone

    GaZnoDrone

    Joined:
    Mar 3, 2015
    Posts:
    28

    I DID THIS, when ever I needed to reset the path traveled. Is this the correct way to do it?
    m_normalizedT = 0;
     
    yasirkula likes this.
  24. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    Yes, that is the correct way.
     
  25. KOemxe

    KOemxe

    Joined:
    May 1, 2018
    Posts:
    33
    Late response, I know. When you're finding the nearest point, are you ONLY going to get the point as it's been laid down on the spline? Like, the spline might have 51 points. Are you only going to return one of THOSE points with the FindNearestPointTo function? Or are able to get a point that's, say, IN BETWEEN two of those points on the spline?
     
  26. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    It will usually return a point that's in between two of the 51 points on the spline.
     
    KOemxe likes this.
  27. KOemxe

    KOemxe

    Joined:
    May 1, 2018
    Posts:
    33
    @yasirkula OMG THANK YOU SO MUCH.

    I have to admit, I didn't really use all of your source code, only a couple of key functions related to exactly what you mentioned. It was JUST what I needed. You're the bauss, my dude.
     
    yasirkula likes this.
  28. yaboo

    yaboo

    Joined:
    Mar 23, 2016
    Posts:
    37
    @yasirkula
    Add a line renderer component if you have time, thanks
     
  29. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
  30. xill

    xill

    Joined:
    Mar 13, 2013
    Posts:
    4
    Hey Yasir great asset, quick question how can I reset progress like
    normalizedT = 0; after completing the path and changing target spline.
    It works when I put 0 in editor but not via script.

    Code (CSharp):
    1.  
    2.     myMover = GetComponent<BezierSolution.BezierWalkerWithSpeed>();
    3. ///
    4.         myMover.progress = 0;
    5.     myMover.spline = myNextPath;
    6.  
    Also tried onPathCompletedCalledAt0&1 = false;

    I can do this
    Code (CSharp):
    1.  Destroy(myMover);
    2.         myMover = gameObject.AddComponent<BezierSolution.BezierWalkerWithSpeed>();
    3. myMover.progress = 0;
    4.     myMover.spline = myNextPath;
    5.  
    but I don't think thats the way and it limits what I'm trying to do
     
    Last edited: Apr 3, 2020
  31. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    Try inserting
    myMover.Execute(0f);
    to the bottom.
     
  32. xill

    xill

    Joined:
    Mar 13, 2013
    Posts:
    4
    Ah just noticed I was using an old version.
    Still... Execute didn't work.

    I don't get how it works when you slide the NormalizedT in the inspector but not when edited via script ¯\_(ツ)_/¯
     
  33. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    I think I've found the issue. Seems like the onPathCompleted event is called before m_normalizedT is updated internally; thus, any changes made to normalizedT inside onPathCompleted were overridden. To fix this issue, try changing the contents of BezierWalkerWithSpeed.cs as follows:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Events;
    3.  
    4. namespace BezierSolution
    5. {
    6.     public class BezierWalkerWithSpeed : BezierWalker
    7.     {
    8.         public BezierSpline spline;
    9.         public TravelMode travelMode;
    10.  
    11.         public float speed = 5f;
    12.         [SerializeField]
    13.         [Range( 0f, 1f )]
    14.         private float m_normalizedT = 0f;
    15.  
    16.         public override BezierSpline Spline { get { return spline; } }
    17.  
    18.         public override float NormalizedT
    19.         {
    20.             get { return m_normalizedT; }
    21.             set { m_normalizedT = value; }
    22.         }
    23.  
    24.         //public float movementLerpModifier = 10f;
    25.         public float rotationLerpModifier = 10f;
    26.  
    27.         [System.Obsolete( "Use lookAt instead", true )]
    28.         [System.NonSerialized]
    29.         public bool lookForward = true;
    30.         public LookAtMode lookAt = LookAtMode.Forward;
    31.  
    32.         private bool isGoingForward = true;
    33.         public override bool MovingForward { get { return ( speed > 0f ) == isGoingForward; } }
    34.  
    35.         public UnityEvent onPathCompleted = new UnityEvent();
    36.         private bool onPathCompletedCalledAt1 = false;
    37.         private bool onPathCompletedCalledAt0 = false;
    38.  
    39.         private void Update()
    40.         {
    41.             Execute( Time.deltaTime );
    42.         }
    43.  
    44.         public override void Execute( float deltaTime )
    45.         {
    46.             float targetSpeed = ( isGoingForward ) ? speed : -speed;
    47.  
    48.             Vector3 targetPos = spline.MoveAlongSpline( ref m_normalizedT, targetSpeed * deltaTime );
    49.  
    50.             transform.position = targetPos;
    51.             //transform.position = Vector3.Lerp( transform.position, targetPos, movementLerpModifier * deltaTime );
    52.  
    53.             bool movingForward = MovingForward;
    54.  
    55.             if( lookAt == LookAtMode.Forward )
    56.             {
    57.                 Quaternion targetRotation;
    58.                 if( movingForward )
    59.                     targetRotation = Quaternion.LookRotation( spline.GetTangent( m_normalizedT ) );
    60.                 else
    61.                     targetRotation = Quaternion.LookRotation( -spline.GetTangent( m_normalizedT ) );
    62.  
    63.                 transform.rotation = Quaternion.Lerp( transform.rotation, targetRotation, rotationLerpModifier * deltaTime );
    64.             }
    65.             else if( lookAt == LookAtMode.SplineExtraData )
    66.                 transform.rotation = Quaternion.Lerp( transform.rotation, spline.GetExtraData( m_normalizedT, InterpolateExtraDataAsQuaternion ), rotationLerpModifier * deltaTime );
    67.  
    68.             if( movingForward )
    69.             {
    70.                 if( m_normalizedT >= 1f )
    71.                 {
    72.                     if( travelMode == TravelMode.Once )
    73.                         m_normalizedT = 1f;
    74.                     else if( travelMode == TravelMode.Loop )
    75.                         m_normalizedT -= 1f;
    76.                     else
    77.                     {
    78.                         m_normalizedT = 2f - m_normalizedT;
    79.                         isGoingForward = !isGoingForward;
    80.                     }
    81.  
    82.                     if( !onPathCompletedCalledAt1 )
    83.                     {
    84.                         onPathCompletedCalledAt1 = true;
    85. #if UNITY_EDITOR
    86.                         if( UnityEditor.EditorApplication.isPlaying )
    87. #endif
    88.                             onPathCompleted.Invoke();
    89.                     }
    90.                 }
    91.                 else
    92.                 {
    93.                     onPathCompletedCalledAt1 = false;
    94.                 }
    95.             }
    96.             else
    97.             {
    98.                 if( m_normalizedT <= 0f )
    99.                 {
    100.                     if( travelMode == TravelMode.Once )
    101.                         m_normalizedT = 0f;
    102.                     else if( travelMode == TravelMode.Loop )
    103.                         m_normalizedT += 1f;
    104.                     else
    105.                     {
    106.                         m_normalizedT = -m_normalizedT;
    107.                         isGoingForward = !isGoingForward;
    108.                     }
    109.  
    110.                     if( !onPathCompletedCalledAt0 )
    111.                     {
    112.                         onPathCompletedCalledAt0 = true;
    113. #if UNITY_EDITOR
    114.                         if( UnityEditor.EditorApplication.isPlaying )
    115. #endif
    116.                             onPathCompleted.Invoke();
    117.                     }
    118.                 }
    119.                 else
    120.                 {
    121.                     onPathCompletedCalledAt0 = false;
    122.                 }
    123.             }
    124.         }
    125.     }
    126. }
     
  34. xill

    xill

    Joined:
    Mar 13, 2013
    Posts:
    4
    Could'nt test yet thanks for fast reply mate, I'll let you know.
     
  35. xill

    xill

    Joined:
    Mar 13, 2013
    Posts:
    4
    Works like a charm. ;)
     
    yasirkula likes this.
  36. Mr-Oliv

    Mr-Oliv

    Joined:
    Sep 14, 2012
    Posts:
    26
    Thanks for sharing this amazing tool and being available to answer questions!
    Is there a handy way to get the normalized t value from a given point index without iterating over the spline?
    Thanks!
     
  37. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    If spline has 3 points, first point will have t=0, second one t=0.5, and the last one t=1. Lengths of the segments don't contribute to the t values at the moment.
     
  38. Mr-Oliv

    Mr-Oliv

    Joined:
    Sep 14, 2012
    Posts:
    26
    Ah nice! That's actually even better. Thanks!
     
  39. kobi666

    kobi666

    Joined:
    Oct 13, 2014
    Posts:
    2
    Hey there everyone on this thread :)

    I'm using the asset and it's awesome, and I want to be able to do something that i'm having a difficulty with:

    I want to create a walker that starts walking (either forward or backward) on the spline,
    but starting from a point in the middle of it.
    i'm trying to RE this,
    but I would really be happy for a tip \ shortcut on how to do so.

    Thanks!
     
  40. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
  41. ElegantMetal

    ElegantMetal

    Joined:
    Oct 17, 2015
    Posts:
    1
    Love the tool! It's been super smooth to use. I'm using the particle system to visualize a path defined by the user, and it works great. However, it doesn't seem to work in a build at all, and I can't seem to figure out why. Any idea on where I should start looking for the problem?
     
  42. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
  43. Gaviathan2

    Gaviathan2

    Joined:
    May 23, 2013
    Posts:
    13
    Hi, I've got road tile prefabs with splines in that are randomly placed at runtime. I'm using bezierWalkerWithSpeed to move vehicles along these roads. They follow the spline and when OnPathCompleted is called my script finds the closest spline point to the vehicle's current location that is not a child of the current spline. The script will set the next spline to reverse if need be and the normalised T to whatever it should be at that position and direction. The movement takes it in the correct direction and if the next spline is in the same order as the previous one (reversed/not reversed) the transition is smooth. Unfortunately if the next spline is going in the opposite direction the car twitches on the transition,as if it is turning around when it doesn't need to. I don't know if the problem is caused by the way I'm choosing the next spline or by the way I'm setting up the next spline. Here is my code,

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using BezierSolution;
    5. using PowerPlayer;
    6.  
    7. public class GTraffic : MonoBehaviour
    8. {
    9.     private BezierWalkerWithSpeed bWalker;
    10.     public BezierSpline bSpline;
    11.     private BezierPoint[] bpArray;
    12.     private BezierPoint nextBp;
    13.  
    14.     public void NextSpline()
    15.     {
    16.         nextBp = PPHelp.FindClosest3(bpArray, transform.position, bSpline.endPoints);
    17.         bWalker.spline = nextBp.transform.GetComponentInParent<BezierSpline>();
    18.         bSpline = bWalker.spline;
    19.         if(bWalker.spline.endPoints[0] == nextBp)
    20.         {
    21.             bWalker.NormalizedT = 0;
    22.             bWalker.reversed = false;
    23.         }
    24.         else
    25.         {
    26.             bWalker.NormalizedT = 1;
    27.             bWalker.reversed = true;
    28.         }
    29.     }
    30.     // Start is called before the first frame update
    31.     void Start()
    32.     {
    33.         bWalker = GetComponent<BezierWalkerWithSpeed>();
    34.         bpArray = GameObject.FindObjectsOfType(typeof(BezierPoint)) as BezierPoint[];
    35.     }
    36. }
    PPHelp.FindClosest3 (array of all bezier points in scene, the position of the object we want to find the closest object to, A list of bezier points we don't want to include in the search)

    I've probably overcomplicated or over simplified something but I'm hoping you'll know what I'm doing wrong, thanks in advance.

    EDIT - I have the walker set to once and if I disable my script and let it run the spline just once it does the twitch at the end. I tried setting if(m_normalisedT >= 1f) to if(m_normalisedT >= 0.9f) but that made no difference. I was trying to get something to kick in before the twitch could happen but obviously that was not the solution.
     
    Last edited: Aug 2, 2020 at 9:41 AM
  44. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,586
    It is possible that some of the splines have duplicate points at the end (2 points instead of 1) since you've said that the twitch also happens when the walker runs without your script. If you can't spot any issues on the splines, you can set the walker's Look At mode to None and interpolate the rotation yourself using spline.GetTangent (check out this code snippet).
     
unityunity