Beau! Bézier Curves are so pleasing to behold! Have you considered using these nicely curved paths in an endless racer game? Merging some of your existing projects/concepts from last year such as Twilight City? Your own ideas for videos and projects are great on their own, and I look forward to what's next. Thanks for sharing all your work in Godot game development!
Thanks for the feedback and glad you liked my other vids ^^. I don't think this approach would work for an endless runner necessarily, as there's a cost to moving CSGMeshes realtime which may end up prohibitive. I might revisit some old Surface Tool code which should be more performant. I got the path mesh to work from memory, I was just having issues with UVs, so fixing for that would need to happen first!
Not sure I know what you mean, but you can make a CSGMesh subtractive which will allow you to tunnel through other CSGMeshes. Something that I toyed with but couldn't get materials to play nice procedurally.
@@BeauSeymour Well, I'm looking for a way to tunnel, if there is, to tunnel to new area. Making mesh where there is none. As in open worlds, but instead of a landscape, tunnels to dig.
Wow, brilliant sir. This will take a few watches. I also love your, what I assume, is your default work environment within GODOT which has sparked ideas of my own. Cheers
Would you be willing to make a follow-up tutorial where you expand this spline track system further? Most racing games would require track tilt and track width parameters per spline point as well as perhaps the ability to create forks for optional shortcuts. As far as I am aware, no tutorial maker ever dared to touch on these topics before. Games could hardly be made without them, though.
Well tilt is largely available though finicky using the Path3D handles. One issue with this is the curve handles affect the tilt along the entire spline, so granular per-point controls is not there (yet, there's a github issue where someone talks about adding tilt vector factoring for CSGPolygons, so may be available in future). Per point width is stepping into custom mesh solution rather than a built in node solution, which is even more finnicky. The Riverways plugin I think is close to what you'd want, so could look at that as a basis and build something off the back of it.
@@BeauSeymour Personally, I'd always build the track visuals entirely by hand, so this additional spline data would be used exclusively for non-graphical purposes. AI racers, for instance, would need the width data for track navigation. Cameras would use the tilt vector to remain upright without the need for raycasts. I've taken a look at Riverways in the past, and it's very close to what I'm looking for. A combination of lackluster code documentation and my own inability forced me to give up eventually. I'll surely come back to it some day.
I don't really dabble with 2D, but does the Line2D node not cover most of it? Otherwise you'd probably want to spring your own mesh by iterating each point pair and calculating the perpendicular vector to determine width and assigning UVs accordingly.
great video!! Following the channel. A doubt I have (I asked this question in another video on a similar subject) would you know how to place objects only when there is a curve? I wanted to put signs pointing curves on the track but I don't know what is the best way to detect the curvature of the track and direction to define the side where the pointing signs will be placed.
Great idea and can think of a few ways you could attack the problem, though will need to test them out. First thing that comes to mind would be to iterate the baked points to take a moving average of turn vectors, then defining a threshold but there's probably better ways.
@@BeauSeymour I have a racing game project that I created a track system with this technique about 3 years ago (I ended up pausing the project at the time due to Godot 3.x limitations and I'm resuming work now with Godot 4). I tried some ways to do this, what I can say is that in practice the signposts should start to be placed before the curves so that the player is informed in advance (maybe an offset). At the time I made a raycast walking on the curve with an object ahead of it, then I could detect the curve, but it wasn't very efficient. I'll take a look at what you said, but one thing I realized is that it's necessary to have a curvature control that also detects the length of the curve, because if not in very short and smooth curves it puts the signs that maybe not are necessary.
Bit late to reply, but had a break from this project for a while. The following is how I (now) detect corners to autopopulate curve signs: func get_curvature_3d(point1:Vector3, point2:Vector3, point3:Vector3): # Get the direction vector of the line between point1 and point2 var d1 = point2 - point1 # Get the direction vector of the line between point2 and point3 var d2 = point3 - point2 # Calculate the cross product of the two direction vectors var cross = d1.cross(d2) # Calculate the magnitude of the cross product var magnitude = cross.length() # Calculate the dot product of the two direction vectors var dot = d1.dot(d2) # Calculate the curvature var curvature = magnitude / abs(dot) return curvature You then feed it point1 (which would be some distance behind the current point on the curve), point2 (the currently sampled point on the curve) and point 3 (some distance ahead on the curve). I may cover this more in another video as I've revamped a lot of the code from this one already to make it do more.
This is a nice method, but quick question. What if I wanted to make something more than a simple loop? What if I wanted to build city roads with intersections? Do you know how one would go about doing that using this approach? I could intersect the curve on itself but the textures would be wrong and the side of the road meshes need to be deleted somehow. Or perhaps I would have to put the intersection pieces first and then connect them with individual curves?
I'm not sure what the 'correct' method is, but suspect the only reasonable way to handle intersections is to precreate them as models, then snap the road curve to them at defined entry/exit points. Anything else I can think up just breaks the moment you try to automate texturing it :S Maybe some I'll have to investigate in a later video but caught up with another project ATM ^^
Nice tutorial but I'm getting completely stuck at 03:07 when you create the other kerb. No idea how you've done it, you skip through at lightening speed.
If you pause at 2:58 you will see in the top right that the four points on screen that represent the road shape are 2D coordinates. (0.0,0.0) -> (0.0,0.1) -> (0.1,0.1) -> (0.1,0.0). This is a closed loop, so the last link to (0.0,0.0) is implied. The Y component defines the height of the road mesh, the X component the width of the road. I just copy this csgmesh that represents the road and tweak at 3:02 to make the 'curb' segments for the left-hand side. All I am doing is making the points start at (0.0,0.0) but then move left of it (negative on the X axis) and slightly higher than the road on the Y axis. The right-hand side is the same again, but I am moving the points further right of 0.1 on the X axis, and the same amount higher on the Y axis as before. Hopefully that helps but let me know how you go!
You would need to build the GUI interface for the user to interact with, which then interfaces with the Path3D node under the hood. This is by definition unique to your implementation, but any GUI tutorials will help get you started.
Where it says 'top orthogonal' in the top left of the window is a button with several drop down options for different views. Alternatively clicking the 'Y' of the gizmo in the top right does the same.
Question, Does this tool have the ability to organically add split paths and gaps into the track for more dynamic design, and does this tool also allow you to widen or thin certain parts of the track. If there is where can I learn about doing that?
None of the above, and all of that would require using a custom mesh implementation (though you could still sample a curve to define the track). Godot's Surface Tool is the easiest approach. Making track wider/thinner situationally is fairly easy, I did it in a later clip just by having a path for the inner and outer edge of the track, but ultimately there comes a point where it's easier to just custom model the solution. I highly doubt any aspect of Mario Kart was procedural for example. If it had to be procedural I would probably prefab a bunch of track splitting models and have the path end snap to the location needed for a seamless transition, then two new paths start on the other side. It would likely be quite finicky/bespoke though so don't expect a general tool to exist. The alternative I can only imagine would have some horrible vertex proximity lookups with some kinda smoothing and the UV situation would be diabolical.
Man, I was hoping I could utilize this in 3.5 but for whatever reason it will NOT accept "$Spawn". It even auto-completes, but I just get "Can't use get_node with absolute paths outside of active scene tree". Bummer! I tried onready var spawn = $Spawn. Nothing. Thanks for sharing!
I have problems with big scale. At 5:18 you suggested to bake meshes to be static. Could you please elaborate a bit more or give a link to tutorial or something what do you mean exactly?
For Godot 3.x there was a at least one plugin to export/bake csg meshes to a static .obj file. I've not used them as never hit performance issues, but for example you could look at the code for this and convert to Godot 4.0 here: godotengine.org/asset-library/asset/563
Two questions: How bad are the performance issues, (And where are they)? How does this interact with collisions? (Could this be used to generate collissions as well?)
Re. Performance, I've only really noticed it when editing and it's minor fps dips. The moment you let go of the mouse it's back to normal, though I can't say how big a impact there would be at scale or when combining with other Boolean objects. I think ultimately it's fine for static tracks, but would likely fall over if you wanted real-time track updates during gameplay (which tbh would have it's own quirks to deal with anyway re. Collisions). Re. Collisions in general, already there as CSGMeshes have the option by default.
Just iterate the defined path and raycast down for each position to find ground position, store these as a new path and replace the old one with it when finished
Surface tool is one of the methods in Godot to define meshes manually (by specifying every vertex position/UV etc.). It enables completely customisable mesh generation, but is a fair amount more involved to get into and is entirely driven by code.
Thanks for watching! Let me know what kinda tutorial you want to see next and like/subscribe! 🥳
A tutorial I would like, would be some way to generate the map by the player. Like, when digging a tunnel.
Beau! Bézier Curves are so pleasing to behold! Have you considered using these nicely curved paths in an endless racer game? Merging some of your existing projects/concepts from last year such as Twilight City? Your own ideas for videos and projects are great on their own, and I look forward to what's next. Thanks for sharing all your work in Godot game development!
Thanks for the feedback and glad you liked my other vids ^^. I don't think this approach would work for an endless runner necessarily, as there's a cost to moving CSGMeshes realtime which may end up prohibitive. I might revisit some old Surface Tool code which should be more performant. I got the path mesh to work from memory, I was just having issues with UVs, so fixing for that would need to happen first!
Not sure I know what you mean, but you can make a CSGMesh subtractive which will allow you to tunnel through other CSGMeshes. Something that I toyed with but couldn't get materials to play nice procedurally.
@@BeauSeymour Well, I'm looking for a way to tunnel, if there is, to tunnel to new area. Making mesh where there is none. As in open worlds, but instead of a landscape, tunnels to dig.
The amount of work that goes into tutorials like these is admirable, great job!!
Wow, brilliant sir. This will take a few watches. I also love your, what I assume, is your default work environment within GODOT which has sparked ideas of my own. Cheers
Glad you liked it ^^. It certainly was my default for a while but for reasons unknown I don't use it anymore (but should!)
very clear, straightforward and good tutorial
Thank you for this video! I had many questions but everything is clear now!
Glad it helped!
Thank you sir!
Would you be willing to make a follow-up tutorial where you expand this spline track system further? Most racing games would require track tilt and track width parameters per spline point as well as perhaps the ability to create forks for optional shortcuts. As far as I am aware, no tutorial maker ever dared to touch on these topics before. Games could hardly be made without them, though.
Well tilt is largely available though finicky using the Path3D handles. One issue with this is the curve handles affect the tilt along the entire spline, so granular per-point controls is not there (yet, there's a github issue where someone talks about adding tilt vector factoring for CSGPolygons, so may be available in future).
Per point width is stepping into custom mesh solution rather than a built in node solution, which is even more finnicky. The Riverways plugin I think is close to what you'd want, so could look at that as a basis and build something off the back of it.
@@BeauSeymour Personally, I'd always build the track visuals entirely by hand, so this additional spline data would be used exclusively for non-graphical purposes. AI racers, for instance, would need the width data for track navigation. Cameras would use the tilt vector to remain upright without the need for raycasts.
I've taken a look at Riverways in the past, and it's very close to what I'm looking for. A combination of lackluster code documentation and my own inability forced me to give up eventually. I'll surely come back to it some day.
Sebastian lague godot version
Haha I'll take that compliment! Thanks 🙏
Brilliant! 👍
Thanks :D
Piece of art! Thank you!
Thanks 😊
This is a fantastic tutorial!
Very useful video, thank you!
I love this thank you for making it
Thank you !!!
Thank you for this amazing content!
I had a question tho, considering this CSG is a 3D node, is there an equivalent way for 2D?
I don't really dabble with 2D, but does the Line2D node not cover most of it? Otherwise you'd probably want to spring your own mesh by iterating each point pair and calculating the perpendicular vector to determine width and assigning UVs accordingly.
Cool
great video!! Following the channel. A doubt I have (I asked this question in another video on a similar subject) would you know how to place objects only when there is a curve? I wanted to put signs pointing curves on the track but I don't know what is the best way to detect the curvature of the track and direction to define the side where the pointing signs will be placed.
Great idea and can think of a few ways you could attack the problem, though will need to test them out. First thing that comes to mind would be to iterate the baked points to take a moving average of turn vectors, then defining a threshold but there's probably better ways.
@@BeauSeymour I have a racing game project that I created a track system with this technique about 3 years ago (I ended up pausing the project at the time due to Godot 3.x limitations and I'm resuming work now with Godot 4). I tried some ways to do this, what I can say is that in practice the signposts should start to be placed before the curves so that the player is informed in advance (maybe an offset). At the time I made a raycast walking on the curve with an object ahead of it, then I could detect the curve, but it wasn't very efficient.
I'll take a look at what you said, but one thing I realized is that it's necessary to have a curvature control that also detects the length of the curve, because if not in very short and smooth curves it puts the signs that maybe not are necessary.
Bit late to reply, but had a break from this project for a while. The following is how I (now) detect corners to autopopulate curve signs:
func get_curvature_3d(point1:Vector3, point2:Vector3, point3:Vector3):
# Get the direction vector of the line between point1 and point2
var d1 = point2 - point1
# Get the direction vector of the line between point2 and point3
var d2 = point3 - point2
# Calculate the cross product of the two direction vectors
var cross = d1.cross(d2)
# Calculate the magnitude of the cross product
var magnitude = cross.length()
# Calculate the dot product of the two direction vectors
var dot = d1.dot(d2)
# Calculate the curvature
var curvature = magnitude / abs(dot)
return curvature
You then feed it point1 (which would be some distance behind the current point on the curve), point2 (the currently sampled point on the curve) and point 3 (some distance ahead on the curve). I may cover this more in another video as I've revamped a lot of the code from this one already to make it do more.
If you want to see what that looks like:
twitter.com/Bimbam_tm/status/1619664573531062273?s=20
@@BeauSeymour Thanks for this function! It will be very useful!!
This is a nice method, but quick question. What if I wanted to make something more than a simple loop? What if I wanted to build city roads with intersections? Do you know how one would go about doing that using this approach? I could intersect the curve on itself but the textures would be wrong and the side of the road meshes need to be deleted somehow. Or perhaps I would have to put the intersection pieces first and then connect them with individual curves?
I'm not sure what the 'correct' method is, but suspect the only reasonable way to handle intersections is to precreate them as models, then snap the road curve to them at defined entry/exit points. Anything else I can think up just breaks the moment you try to automate texturing it :S Maybe some I'll have to investigate in a later video but caught up with another project ATM ^^
Nice tutorial but I'm getting completely stuck at 03:07 when you create the other kerb. No idea how you've done it, you skip through at lightening speed.
If you pause at 2:58 you will see in the top right that the four points on screen that represent the road shape are 2D coordinates. (0.0,0.0) -> (0.0,0.1) -> (0.1,0.1) -> (0.1,0.0). This is a closed loop, so the last link to (0.0,0.0) is implied. The Y component defines the height of the road mesh, the X component the width of the road.
I just copy this csgmesh that represents the road and tweak at 3:02 to make the 'curb' segments for the left-hand side. All I am doing is making the points start at (0.0,0.0) but then move left of it (negative on the X axis) and slightly higher than the road on the Y axis.
The right-hand side is the same again, but I am moving the points further right of 0.1 on the X axis, and the same amount higher on the Y axis as before.
Hopefully that helps but let me know how you go!
@@BeauSeymour Excellent, thank you very much!
Thats amazing but how to edit track in packaged game not in editor
You would need to build the GUI interface for the user to interact with, which then interfaces with the Path3D node under the hood. This is by definition unique to your implementation, but any GUI tutorials will help get you started.
@@BeauSeymour thank you now i know where to start
can you please tell me how u opened the top orthogonal window?
Where it says 'top orthogonal' in the top left of the window is a button with several drop down options for different views. Alternatively clicking the 'Y' of the gizmo in the top right does the same.
@@BeauSeymour thanks for replyin! appreciate dat
Question, Does this tool have the ability to organically add split paths and gaps into the track for more dynamic design, and does this tool also allow you to widen or thin certain parts of the track. If there is where can I learn about doing that?
None of the above, and all of that would require using a custom mesh implementation (though you could still sample a curve to define the track). Godot's Surface Tool is the easiest approach. Making track wider/thinner situationally is fairly easy, I did it in a later clip just by having a path for the inner and outer edge of the track, but ultimately there comes a point where it's easier to just custom model the solution. I highly doubt any aspect of Mario Kart was procedural for example. If it had to be procedural I would probably prefab a bunch of track splitting models and have the path end snap to the location needed for a seamless transition, then two new paths start on the other side. It would likely be quite finicky/bespoke though so don't expect a general tool to exist. The alternative I can only imagine would have some horrible vertex proximity lookups with some kinda smoothing and the UV situation would be diabolical.
Man, I was hoping I could utilize this in 3.5 but for whatever reason it will NOT accept "$Spawn".
It even auto-completes, but I just get "Can't use get_node with absolute paths outside of active scene tree". Bummer!
I tried onready var spawn = $Spawn. Nothing. Thanks for sharing!
Hmm in principle it should work the same in G3.x so not sure what's going on here :(
@@BeauSeymour no worries, I have 4.0 now. Thank you!
I have problems with big scale. At 5:18 you suggested to bake meshes to be static. Could you please elaborate a bit more or give a link to tutorial or something what do you mean exactly?
For Godot 3.x there was a at least one plugin to export/bake csg meshes to a static .obj file. I've not used them as never hit performance issues, but for example you could look at the code for this and convert to Godot 4.0 here: godotengine.org/asset-library/asset/563
@@BeauSeymour thank you!
Two questions:
How bad are the performance issues, (And where are they)?
How does this interact with collisions? (Could this be used to generate collissions as well?)
Re. Performance, I've only really noticed it when editing and it's minor fps dips. The moment you let go of the mouse it's back to normal, though I can't say how big a impact there would be at scale or when combining with other Boolean objects. I think ultimately it's fine for static tracks, but would likely fall over if you wanted real-time track updates during gameplay (which tbh would have it's own quirks to deal with anyway re. Collisions).
Re. Collisions in general, already there as CSGMeshes have the option by default.
@@BeauSeymour So Satisfactory belts should be fine, but Mario Kart wiggly tracks are out?
Good enough for me :D
@@lillysmith6123 wiggly tracks is something I'm eyeballing, but not using CSGMeshes for. Future video perhaps if I get it working.
how you made this snap funktion?
Just iterate the defined path and raycast down for each position to find ground position, store these as a new path and replace the old one with it when finished
How i can make this water??🥺
I basically use the same approach as I discuss in this video: ua-cam.com/video/T1Eri8ql7Sg/v-deo.html
What is a surface tool???
Surface tool is one of the methods in Godot to define meshes manually (by specifying every vertex position/UV etc.). It enables completely customisable mesh generation, but is a fair amount more involved to get into and is entirely driven by code.
@@BeauSeymour oo i get it now...
Btw great tutorial...
And how about actual production games? CSG nodes are basically useless for real games, only for prototyping as they don't have UVs.
I didn't realise Triplanar mapping wasn't used in 'real games'.