Issue with events and reversing bezier path



  • Hi Tom,

    thanks again for the explanation, you are missing one detail. From the scripting reference:

    SetPath (PathManager newPath)
    Changes the current path of this object and starts movement.

    Settings on the movement script can only be taken into account if they are done before movement is started. You have to set "reverse" in your script before calling SetPath, not afterwards.



  • Thanks for the quick reply @Baroni. My mistake. That change fixes the backwards movement when using a PathManager path so it's now consistently going B->A everytime the PlayBackwards() function is called. Great!

    I'm still however experiencing ping-pong movement when repeatedly calling the same function on a BezierPathManager with my loop type set to 'none'. I'm using the function below.

        [ContextMenu("PlayBackwards")]
        void PlayBackwards() {
            splineMove.reverse = true;
            splineMove.SetPath(path);
        }
    

    Do you know what I need to change in that function to experience B->A movement everytime it's called for a bezier path?

    Cheers, Tom



  • Ah, there's indeed an oversight in the BezierPathManager script. I guess no one tried to call reverse that often before :)

    While the PathManager script returns a copy of waypoints for the movement script to work on, the BezierPathManager always returns the same array of waypoints, which results in the movement script reversing the same array over and over again. To be in line with the PathManager, the GetPathPoints method in the BezierPathManager (line 133) should actually be:

            /// <summary>
            /// Returns waypoint positions (path positions) as Vector3 array.
            /// <summary>
            public override Vector3[] GetPathPoints(bool local = false)
            {
                Vector3[] copy = new Vector3[pathPoints.Length];
    
                if(local)
                {
                    for (int i = 0; i < copy.Length; i++)
                        copy[i] = transform.InverseTransformPoint(pathPoints[i]);
                }
                else
                    System.Array.Copy(pathPoints, copy, pathPoints.Length);
    
                return copy;
            }
    

    Let's hope you don't find any other issues ;)



  • That change works perfectly thanks @Baroni. I've no more issues to report! Will this fix and the event fix above be included in the next update of the plugin? Or will I need to copy these changes back in if we update the plugin in the future?

    Thanks again for your help and quick replies. Cheers, Tom



  • Thanks! They're going to be included in the next update.



  • Great to see that this bug was already reported and being worked on, I was having the exact same issue.

    I recorded a clip of what I'm experiencing due to this and uploaded it here (unlisted) https://www.youtube.com/watch?v=7O0mjhahjKA&feature=youtu.be
    The basic concept of my system is that I have multiple paths strung together to then be able to have my characters switch from one path to another to create a road network they can follow to get to where they need to go whilst following a pre-determined path.

    As you can see on the video the bottom one which uses the standard/straight path works flawlessly, the top path which is roughly identical but uses Bezier paths just stops after the first reverse even though all parameters are exactly the same.

    I saw a couple suggestions in this thread which I have tried myself as well already but all of them produced even stranger results as the cube would just jump around between nodes, flicker teleport or go to a completely unexpected location. Settings the cube to Ping Pong also didn't fix it for me and caused strange behavior as well.

    I also tried the latest change in the GetPathPoints method but it still got stuck on the exact same location for me.

    The only changes I've made to the original version were that I added a C# event for when a waypoint is reached (so that it works on a path level and not the SplineMove level) and a couple Debug.Log() statements to print out the data that's coming in (as is visible on the video).

    Oh and I did also change SetPath to accept a start index and a reversed parameter

    public void SetPath(PathManager newPath, int newStartPoint = 0, bool isReversed = false)
            {
                //disable any running movement methods
                Stop();
                //set new path container
                pathContainer = newPath;
    
                startPoint = newStartPoint;
    
                reverse = isReversed;
    
                //restart movement
                StartMove();
            }
    

    If you want to re-create my setup then in the SplineMove class (btw I renamed it to SplineMove CamelCase instead of splineMove pascalCase, same with the NavMove) add this event handler

            public event EventHandler<Tuple<int, bool>> onWaypointReached;
    

    And in OnWaypointChange add

                onWaypointReached?.Invoke(this, new Tuple<int, bool>(index, reverse));
    

    Right before the check for the events.

    Then create a new class called SplineEventHandler and paste this code in it

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using SWS;
    using UnityEngine;
    
    [Serializable]
    public struct PathConnection
    {
        public PathManager StartPath;
        public int LastIndex;
        public bool FromDirection;
    
        public PathManager NewPath;
        public int NewIndex;
        public bool Reversed;
    }
    
    [RequireComponent(typeof(SplineMove))]
    public class SplineEventHandler : MonoBehaviour
    {
        private SplineMove _splineMove;
    
        [SerializeField]
        public List<PathConnection> Connections;
    
        private void Awake()
        {
            _splineMove = GetComponent<SplineMove>();
            _splineMove.onWaypointReached += SplineMoveOnWaypointReached;
        }
    
        private void SplineMoveOnWaypointReached(object sender, Tuple<int, bool> eventData)
        {
            int index = eventData.Item1;
            bool reversedDirection = eventData.Item2;
            
            if (Connections.Any(x => x.StartPath == _splineMove.pathContainer && x.LastIndex == index && x.FromDirection == reversedDirection))
            {
                PathConnection connection = Connections.First(x => x.StartPath == _splineMove.pathContainer && x.LastIndex == index && x.FromDirection == reversedDirection);
                Debug.Log($"Hit {connection.StartPath.name} at index {index} with direction {reversedDirection}");
    
                if (connection.NewPath == null)
                {
                    _splineMove.Reverse();
                }
                else
                {
                    _splineMove.moveToPath = true;
                    _splineMove.SetPath(connection.NewPath, connection.NewIndex, connection.Reversed);
                }
            }
        }
    }
    
    

    Then simply add the SplineEventHandler component to the cube that has the SplineMove class and setup the connections

    StartPath = the path that the cube is currently on
    LastIndex = the index on the start path at which it may perform the change
    FromDirection = the direction on the start path at which it may perform the chance (this is equal to the current 'reverse' property on the SplineMove)

    NewPath = the new path to move to
    NewIndex = the index where to start the path from
    Reversed = will this path be started normally or reversed

    Here's a screenshot of how I've got my paths setup in the video (paths 1, 2 & 3 go from left to right)

    alt text

    If you need any more information just let me know, I'd love to have this bug resolved properly so I can continue improving this road system (the UI is not really user friendly to say the least :P)



  • Hi Miley,

    I am sorry for the delayed response! I usually try to avoid the Unity 2019.3 mess, so I ran into compile errors with a lower Unity version, and had to download and install it anyway :) Thank you for your patience, and your detailed instructions (including the video) on what you would like to achieve, tried out and are stuck at.

    After reproducing your setup, I've found two things:

    • in your video at 1:35, I saw that you didn't implement this fix in the splineMove script yet. It is required though, to correctly calculate the waypoint index on bezier paths. That is in addition to the fixed GetPathPoints method above.
    • after implementing both fixes, you will now see that your object will indeed keep moving between paths back and forth. However, you will notice that when moving backwards, the object moves to the beginning of the path (instead of the end).

    That's because on bezier paths, the "startPoint" variable which you are assigning in the SplineEventHandler is taking all path points into account (displayed as gray dots in the scene view). The path segment detail is defined on the BezierPathManager inspector, so if you leave it at the default of 1, each waypoint segment has 10 path points. Therefore the object moves to path point 1 (close to waypoint 0), instead of waypoint 1. You would actually need to enter "10" to have it move to the end = waypoint 1.

    alt text

    While I am aware that this creates some confusion, we made it that way to allow objects move to positions between waypoints, which is not possible with standard curved paths. We already have a method converting path points to waypoint index. Would a method converting waypoint index to path points help, so you could keep the index value "1" in the SplineEventHandler?



  • Thanks for the reply, I just re-installed the package (as my line numbers were messed up due to my own changes) so I could properly integrate the changes you mentioned and indeed it started working a lot better. I do still have the issue now that when my cube reaches the very beginning again it doesn't reverse but instead jumps to the end of the first path and moves on to the middle path for some reason (changing the settings in my Connections list doesn't seem to do much here). Also there are always two lines being drawn from the first path to the second path even if there's nothing active on there (this didn't happen before IIRC). would there be any reason for these to be drawn?

    Here's a video of the current state, it's almost there but not quite https://www.youtube.com/watch?v=U0u7DYDxwyA

    As for the waypoint indicies, I changed them to go to index 10 instead of 1 on the returning paths as you suggested and that did make it work as expected, having a helper method for this which can convert the waypoint index to the point index would be very helpful to have (and maybe even a more abstract version on top of that of "GetFirstPoint" and "GetLastPoint" for when you just want the start or the end of the path (any point in between would then obviously go through the conversion helper method).

    Btw, why have you been avoiding 2019.3 so far? I've been using it as my main version since 2019.3.6f1 and am currently on 2019.3.13f1 (about to upgrade to 14f1 soon) and it's been very stable for me, 1f1 through 5f1 were indeed quite slow and unstable for me as well but every since 6f1 it's been great :)



  • Also there are always two lines being drawn from the first path to the second path even if there's nothing active on there (this didn't happen before IIRC). would there be any reason for these to be drawn?

    I don't see those in my repro scene, maybe you have another object with a movement script in the scene? Difficult to tell.

    I do still have the issue now that when my cube reaches the very beginning again it doesn't reverse but instead jumps to the end of the first path

    I was able to reproduce this, and it is a result of calling "splineMove.Reverse()" at the last waypoint, which invokes the waypointChange event twice - once for each direction. This results in firing off the next PathConnection in the SplineEventHandler instantly. Honestly I'm not sure how to avoid this at the moment, I may have to contact the developer of DOTween for that. You can just avoid calling Reverse though in your SplineEventHandler, and add the turn manually:

            if (Connections.Any(x => x.StartPath == _splineMove.pathContainer && x.LastIndex == index && x.FromDirection == reversedDirection))
            {
                PathConnection connection = Connections.First(x => x.StartPath == _splineMove.pathContainer && x.LastIndex == index && x.FromDirection == reversedDirection);
                Debug.Log($"Hit {connection.StartPath.name} at index {index} with direction {reversedDirection}");
    
                //removed Reverse here
                _splineMove.moveToPath = true;
                _splineMove.startPoint = connection.NewIndex;
                _splineMove.reverse = connection.Reversed;
                _splineMove.SetPath(connection.NewPath);
            }
    

    alt text

    having a helper method for this which can convert the waypoint index to the point index would be very helpful to have

    Thanks! Going to add that, with the other fixes, to the next version.

    a more abstract version on top of that of "GetFirstPoint" and "GetLastPoint"

    "GetFirstPoint" would just return 0 every time, as there is nothing to calculate :)
    "GetLastPoint" would then be the same as the conversion method, just passing in waypoints.Length.

    Btw, why have you been avoiding 2019.3 so far?

    As an asset developer I'm forced to use the first cycle version (2019.3.0) for uploading, due to supporting all versions above that. Personally, it's not about the stability of the editor itself, but because of the various Package Manager products, with most of them in preview - or not working well in combination with other packages. Basically since the Package Manager was released, we've had a guideline to avoid all packages shipping with it (except UI). That's why I'm still developing with one of the last versions without it (2017 LTS) or soon 2018 LTS.



  • I don't see those in my repro scene, maybe you have another object with a movement script in the scene? Difficult to tell.

    The extra lines seem to be quite random, on one run they're there and on the other their gone. Can't seem to get a grip on why it might happen but it seems to be only visual so I can basically ignore it.

    I was able to reproduce this, and it is a result of calling "splineMove.Reverse()" at the last waypoint, which invokes the waypointChange event twice - once for each direction. This results in firing off the next PathConnection in the SplineEventHandler instantly. Honestly I'm not sure how to avoid this at the moment, I may have to contact the developer of DOTween for that. You can just avoid calling Reverse though in your SplineEventHandler, and add the turn manually:

    Yes! That fixed the issue! There was a small bug still where it would jump back 1 point and then teleport to the end again when the path got reversed but I was able to fix that by changing the moveToPath to

    _splineMove.moveToPath = connection.StartPath != connection.NewPath;
    

    If moveToPath was always true then it would again double fire the event which would cause issues, if the two connected paths are the same then it's same to assume that the cube doesn't need to move to the new location

    Thanks! Going to add that, with the other fixes, to the next version.

    Yay!

    "GetFirstPoint" would just return 0 every time, as there is nothing to calculate :)

    Haha true, it would be more about consistency in that case if a GetLastPoint method was added, but I can create those two myself as an extension method when needed :)

    As an asset developer I'm forced to use the first cycle version (2019.3.0) for uploading, due to supporting all versions above that. Personally, it's not about the stability of the editor itself, but because of the various Package Manager products, with most of them in preview - or not working well in combination with other packages. Basically since the Package Manager was released, we've had a guideline to avoid all packages shipping with it (except UI). That's why I'm still developing with one of the last versions without it (2017 LTS) or soon 2018 LTS.

    Aha, never knew that, sounds reasonable

    Thanks for all the help with this, now I need to create the paths for my entire island and also create a cleaner UI for this system :P


Log in to reply