Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Official Unity Splines

Discussion in 'World Building' started by gabrielw_unity, Oct 5, 2022.

  1. sand_lantern

    sand_lantern

    Joined:
    Sep 15, 2017
    Posts:
    210
    That was the final piece that I was just having a hard time understanding. Since when you don't feed in a rotation, you need the forward/backward directions to be set towards the points, I was trying to do that too with rotation set, which was causing it to be rotated twice. Thank you for taking the time to get me where I needed to go.

    If anyone is curious, here is the sweet spot I found for aligning knots to look close to what I want.
    upload_2023-9-29_15-53-34.png
    Code (CSharp):
    1.         public static void UpdateLinePath (in float smoothFactor, Transform transform, Spline spline, ITentailMeshBone[] bones)
    2.         {
    3.             bool init = false;
    4.             if (spline.Count != bones.Length)
    5.             {
    6.                 spline.Clear ();
    7.                 init = true;
    8.             }
    9.             float scale = smoothFactor, prevMag = 0f, nextMag;
    10.             float3 center = transform.position, nextDir = 0f, prevDir, current, next;
    11.             Quaternion rot1, rot2;
    12.             for (int iB = 0; iB < bones.Length; iB++)
    13.             {
    14.                 prevDir = nextDir;
    15.  
    16.                 BezierKnot curveKnot;
    17.                 current = bonePosition (bones[iB], center);
    18.  
    19.                 if (iB < bones.Length - 1f)
    20.                 {//Other points
    21.                     next = bonePosition (bones[iB + 1], center);
    22.                     nextMag = length (current - next);
    23.                 } else
    24.                 {//Last point
    25.                     next = bonePosition (bones[^1], center);
    26.                     nextMag = prevMag;
    27.                 }
    28.  
    29.                 if (iB == 0)
    30.                 {//First point
    31.                     prevMag = nextMag;
    32.                     nextDir = normalize (bones[iB].Forward);
    33.                     prevDir = nextDir;
    34.                 } else if (iB < bones.Length - 1f)
    35.                 {//Other points
    36.                     nextDir = normalize (next - current);
    37.                 } else
    38.                 {//Last point
    39.                     nextDir = normalize (current - bonePosition (bones[^2], center));
    40.                 }
    41.  
    42.                 rot1 = Quaternion.LookRotation (nextDir);
    43.                 rot2 = Quaternion.LookRotation (prevDir);
    44.  
    45.                 if (prevMag > nextMag)
    46.                 {
    47.                     prevMag = Mathf.Clamp (Mathf.Lerp (prevMag, nextMag, .5f), nextMag, nextMag * 10f);
    48.                 } else if (nextMag > prevMag)
    49.                 {
    50.                     nextMag = Mathf.Clamp (Mathf.Lerp (nextMag, prevMag, .5f), prevMag, prevMag * 10f);
    51.                 }
    52.  
    53.                 curveKnot = new BezierKnot (current, prevMag * scale * -Vector3.forward, nextMag * scale * Vector3.forward, Quaternion.Slerp (rot1, rot2, (nextMag * .5f) / (nextMag + prevMag)));
    54.  
    55.                 //Debug.DrawRay (curveKnot.Position, mul (curveKnot.Rotation, curveKnot.TangentIn), Color.yellow, 0f, false);
    56.                 //Debug.DrawRay (curveKnot.Position, mul (curveKnot.Rotation, curveKnot.TangentOut), Color.red, 0f, false);
    57.  
    58.                 if (init)
    59.                 {
    60.                     spline.Add (curveKnot, TangentMode.Continuous);
    61.                 } else
    62.                 {
    63.                     spline.SetKnot (iB, curveKnot);
    64.                 }
    65.  
    66.                 prevMag = nextMag;
    67.             }
    68.             spline.SetTangentMode (TangentMode.Continuous);
    69.         }
    70.  
    71.       private static float3 bonePosition (in ITentailMeshBone bone, in float3 center)
    72.       {
    73.          if (bone != null)
    74.          {
    75.             return (float3) bone.Position - center;
    76.          } else
    77.          {
    78.             return 0f - center;
    79.          }
    80.       }
    81.  
    82.     public interface ITentailMeshBone
    83.     {
    84.         public float Radius { get; }
    85.         public Quaternion Direction { get; }
    86.         Vector3 Forward { get; }
    87.         Vector3 Up { get; }
    88.         Vector3 Position { get; }
    89.     }
     
    ThomasLopez likes this.
  2. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    405
    I was thinking similar to just adding a polygonCollider to a sprite shape in 2D: https://docs.unity3d.com/Packages/com.unity.2d.spriteshape@5.0/manual/SSCollision.html

    But i realize there are more parameters. Maybe add a SplineCollider type? Or did someone already code that? Now i'm thinking of using Microverse roads for this which already supports the collisions. But i need to try it out more to fully understand it, i think the collision is in the mesh/prefabs that it uses to build the roads.

    Also, is there a way to select a spline by just clicking on the mesh that the spline is related to? Or some kind of always visible handles. Cause with a scene with 40 different roads, it's quite hard to find the correct gameobject to edit the spline. Road Architect asset does it like this. Would that be sometime you would add?
     
    Lars-Steenhoff likes this.
  3. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,111
    That shine-through of the spline lines is super annoying and very irritating. Example:

    All the splines are behind that cube.

    pl.png
    Especially when you work eg inside a tunnel you don't want to see anything of the outside, definitely not any spline data.

    Please fix, disabling gizmos is not an option :)

    Or as proper example while editing roads:

    be.jpg
    Really super annoying.
     
    Last edited: Oct 2, 2023
  4. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,457

    You can actually click on splines to select them, but it's virtually impossible to do in actual real-world use cases instead of testing scenes. You basically have a 1 pixel wide line for the spline, so if your on a retna monitor, forget it - you'll select everything except the spline. I get a request about once a day to fix this with roads, so I've tried all kinds of "redrawing the spline or handles myself", but they all get slow and have problems, and once in spline mode you can't easily get out of it.

    Yeah, again, doesn't scale well to real scenes, much like gizmos in general.

    For MicroVerse's gizmos I run an occlusion cull in a burst job to cull them all. However, there are numerous places where I have to turn the physics representation of objects off because if they are on they will take up 100's of ms of update time to integrate back into the world, and then turn them on when saving. So a simple physics.raycast breaks there.

    Ideally, what you want is a system of gizmos/spline knots/lines which can:
    - Optionally be visually occluded
    - Optionally be faded out by distance from their bounds
    - Optionally be selected from selecting a sub-object (which can be hidden, etc).
    - Not require mode switches on a tool bar to get in and out of spline editing mode

    The last two are really important for splines, which often are used to generate a visual representation in the scene. Users want to select the road, not a single pixel wide bezier curve in the scene. In some cases drawing the actual spline is a huge distranction, as it only interfiers with the data your trying to see. Just being able to draw the knots and use the generated data would be preferable.

    As worlds go larger, not only does this become a visual issue, it becomes a performance one as well. Drawing lots of splines can be slow, and I've noticed they often jitter their positions heavily while the camera is moving, which is very distracting.

    Also, bug of the day, IsAvailable() never seems to get called on an EditorTool, making contextual editors impossible.
     
  5. ZanthousDevelopment

    ZanthousDevelopment

    Joined:
    Jul 12, 2023
    Posts:
    12
    I used the spline tool to originally draw a couple simple splines, and it worked fine, however now I can't seem to draw any more unless I'm in a non-2d view, the handle for creating a point doesn't appear unless in 3d perspective. I just need extremely basic splines in 2d for my game, am I missing something? Also the editor performance is very poor for this usecase even with just over 100 knots, linear, non-rotated. Should I be using something else?
     
  6. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,111
    Is there an ETA for the fix?
     
  7. TMC_FumiyaMatsune

    TMC_FumiyaMatsune

    Joined:
    Aug 30, 2023
    Posts:
    11
    Sorry if it's already in the thread.
    When I'm running an object on a spline, is there a way to get where position (transform, rotation, between knots) of the spline where it's running?
     
  8. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    @jbooth, I just tried it, this is called properly from my tests, though you have to reference properly the tool to the system by using the EditorTool attribute. Try this core for instance, that should work and only be available when selection contains at least 2 objects:

    Code (CSharp):
    1. [EditorTool("My Tool", typeof(Transform))]
    2. class MyTool : EditorTool
    3. {
    4.     public override bool IsAvailable() => Selection.count > 1;
    5. }
    6.  
     
  9. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    No ETA for now, but should be before the end of the month.
     
    Rowlan likes this.
  10. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey @TMC_FumiyaMatsune, I would need a little more context on how you are making that object move along the spline, but if you are moving the object, it means that you already have this info somewhere otherwise it won't be moving :) Are you using the SplineAnimate component or something else ?
     
  11. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hi @nicmarxp :)
    So it's not really possible to add a SplineCollider type, as a spline is just a line and doesn't have a representation physics-wise... The elements associated to a spline (a generated mesh for instance, of prefab instances along it...) are generally the physical elements that have a rigid body and a collider, so basically a MeshCollider should be associated to your mesh when you'll need that.
    But this is not a SplineCollider as this will depend on what your geometry (is it a road or a tube for instance).
    Hope that answers your question ?

    Also on the second point, as @jbooth said :
    "You can actually click on splines to select them, but it's virtually impossible to do in actual real-world use cases instead of testing scenes."
    I'll have a look at that when I'll have time to try to improve spline selection but gizmos already make this possible.

     
  12. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hi @Rowlan,

    This is actually done with the Handles when you are in the Splines context but not with the Gizmos as this is actually the purpose of gizmos, to visualize and being able to select elements in the scene, no matter of the occluding geometry and the distance.

    But I see your problem and I'll try to think about that...

    @gabrielw_unity, would you have an idea on this, or a different input than me?

     
  13. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,111
    That's just a small example. It'll escalate pretty quickly. Not sure if some kind of culling is possible. Or maybe fading by distance. But either way the info that's shown in the screenshot is redundant and doesn't contribute to the editing and is rather irritating.
     
  14. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,457
    I did eventually get this to fire, but I think it's maybe not for what I think it's for? What I want to do is make the tool available on my custom class based on some checks. For instance, I now have the "road system" making caves, which allow for defomation along the spline that's not appropriate for roads. So I only want this to show in the tool bar if the system is being used to generate caves. However, this seems to get called when I click on the tool in the toolbar, not to check if the toolbar item should be showed or not.
     
  15. TMC_FumiyaMatsune

    TMC_FumiyaMatsune

    Joined:
    Aug 30, 2023
    Posts:
    11
    Hi @ThomasLopez
    Objects are moved using spline animation. Is there an example of getting the knot that the object went through, or the closest knot?
     
  16. RoyBarina

    RoyBarina

    Joined:
    Jul 3, 2017
    Posts:
    98
    Hello
    Seems like prefabs break when baking them in "Spline Instantiate"
    I also noticed that before baking the prefabs they're excluded from the scene hierarchy.. is that necessary? it prevent me from finding them in runtime (to trigger something in them at start for instance) so I tried baking them and.. well read this comment again lol
    Am I missing something?
     
    ThomasLopez likes this.
  17. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,111
    Check the Splines Samples in Package Manager.
     
  18. WhiteCastle

    WhiteCastle

    Joined:
    Sep 6, 2013
    Posts:
    14
    I am dealing with a problem using offsets to place something simple like fence posts. Using the same spline that I use for the road, any attempt to place the posts offset result in issues with curves. Inside curves result in multiple crossed posts. Based on what an X offset does, this makes perfect sense. In all the examples I see, side objects are placed with their own spline. My desire would be to auto create side splines from the main center spline used for the road. Does anyone know any tricks or algorithms to create an offset spline?
    upload_2023-10-6_13-21-2.png
     
    ThomasLopez likes this.
  19. WhiteCastle

    WhiteCastle

    Joined:
    Sep 6, 2013
    Posts:
    14
    despite of hours Googling, I failed to search for "OFFSET BÉZIER CURVES" directly. There are some promising algorithms out there. Still, any suggestions are welcomed.
     
  20. Alturis2

    Alturis2

    Joined:
    Dec 4, 2013
    Posts:
    38
    I have been experimenting with the new Unity Splines package. And looking through SplineUtility there seem to be many functions that sound like what I want but they don't produce the result I would expect.

    What I am trying to do is take an object determined to be at a given distance along a spline's calculated distance.

    float splineDist = Spline.CalculateLength(transform.localToWorldMatrix);
    float objectDist = <value between 0 and splineDist>;
    float t = objectDist / splineDist;

    Vector3 point = Spline.EvaluatePosition( t );

    However, this produces a result where the objects will move faster near the spline start and end points and slower near the spline middle. Likely varies quite a bit depending on how the spline is defined.

    I have tried various flavors of ConvertIndexUnit, GetPointAtLinearDistance, GetNormalizedInterpolation etc but none of them are providing the result I am looking for. I want objects to move along the spline at a set speed, regardless of the curvature of the spline.
     
  21. TMC_FumiyaMatsune

    TMC_FumiyaMatsune

    Joined:
    Aug 30, 2023
    Posts:
    11
    Thank you for the sample.
    What I was able to confirm in the sample was to find the closest point from multiple splines, which was not what I expected. Is there an example of using a single spline with SplineAnimator to get the array number of the knot closest to a moving object?
     
    Last edited: Oct 9, 2023
  22. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey @TMC_FumiyaMatsune,
    Using the SplineAnimate component, you can access to SplineAnimate.NormalizedTime to get the current value of the animation in [0,1] for the current object.
    Then you can convert that time using SplineUtility.ConvertIndexUnit to convert it to PathIndexUnit.Knot where the int part of the index will be the know index! :D

    Hope this helps!

     
  23. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey @jbooth,

    So the IsAvailable() method determine if a tool is available regarding the current selection and thus is called when the selection changes.

    I don't know how you differentiate between caves and roads, but if it's a different component for instance, only using the attribute [EditorTool("Cave Tool", typeof(CaveComponent))] will make it available when you have a GameObject on this type in your selection and the "targets" of that tool will only be GameObjects with this component.

    If they have a similar Component but a different settings of that component to differentiate Caves from Roads, then you can check in IsAvailable() if at least one element of the targets is a Cave, or for instance that there is only one element in your selection and that one is of Cave type.

    Hope this helps :) Otherwise don't hesitate to detail a little your need and I can maybe help to think of a solution :)


     
  24. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    @Rowlan,
    This is not really possible, this is actually exactly what Gizmos are for... Not being culled and being visible from a distance and beind geometry, so I'm not certain there is a solution to this problem :/

     
    Rowlan likes this.
  25. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey, @RoyBarina!
    Thanks for that, so, the fact that baking the instances is breaking the connection to prefab is a bug indeed!
    I'll log it and fix it asap :)

    The fact that prefabs are not shown in the hierarchy is actually done on purpose. SplineInstantiate regenerate the instances every time the Spline is modified (knots added, tangents changed...) so all the changes you would do on visible game objects would be lost. Plus, as the instances are generated procedurally, we don't want someone else that the Component to handle the instances positions for exemple, or to be able to change the hierarchy of these elements as this would be more complicated for the instanciation system to follow the changes and clean properly the scene when regenerating the system :)

    Hope this gives you a better idea of what's happening :)

     
    RoyBarina likes this.
  26. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey @WhiteCastle,

    I think there is another post before that was trying to do something in the same idea.
    I did some work on that previously but don't have my code anymore :(

    The idea which was working the best IIRC was to offset the main spline by offsetting each of its knots and then I think I was considering the curvature of each knot to scale the tangents of the new knots.
    Basically:
    - 0 Curvature would be flat, keep the same tangents,
    - If the curvature is on the same side of the original spline than the new spline (for instance curvature indicates that the original spline is turning towards the new one), then scale down the tangents as the new spline curve at that position will be stronger (steeper).
    - If the curvature is on the other side of the original spline compared the new spline (for instance curvature indicates that the original spline is heading farther to the new one), then scale up the tangents as the new spline curve will be larger/slower.

    Hope this makes sense to you?

     
  27. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey @Alturis2,

    Did you take a looke at our SplineAnimate component ?
    This is exactly what the component is doing :)

    The thing is that linearly interpolated distance (as you do) will not result in linear bezier distances on the curve.
    I encourage you to watch the really good video Freya did on Bezier splines, you'll see exactly your problem at 15'55" :



     
  28. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,457
    They are the same component, but depending on values in a config file they should show different options in the toolbar. Essentially I have a tool that lets you scale the spline in width and height - this is needed for caves, but doesn't make sense for roads, so the idea is that I check in IsAvailable and the icon for that control either shows up or doesn't. But currently the icon for the tool shows up all the time.

    The only solution I can think of for spline selectability/visibility issues is the following:

    1. Make the spline width more than 1 pixel. On a m2 macbook pro's screen, a pixel is 0.1mm in size, which is basically the width of a human hair. That's some serious mouse precision to require with a finger pad.
    2. Allow the system using the spline to define it's rules for selection. For instance, I'm spawning (hidden) meshes along the spline, so I want the user to be able to select them and have that select the spline. This would make selection work regardless of #1. (Note that there is one issue here - the spawning of the physics objects is too slow to do in real time, so these have no physics objects until the user saves the scene)
    3. Allow the system using the spline to easily turn off or replace the visibility and selection systems. If #2 is achieved, I wouldn't want splines drawn at all until the road or cave is selected, for instance.

    There is no correct combination of gizmos and colors that will always work for splines, because we don't really care about splines, just the content they are used for. For instance, a camera path would likely work fine for the current system (excepting #1). If you make them occlude, then working with caves becomes difficult because you can't see them from outside. If you make them draw all the time and not occlude, then they turn into a jumbled mess across the whole scene. The current colors are black and dark blue, which you can't see in many scenes (dark caves).

    There is literally no possible solution for "Spline drawing and selecting" because the use cases and conditions are too varied, so the only way to make it work for all cases is to provide a default case and allow systems using splines to override and customize that solution.
     
    ThomasLopez likes this.
  29. Ell223

    Ell223

    Joined:
    Jan 27, 2014
    Posts:
    15
    Is there a way to update knot position in code without incurring GC allocations? Current method:

    Code (CSharp):
    1. var pos = anchors[i].transform.position.ToFloat3();
    2. container.Spline[i] = new BezierKnot(rail.transform.InverseTransformPoint(pos));
    SetKnot same effect. And also SetTangentMode?

    I'm trying to match a spline to a moving object essentially, by matching its knots to in-scene transforms.
     
  30. wwg

    wwg

    Joined:
    Apr 2, 2014
    Posts:
    130
    [SOLVED: I was finally able to track this down to a conflict with some old MeshMaker asset files that I had in my project. Completely removed the MeshMaker files and splines works.]

    Hi all. I'm having trouble getting the splines package working in an existing project (Unity 2021.3), but it works fine in a new project (also 2021.3). I've tried deleting the Library folder.

    The error messages would suggest (to me) that the Settings Management package isn't working correctly in my project.

    Does anyone (@ThomasLopez ) have any idea of what might be causing this and/or where I should be looking for solutions?

    Thanks!

    About 12 error messages - here are a few examples:

    Library\PackageCache\com.unity.splines@2.4.0\Editor\Controls\SplineHandleSettings.cs(19,69): error CS1503: Argument 1: cannot convert from 'Settings' to 'UnityEditor.SettingsManagement.Settings'

    Library\PackageCache\com.unity.splines@2.4.0\Editor\Core\PathSettings.cs(15,46): error CS1729: 'Settings' does not contain a constructor that takes 1 arguments

    Library\PackageCache\com.unity.splines@2.4.0\Editor\Core\PathSettings.cs(25,17): error CS1503: Argument 2: cannot convert from 'Settings' to 'UnityEditor.SettingsManagement.Settings'

    Library\PackageCache\com.unity.splines@2.4.0\Editor\Utilities\SplineHandleUtility.cs(48,83): error CS1503: Argument 1: cannot convert from 'Settings' to 'UnityEditor.SettingsManagement.Settings'

    Library\PackageCache\com.unity.splines@2.4.0\Editor\Tools\SplineTool.cs(74,104): error CS1503: Argument 1: cannot convert from 'Settings' to 'UnityEditor.SettingsManagement.Settings'

    Library\PackageCache\com.unity.splines@2.4.0\Editor\Utilities\SplineGizmoUtility.cs(14,87): error CS1503: Argument 1: cannot convert from 'Settings' to 'UnityEditor.SettingsManagement.Settings'
     
    Last edited: Oct 22, 2023
    ThomasLopez likes this.
  31. Hao_Cgh

    Hao_Cgh

    Joined:
    Sep 27, 2021
    Posts:
    5
    How to creat a spline by code , I want to get a smooth curve by add knot by code, is it possible?
     
  32. Recon03

    Recon03

    Joined:
    Aug 5, 2013
    Posts:
    840
    this^^^^
     
    ThomasLopez likes this.
  33. AntonAndev

    AntonAndev

    Joined:
    Aug 13, 2014
    Posts:
    44
    Hello. I use splines to move a train. I discovered a strange problem with getting a position using a progress ratio. I place straight railroad prefabs, get the points (knots) from a each prefab, and create a single straight spline. I calculate the position of a locomotive and 2 cars relative to each other and get the positions on a spline and progress ration for each train's component.

    Code (CSharp):
    1. SplineUtility.GetNearestPoint(locomotiveAI.Path.SplinePath, calculatedPosition, out nearestComponentPositionOnSpline, out progressRatio);
    2. Debug.Log("Nearest point: " + nearestComponentPositionOnSpline + ", progress ratio: " + progressRatio);
    Then I begin to move the spawned train in the FixedUpdate() along a world X-axis. Each game object has kinematic RigidBody with interpolation setting.

    LocomotiveController.cs
    Code (CSharp):
    1.         public void Move()
    2.         {
    3.             if (ProgressRatio <= 1f)
    4.             {
    5.                 ProgressRatio += DistanceSpeed * Time.deltaTime;
    6.                 position = locomotiveAI.Path.SplinePath.EvaluatePosition(ProgressRatio);
    7.                 direction = Vector3.Lerp(direction, locomotiveAI.Path.SplinePath.EvaluateTangent(ProgressRatio), Time.deltaTime * TurnSpeed);
    8.                 rotation = Quaternion.LookRotation(direction);
    9.  
    10.                 rigidBody.MovePosition(position);
    11.                 rigidBody.MoveRotation(rotation);
    12.  
    13.                 Debug.Log("Changing loco position: " + position + " , progress ratio: " + ProgressRatio);
    14.  
    15.                 foreach (var carController in CarControllers)
    16.                 {
    17.                     carController.Move();
    18.                 }
    19.             }
    20.         }
    This is a log of positions at the moment the train is spawned on the spline and then frame by frame:

    Train car 2

    Calculated Position: (8.22, 50.31, 10.00)

    Nearest point: float3(8.22f, 50.33001f, 9.984711f), progress ratio: 0.02428194

    Frame 1: Changing position: (8.22, 50.33, 9.98) , progress ratio: 0.02645585

    Frame 2: Changing position: (8.32, 50.33, 9.98) , progress ratio: 0.02862976

    Frame 3: Changing position: (8.42, 50.33, 9.98) , progress ratio: 0.03080368


    Ratio is changed, but the X position is not changed in the first frame

    Train car 1

    Calculated Position: (14.66, 50.32, 9.98)

    Nearest point: float3(14.66f, 50.33f, 9.984711f), progress ratio: 0.1632737

    Frame 1: Changing position: (14.61, 50.33, 9.98) , progress ratio: 0.1654476

    Frame 2: Changing position: (14.71, 50.33, 9.98) , progress ratio: 0.1676215

    Frame 3: Changing position: (14.81, 50.33, 9.98) , progress ratio: 0.1697954


    Ratio is changed, but the X position is changed in the opposite direction. But then since Frame 2 the position changes in the right direction.

    Train locomotive

    Calculated Position: (22.23, 50.32, 9.98)

    Nearest point: float3(22.225f, 50.33f, 9.984711f), progress ratio: 0.3308123

    Frame 1: Changing loco position: (22.32, 50.33, 9.98) , progress ratio: 0.3329862

    Frame 2: Changing loco position: (22.42, 50.33, 9.98) , progress ratio: 0.3351601

    Frame 3: Changing loco position: (22.52, 50.33, 9.98) , progress ratio: 0.337334

    Changing loco position: (22.62, 50.33, 9.98) , progress ratio: 0.3395079


    Loco moves in the right direction since the first frame.

    Unity 2022.3.10f1, Unity Spline Package 2.4.0

    UPDATE! And solution. I found on 4th page a solution to change parameters, (10, 5) at the end instead of default. Now it works as expected.
    Code (CSharp):
    1. SplineUtility.GetNearestPoint(SplinePath, calculatedPosition, out nearestPositionOnSpline, out progressRatio, 10, 5);
     
    Last edited: Nov 1, 2023
    ThomasLopez likes this.
  34. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hi @Ell223,

    The allocation problem should be solved in Splines 2.5.1 if I'm not mistaken.

    Regarding your solution, I would preferably re-use the previous value of the know to avoid erasing the previous tangents:

    Code (CSharp):
    1.  
    2. var knot = spline[KnotIndex];
    3. knot.Position = newPosition;
    4. spline[KnotIndex] = knot;
    5.  
    You can also use the public struct SelectableKnot/SelectableTangent, this is what we are using in our tools to handle updating the selected elements. That might gives you some utilities (like local vs world space, access to tangents modes....).


     
    Ell223 likes this.
  35. AntonAndev

    AntonAndev

    Joined:
    Aug 13, 2014
    Posts:
    44
  36. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hi @jbooth,

    Answering on the first section of your post here.
    So as you say, these are tool settings and should be display as such :)

    The IsAvailable() will be verified on selection changes. So your tool should be available whenever road or caves are available if I'm correct. Then the settings should be different regarding your selection.

    This can be done by adding these options as Visual Elements in the Tool Settings overlay, next to orientation/pivot options. For instance, if you switch to the Spline context you can see that the "visuals options' drop down had been added here, and the Tool handle rotations options has been overridden to add 'parent' and 'element' modes.
    These are settings that can be added simply to a tool or a context.
    Just define the VisualElements for these options and the callback they need to react to (selection change for instance), you can show/hide them on these callbacks. Adding them in the toolbar is quite simple, just create your visual element and reference it as a toolbar element using the class attribute:
    Code (CSharp):
    1. [EditorToolbarElement("MyToolSettings")]
    Then derfine an editor for your tool and add this element:
    Code (CSharp):
    1. [CustomEditor(typeof(MyTool))]
    2. class MyToolEditor : UnityEditor.Editor, ICreateToolbar
    3. {
    4.     public IEnumerable<string> toolbarElements
    5.     {
    6.         get
    7.         {
    8.                 yield return "MyToolSettings";
    9.         }
    10.     }
    11. }
    Hope this helps!


     
  37. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hey @AntonAndev,

    That depends of the kind of performance you need.
    Using NativeSpline allow to prepare the data by computing and caching the spline data (knots world positions and others) to facilitate and accelerate computation after. Jobs should enhance the performance as well by taking advantage of the Unity Jos system (using multithreaded code). BUT I would recommand that you first check that you really need that as sometimes preparing and retrieving the data from a multithread job can be less efficient if the amount of work done there doesn't really worth it :)

     
  38. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    @Hao_Cgh,

    This is totally feasible and that's why the package is there (and also added some tools and utility to manipulate these).
    Please check the doc for more info:
    https://docs.unity3d.com/Packages/com.unity.splines@2.5/manual/index.html

    You might want to create an empty SplineContainer and then add splines and knots in there.
    Also, get the samples coming along with the package, that'll give you extra examples and code to start with.

     
  39. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    @Recon03, @TJHeuvel-net , you'll be happy to hear that changed in the latest release of the package (2.5.1)!

     
    Recon03 and TJHeuvel-net like this.
  40. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    @AntonAndev,

    Glad you got the solution for that, so yes, depending on your need you might a different resolution to solve a proximity problem between a point and a spline... This is why there is these 2 parameters : The resolution used and the number of iterations.

    Just copying the official doc about these 2 parameters here :

    resolution
    Affects how many segments to split a spline into when calculating the nearest point. Higher values mean smaller and more segments, which increases accuracy at the cost of processing time. The minimum resolution is defined by PickResolutionMin, and the maximum is defined by PickResolutionMax. In most cases, the default resolution is appropriate. Use with iterations to fine tune point accuracy.

    iterations
    The nearest point is calculated by finding the nearest point on the entire length of the spline using resolution to divide into equally spaced line segments. Successive iterations will then subdivide further the nearest segment, producing more accurate results. In most cases, the default value is sufficient, but if extreme accuracy is required this value can be increased to a maximum of PickResolutionMax.



     
  41. AntonAndev

    AntonAndev

    Joined:
    Aug 13, 2014
    Posts:
    44
    In my game I need to move many trains along a splines. Could you please add example to the package how to move a kinematic rigibody on a spline with NativeSpline and Job System?

    For example I have a test environment, there is a spline constructed from many splines from instantited prefabs.
    Code (CSharp):
    1. foreach (var knotPosition in knotsPositions)
    2.             {
    3.                 newSpline.Add(new BezierKnot(knotPosition), TangentMode.AutoSmooth);
    4.             }
    5.  
    6.             var localToWorldMatrix = gameObject.transform.localToWorldMatrix;
    7.             splineSlices.Add(new SplineSlice<Spline>(newSpline, new SplineRange(0, newSpline.Count), localToWorldMatrix));
    8.             SplinePath splinePath = new SplinePath(splineSlices);
    The final spline has the following parameters: curves - 142, knots - 142, m_Length - 587.59, m_SegmentLengthsLookupTable - 4260. I instantiate one object that starts moving along this spline using following code in the FixedUpdate():
    Code (CSharp):
    1.                 ProgressRatio += Speed * Time.deltaTime;
    2.  
    3.                 using (NativeSpline nativeSpline = new NativeSpline(locomotiveAI.Path.SplinePath))
    4.                 {
    5.                     nativeSpline.Evaluate(ProgressRatio, out var pos, out var dir, out var up);
    6.                     rotation = Quaternion.LookRotation(dir);
    7.                     rigidBody.MovePosition(pos);
    8.                     rigidBody.MoveRotation(rotation);
    9.                 }
    With one object on Apple M1 chip I have 60 FPS. With 2 objects - 2.5 FPS (second object has its own copy of spline). With connected Profiler I have 2.5 FPS with just one object on a spline. It looks like a bug. I attached two screenshots of the Profiler. Why so many calls and calculations inside spline library?

    Performance depends on a length of a spline. Is it possible to get 60 FPS with 500 objects on a spline with any length? Currently I have 1.5 FPS with 2-3 objects on a long spline on a terrain.

    Unity 2022.3.12f1, Splines 2.4.0
     

    Attached Files:

    Last edited: Nov 10, 2023
  42. MUGIK

    MUGIK

    Joined:
    Jul 2, 2015
    Posts:
    473
    WHY do splines save Spline.MetaData to disk? All these values will be changed once one of the knots moves a bit.
    Yes, you should save TangentMode, but all other values should not end up on the disk.

    upload_2023-11-5_13-57-1.png

    Here I created a new spline using create game object menu "Spline/Square". Then dragged this GO to Assets folder to create a prefab. Then I moved one knot and here are all these changes that git shows. It's about 130 lines changed just for one knot moved. In our scene we have dozens of splines. Such a waste of space.

    And you don't even save m_UpVectors array from Spline.MetaData, why would you then save m_DistanceToInterpolation...
    I just don't get it.
     
    ThomasLopez likes this.
  43. MUGIK

    MUGIK

    Joined:
    Jul 2, 2015
    Posts:
    473
    I removed [SerializeField] attribute from Spline.MetaData.m_DistanceToInterpolation field and resaved our main game scene.
    Scene file size went from 13mb to 11mb. So in our case, it was 2mb of useless data cluttering the git change tracking.
     
    ThomasLopez likes this.
  44. Ell223

    Ell223

    Joined:
    Jan 27, 2014
    Posts:
    15
    Thank you, but 2.5.1 doesn't seem to solve the allocation. Looking at the profiler, half the allocations seem to happen when Spline calls a spline changed event, and SplineExtrude fires off its listener. But I don't have a spline extrude on the object, so a little confused about that.

    I have spline extrude on OTHER objects (which don't update at all at runtime), but why would they be responding to an event on a completely separate Spline container? If I disable them, the GC allocations from SplineExtrude go away. But again, these are completely separate splines on completely separate objects.

    Rest of the allocations happen in the SplineDataDictionary.

    I'm only updating knot position, not adding or removing, so I wouldn't expect any allocations.
     
  45. MUGIK

    MUGIK

    Joined:
    Jul 2, 2015
    Posts:
    473
    Spline.cs safety check in case one of the subscribers throws exception:

    Code (CSharp):
    1.  
    2. m_QueueAfterSplineModifiedCallback = true;
    3. UnityEditor.EditorApplication.delayCall += () =>
    4. {
    5.     try
    6.     {
    7.         afterSplineWasModified?.Invoke(this);
    8.     }
    9.     finally
    10.     {
    11.         m_QueueAfterSplineModifiedCallback = false;  
    12.     }
    13.  
    14. };
     
    ThomasLopez likes this.
  46. veddycent

    veddycent

    Joined:
    Jul 22, 2013
    Posts:
    108
    Hi all,

    Using the "Spline Instantiate" script does anyone know if it's possible to have a range along a spline where instantiated game objects will be restricted too?

    I would have thought that the positions would have such an option but unfortunately not.

    Thanks!
     
  47. Red_Dragon69

    Red_Dragon69

    Joined:
    Sep 7, 2015
    Posts:
    111
    Hi :)
    if I'm switching to Spline-Context in SceneView and the selected Spline GameObject has no knots added, I'll always get the following errors (same is btw. when having a procedural generated Spline and leaving playmode, resulting in a Spline GameObject without knots added).

    Is there any fix for this?

    br,
    Max

    upload_2023-11-14_14-33-30.png
    upload_2023-11-14_14-33-44.png
    upload_2023-11-14_14-34-19.png
     
  48. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    Hi @AntonAndev,

    So to answer your first question, we are not planning to add any new samples to the current package for now.

    Then regarding your code, I assume your spline is not changing once it's created, so I would recommand that you don't create a new NativeSpline everytime you are in FixedUpdate(). This is moving the local spline in world space and computing a lot of positions along it and at the end you are only using that to sample 1 position, with seems a little overkill! So, I would rather advice to create that Native Spline once and use the same one for all of the evaluation you need later.

    It's really important to understand here the cost you are introducing by creating this NativeSpline and the ration between this creation and the number of evaluations done after that on that Native Spline.

    If you have a second object on the same Spline, use the same NativeSpline, otherwise just get one NativeSpline per object.

    Also, I think we optimized a little allocation in 2.5.0, so don't hesitate to update the package version :D

    Hope that's gonna help :)

     
  49. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    That's indeed a good point!
    We will look at these, thanks for pointing that out indeed!


     
  50. ThomasLopez

    ThomasLopez

    Unity Technologies

    Joined:
    Jun 8, 2020
    Posts:
    159
    @Ell223,

    It's totally normal that SplineExtrude listener are fired when you modify a spline.
    A Spline is not a Unity Object, it can be contained in any MonoBehavior, not only a Spline Container. Spline.Changed event is a static event that is send to every listener as you could want to react to another spline change or a spline that is not in a container depending on your use case.

    The fact that is not on the same object is not really relevant as your SplineExtrude component and your spline can be on different objects, that not important :)

    However, I think it should indeed not allocate anything, we will have a look at this! Thanks!
    Hope the rest of my explanation helps you understand what's happening in the change events :)