Search Unity

[RELEASED] Racetrack Builder

Discussion in 'Assets and Asset Store' started by TomMulgrew, Mar 14, 2019.

  1. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @ToothyBirds I've figured out a process to remove unreferenced meshes. It's a bit cumbersome though.

    Basically:
    1. Remove the "Saved Meshes" script object reference from the Mesh Manager in each scene
    2. Make sure you don't have a scene containing a racetrack open (create a new empty scene if necessary). I'm not sure why this is necessary, but it appears to be.
    3. Select all your racetrack scenes in your project folder, then right click -> "Select Dependencies".
      This will give you everything referenced by your scenes. The racetrack meshes have numeric names and should be grouped together.
    4. From that list, select all the racetrack meshes, then right click -> "Export package" and save it somewhere.
    5. Go to your "Meshes/Racetrack Builder" folder, where all your meshes are, and delete them all.
    6. Import the package again.
    You should be left with just the meshes that are used in your scenes.
    You can also select the Racetrack Meshes scriptable object and click "Remove deleted meshes" to tidy up the tracking information.
     
    StevenPicard likes this.
  2. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Does anyone know of a good Car Controller asset I can recommend for driving on tracks?

    I've just tidied up the documentation and removed references to the Unity Standard Assets package - which has been deprecated for a while now. I would be nice to have something to recommend in it's place.
     
  3. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Multi-selecting curves is starting to come together. This makes it much less tedious to set properties on sections of the track, like widening or the "raise terrain" flag.

    This will likely be released in the next update.
    upload_2022-6-24_11-22-28.png
     
  4. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    I've created some tutorials in an new simpler format. Each is less than 5 minutes and demonstrates a core technique.

    These will become the main videos on the store page (in the next release), as they are a bit more accessible than listening to me talk for 30+ minutes (and some of the older videos are a bit out of date).




     
    lukasynthetic and iDerp69 like this.
  5. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Apparently I can't have more than 5 videos in one post.
    Here's the other 4.




     
    lukasynthetic and iDerp69 like this.
  6. rickysays

    rickysays

    Joined:
    Aug 17, 2022
    Posts:
    1
    This might be a silly question, but would the generated track game run on an Oculus Quest 2?
     
  7. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @rickysays
    Yes, it should. It was originally implemented for an Oculus Go game (I wanted to make something like Stunt Car Racer in VR), and performance was okay. So I wouldn't expect issues on Quest 2.
    Basically it copies regular Unity meshes and warps their shape around the racetrack spline. The output is regular Unity meshes, so there's nothing particularly complex at runtime.
     
  8. juhbee

    juhbee

    Joined:
    Feb 17, 2016
    Posts:
    4
    Hi Tom, I am interested in your asset for an oval racing title. Is there an option to only have a wall on one side of the track? Everything I have seen shows either no walls or both sides.

    I would like to be able to create an oval track, with a wall on the right side only, that connects into an apron or infield surface area. Is this a possibility?
     
  9. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Hi,

    Create a single wall: yes. Connect to an infield area: not really.

    I had a quick play and came up with this:
    upload_2022-10-22_12-2-47.png

    It's possible to put a wall on one side of the track, but you'd need to create your own track type (aka "mesh template"), which requires a little bit of work. There's a walkthrough in the documentation https://drive.google.com/file/d/1EWZw7EGM01IIfQSoDN7MLY3-YBLev_P-/view (page 34), and a tutorial video Racetrack Builder - Track types - YouTube which is a little out of date (the buttons to apply changes to the track have changed) but the main concepts are still relevant.

    You would probably need to supply your own meshes and textures to get a decent looking result, and use the UV generator component to fix the texture seams.

    Regarding the apron, there's not really any support for this. The road surface only has a single bank angle - it's not possible to leave the inside flat and bank the outside. You could use a separate mesh for the inside area (not generated by Racetrack Builder), but there would be a texture seam at minimum, as there will be no real relationship or connection between the racetrack and inner meshes.

    So it depends on how precise your requirements are.
     
  10. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @juhbee

    Having said that, I did manage to come up with this:
    upload_2022-10-22_12-39-17.png

    This is actually two "racetrack" objects, one for the flat inner part, and the other for the banked outer part.
    The outer racetrack has a track type where everything is to the right of the spline. The inner has a track type with everything to the left of the spline.

    I basically cloned the outside track to get the inside one, then removed the bank angles. Obviously you would need to be careful to keep them identical and aligned from that point.

    I configured the UV generator to use world X and world Z to generate the texture coordinates for the top of the track, which eliminates the texture seam.

    EDIT:

    Turns out I had misconfigured the UV generation and it wasn't actually activating.
    Also UV generation aligned to the track direction does actually work without creating a texture seam (if everything is configured carefully)

    upload_2022-10-22_12-58-15.png
     
    Last edited: Oct 22, 2022
    derkoi likes this.
  11. Tu-Le

    Tu-Le

    Joined:
    Jun 5, 2015
    Posts:
    14
    your package is awesome! i just bought it 2 weeks ago, I wonder if have any way to reduce the mesh collider on surface?
     
  12. juhbee

    juhbee

    Joined:
    Feb 17, 2016
    Posts:
    4
    Tom,

    That is actually perfect for my use case - and is a thousand times easier than building in 3D modeling software. I will definitely be using your tool here in my current project. Did you notice any weird physic impacts with the car controller from the two track meshes sharing that seam?

    Thank you so much for your help!
     
  13. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @Tu-Le

    Do you mean make the collision mesh lower resolution?
    upload_2022-10-25_19-53-40.png

    You can do this by modifying the track type prefabs (or taking a copy and modifying that).

    For example:
    • Drag the "asphalt poles" prefab into the scene, and rename it
    • Select the asphalt mesh
      upload_2022-10-25_19-56-26.png
    • Change "Mesh Collider" component's mesh to something lower resolution, like using the same mesh as the visual mesh (which is lower resolution).
      upload_2022-10-25_19-58-5.png
    Now you have a new track type you can drag into the "Template" slot of a curve to generate the track with a lower resolution collision mesh.
     
    Tu-Le likes this.
  14. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @juhbee
    I tested it with the Unity Standard Assets (deprecated, but still a solid car controller imo).

    Sharing the seam is not so much an issue with the car controller. I've seen it cause problems with sphere colliders, as the object will often bounce up when it crosses the seam, but wheel colliders seem a bit more tolerant.

    However, the sharp angle at the seam causes problems - it's easy to lose control of the vehicle. Ideally it needs to be smoothed out, but there's no way to do that with racetrack builder that I can think of.

    upload_2022-10-25_20-16-4.png
     
  15. Tu-Le

    Tu-Le

    Joined:
    Jun 5, 2015
    Posts:
    14
    thanks!
     
  16. nghiah

    nghiah

    Joined:
    Oct 27, 2022
    Posts:
    1
    Hi Tom,

    This might sound a bit silly but are the units of measurement used in your package as shown in the videos (such as for the length of the curve) based on real life units (metres, miles, radian etc.) ? Is there a documentation or list of units of measurements?

    Thank you for your awesome work!
     
  17. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
  18. derkoi

    derkoi

    Joined:
    Jul 3, 2012
    Posts:
    2,260
    @TomMulgrew

    I'm using unity 2021.3.20 & when I rebuild my track, I get a lot of errors spamming my console.

     
  19. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @derkoi

    It looks like it's this line in Assets/Racetrack Builder/Scripts/RacetrackBuilder.cs (line 912)
    Code (CSharp):
    1. Debug.Assert(isCollisionMesh || vertexInfo.Vertices.Length == mesh.tangents.Length);
    I believe this assert can safely be removed. I suspect it's from an older version that recalculated tangents explicitly, whereas the current version just calls the Unity built-in Mesh.RecalculateTangents() function.

    If you remove or comment out that line, does it appear to work?
     
  20. derkoi

    derkoi

    Joined:
    Jul 3, 2012
    Posts:
    2,260
    Hi, Thanks for the quick reply, yes commenting out line 912 stopped the errors.
     
  21. derkoi

    derkoi

    Joined:
    Jul 3, 2012
    Posts:
    2,260
    Hi again @TomMulgrew

    For some reason I'm getting another error message when I try to move the first track section created on a new track

     
  22. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Hi derkoi. Does this error stop it from working?
     
  23. faUnity

    faUnity

    Joined:
    Aug 7, 2023
    Posts:
    15
    HiTomMulgrew,

    First i realy enjoy your asset :)

    My problem is about Layers. I try to make a hover vehicle track (hover physics is very sensible to

    the Delta angle between curves. This could need hundreds of curves to have a smooth run so i’m not sure about performances issues...)

    So mainly i need 2 Layers. Let’s say Floor to hover above and Wall to prevent from climbing walls,

    to manage collisions , FX etc.

    So i attached a script to RaceTrack object that acceeds to the object collider and assign Wall as Layer. Same for the floor layer. This works pretty good. No problem.

    Sorry it’s a bit long, so i come to the main issue :

    I use a follow camera and i want to avoid the ship to be hidden by the barrieres. So i added some statements to my camera script to acceed to the railing objects and disable the renderers.

    This also works pretty good. the barrieres are hidden BUT not the small_pole(Clone) Spacing group 1 objects. I can acceed to them and disable the renderers BUT not specifically. They are hidden on the right and the left… But i just want to hide the rendrers i’m colliding with. So my question is :

    Is there a way to turn the small_pole(Clone) Spacing group 1 as child of continuous(Clone) Continuous (respectively right/left like collider objects)?

    Hope i’ve been enough clear.

    Thanks by advance for your answer.
     
  24. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Hi @faUnity.

    I think I understand what you're saying.
    You want the repeating poles to be associated with the collider on the corresponding side, so that you can hide them all in one go(?).
    upload_2023-10-2_14-12-29.png

    One workaround is to edit the track type (RacetrackMeshTemplate) prefab and move the pole mesh out of the spacing group into the continuous group.
    upload_2023-10-2_14-25-22.png

    The generated template copy objects then come out like this:
    upload_2023-10-2_14-27-27.png

    Which might be closer to what you're looking for?

    However, this does have some consequences:
    1. The pole won't automatically repeat. If you want more than one pole per mesh template copy, you will need to duplicate the mesh (like I've done here with small_pole (1), (2), ...).
    2. The pole is now treated like part of the continuous mesh. So instead of repeating the same pole mesh at different positions, it copies the mesh for each pole and warps the vertices, using the same algorithm it uses for the road surface, barriers etc. This could affect scene size and Unity draw call batching (although my guess is it probably doesn't make much difference as the pole meshes are quite basic).
    3. The pole cannot have its own collider, unless the collider is a mesh collider. A capsule collider (for example) would have simply been dropped. In this case the small poles don't have their own colliders, so this doesn't apply.

    Let me know if this helps. Otherwise, I can think about ways to add some more control to the hierarchy of the objects that Racetrack Builder generates.
     
    faUnity likes this.
  25. faUnity

    faUnity

    Joined:
    Aug 7, 2023
    Posts:
    15
    Hi Tom,
    first of all thank you so much for you availability :)
    Yes absolutely. That's the hierarchy and the result i would.
    So i modified the prefab like you said
    Modified.PNG

    It works but it keeps creating small poles objects out of continuous :(
    Capture.PNG
    I maybe did something wrong :oops: i'm still not used to Racetrack Builder...

    Thanks for your time.
     
  26. faUnity

    faUnity

    Joined:
    Aug 7, 2023
    Posts:
    15
    So i made it work BUT ther is a BUT :eek:
    Capture2.PNG
    As you can see it works for the small poles but the left & right Big poles (pillards of the track) are not generated.
    And i know why...
    So this is what i did :

    The prefab:
    1. i removed the spaced object.
    2. i moved the small_pole object into continuous(child of)
    3. i added RacetrackSpacingGroup.cs as component to small_pole object.
    This makes errors at the lines below of RacetrackBuilder.cs.
    Just to make a try, i disabled those lines in the script.

    So it works but this affect the big poles generation (the pic) and i don't (yet) know how it should work with the other templates prefab.
    Other thing : the five small_pole objects are located at the same position, as one...
    Code (CSharp):
    1.                 var subtreeCopy = GameObject.Instantiate(subtree);
    2.                 subtreeCopy.transform.parent = templateCopy.transform;
    3.                 subtreeCopy.gameObject.isStatic = templateCopy.isStatic;
    4.                 subtreeCopy.name += " Spacing group " + spacingGroup.Index;
    5.                 var spacedCopy = subtreeCopy.gameObject.AddComponent<RacetrackSpacedCopy>();     // Tag with RacetrackSpacedCopy component
    6.                 spacedCopy.SpacingGroupIndex = spacingGroup.Index;
    7.  
    Code (CSharp):
    1.                 subtreeCopy.transform.localPosition = newTemplateFromSubtree.MultiplyPoint(Vector3.zero);
    2.                 subtreeCopy.transform.localRotation = newTemplateFromSubtree.rotation;
    3.                 subtreeCopy.transform.localScale = newTemplateFromSubtree.lossyScale;
    4.  
    ;)
     
    Last edited: Oct 3, 2023
  27. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @faUnity make sure not to put an object with a "Racetrack Spacing Group" component in the subtree of an object with a "Racetrack Continuous" component (or vice versa).
    "Racetrack Continuous" tells Racetrack Builder that all the meshes in the subtree are continous, and should be warped along the curve.
    "Racetrack Spacing Group" tells Racetrack Builder that there are objects in the subtree (marked with "Racetrack Spaced" components) that should be repeated at regular intervals.

    The two mechanisms are mutually exclusive, it is undefined behaviour to have an object that is both "continuous" and in a "spacing group".

    upload_2023-10-3_19-34-20.png
    I've attached a modified "asphalt poles barriers" track type prefab, that might be helpful. Modified as follows:
    • Moved small_pole out of "spaced" object (the one inside the "rail barrier" object, not the top level one) into "continuous" object.
    • Deleted "spaced" object
    • Removed "Racetrack Spaced" component from small_pole
    • Duplicated small_pole a few times at different Z offsets.
    • Repeated the process for the opposite side
    I've left the top level "spaced" object alone. This generates the large poles underneath the track.

    This results in the following hierarchy when the racetrack is built:
    upload_2023-10-3_19-38-2.png

    Is that the result you're trying to get?
     

    Attached Files:

    faUnity likes this.
  28. faUnity

    faUnity

    Joined:
    Aug 7, 2023
    Posts:
    15
    Hi Tom,
    Yes it's perfect! :)
    Thanks a lot for your explanations and the prefab.

    Greetings.
     
  29. pako

    pako

    Joined:
    Nov 21, 2012
    Posts:
    111
    Hi Tom,

    I'm trying to get some info from the racetrack but I'm not quite clear how to do that.

    The tracks I'm creating are not closed-loop. I set a GameObject for the finish line, and I need to know the length from a specific point to the finish line, at run time. This way I can get the total length of the race, which will typically be less than the total length of the racetrack (the start line is after the start of the racetrack, and the finish line is before the end of the racetrack). During runtime, I could also get the distance of each car from the finish line, which would give me the order of cars in the race.

    I read all the documentation, including the example code, and had a look at the code, but it's still not clear how I can achieve the above. I was hoping that I could get the necessary info from the RacetrackCarTracker component, but even if it's possible, I don't find it straightforward.

    EDIT:
    I'm trying to find a solution to this by trial and error. I use a racetrack which has 12 straight curves, of length 250m each. I then place a GameObject with a RacetrackCarTracker component, and a custom "RacetrackMarker" component, just above the 6th curve. When I call racetrackCarTracker.currentCurve from inside the Awake() method of "RacetrackMarker", I get the result of 0, whereas I was expecting to get a 5 (the index in the RacetrackCurves List).

    EDIT 2: I also tried calling racetrackCarTracker.currentCurve from inside the Start() and even Update(), and I still get the result of 0.

    This asset would really benefit from API documentation.

    I'd really appreciate your help!
     
    Last edited: Nov 22, 2023
  30. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Hi.

    The logic to calculate the current curve is in RacetrackCarTracker.FixedUpdate(), so calling that explicitly in your startup code might help.
    However, the component is designed to track an object as it moves along the track, based on knowing which curve it was on initially, and by default it won't detect when the object has moved 5 corners ahead from the initial value of "Current Curve". It might if you increased the "Curve Search Ahead" parameter, but that may have performance implications, and it's not really using it for its intended purpose.

    A better approach might be to attach the "Racetrack Relative" component to your start and finish lines. This allows you to specify their position relative to the racetrack. In particular the Z position is the distance along the racetrack - so you can adjust it until they appear where you want them to be, and then that gives you the distances. (It also has the advantage that the start and finish lines will be repositioned automatically if you modify the racetrack.)

    After that your remaining problem is calculating how far the cars are along the track. In this case the RacetrackCarTracker is part of the solution, as you can tell it which curve the vehicles are on initially at the start of the race. To get more precise information there's a method on the old RacetrackProgressTracker component called GetCarState(), which returns the object's position relative to the racetrack, including the distance along the track.

    Code (CSharp):
    1.     public struct CarState
    2.     {
    3.         /// <summary>
    4.         /// Whether the state is valid.
    5.         /// Can be invalid if the car is not currently above the racetrack.
    6.         /// Other properties should be ignored when IsValid is false.
    7.         /// </summary>
    8.         public bool IsValid { get; set; }
    9.  
    10.         /// <summary>
    11.         /// Segment the car is on
    12.         /// </summary>
    13.         public RacetrackSegment Segment { get; set; }
    14.  
    15.         /// <summary>
    16.         /// Index of segment the car is on
    17.         /// </summary>
    18.         public int SegmentIndex { get; set; }
    19.  
    20.         /// <summary>
    21.         /// Car position relative to segment
    22.         /// </summary>
    23.         public Vector3 Position { get; set; }
    24.  
    25.         /// <summary>
    26.         /// Car direction relative to segment (Z axis = forward)
    27.         /// </summary>
    28.         public Vector3 Direction { get; set; }
    29.  
    30.         /// <summary>
    31.         /// Car velocity relative to segment (Z axis = forward)
    32.         /// </summary>
    33.         public Vector3 Velocity { get; set; }
    34.  
    35.         /// <summary>
    36.         /// Car angle relative to segment (0 = forward)
    37.         /// </summary>
    38.         public float Angle { get; set; }
    39.        
    40.         /// <summary>
    41.         /// Segment space to track space transform
    42.         /// </summary>
    43.         public Matrix4x4 TrackFromSeg { get; set; }
    44.  
    45.         /// <summary>
    46.         /// Track space to segment space transform
    47.         /// </summary>
    48.         public Matrix4x4 SegFromTrack { get; set; }
    49.  
    50.         /// <summary>
    51.         /// Car distance down track
    52.         /// </summary>
    53.         public float TrackDistance { get; set; }
    54.     }
    55.  
    This should really have been moved to RacetrackCarTracker when I implemented it (as it's intended to replace RacetrackProgressTracker which has issues with junctions), but I completely forgot it existed at the time.

    I will try to fix this up in the next few days and release a fixed version.
    Or if you want to give it a go it should basically be a matter of moving the method over, with some hopefully minor fixups.

    Let me know if this helps.
     
  31. pako

    pako

    Joined:
    Nov 21, 2012
    Posts:
    111
    @TomMulgrew thank you for your response.

    Yes, it worked!

    I managed to set the start/finish using the "Racetrack Relative" component.

    I'm not sure when to use the "Update Position" button on the "Racetrack Relative" component. It seems that it doesn't do anything.

    Also, if I modify the racetrack, I can't get the start and finish to be repositioned automatically. It would be great if I could get that to work, in case I need it at some point.

    I also modified the RacetrackCarTracker to include the GetCarState() functionality, and I can get the car positions along the track. Excellent!

    Thanks again for helping me out!
     
  32. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    @pako great to hear.

    Yeah, in theory the "Update Position" button should never be needed - it should always set the object's position automatically.
    I left it in just in case.
     
  33. lukasynthetic

    lukasynthetic

    Joined:
    May 20, 2020
    Posts:
    30
    Hello. Really enjoying your asset! Although I could use some guidance on loop da loop. I was wondering if there are any recommendations/ rules how to create one? I'm trying different angles and turns to make it smooth. Adding two 180° messes the spline (somehow it goes up instead of down - I had to add straight curve in between). Testing reveals 'juttering' on some runs.
    Thank you.
     
  34. TomMulgrew

    TomMulgrew

    Joined:
    Dec 4, 2018
    Posts:
    41
    Hi @lukasynthetic.

    Loops work best if you use 4 pieces, each with a 90 degree gradient turn:
    E.g.

    Turn=10, Gradient=-90
    Turn=10, Gradient=-180
    Turn=-10, Gradient=90
    Turn=-10, Gradient=0

    upload_2024-1-28_0-20-53.png

    If you try to create a 180-degree vertical turn, it may choose to go up instead of down (or vice versa), because each way is equally valid - it doesn't know your intent, only the initial and final gradients. Making smaller vertical turns (e.g. 90 degrees) takes away the ambiguity.

    When you say it's "juttering", are you talking about visually, or do you get camera shake when you drive on it, or something else?
    It may be the visual or collision mesh of the track type needs more polygons, perhaps?
     
  35. lukasynthetic

    lukasynthetic

    Joined:
    May 20, 2020
    Posts:
    30
    Hello,
    Silly me, I looked at example scenes and boom there it is.
    I recreated your setup and definitely makes sense and is more manageable. I used 15°, but then had to tweak slightly as the exit was crossing the entrance.
    I'll try making denser collider for jittering. Yes, it's the whole car bumping.

    Not sure if related to the loop, but I found the car is being reset facing back. I need to test if this happens also before the loop.
    I'll be back shortly :)