This article will cover how to implement two aspects of grinding: how to actually create a rail in your engine, and then how to determine which direction the player grinds in. I’m using Godot for my prototype currently (though I may switch to Unity), and this is the part which is most likely engine-specific.
Constructing a rail
Godot provides two node types which are necessary for this: Path and PathFollow.
A Path contains a Curve3D path, and allows for a PathFollow node to traverse across the path. A Curve3D node, represents a Bezier curve in 3D space, though in this article I will only cover the case where there is no curve, only a series of straight lines from point-to-point in the Curve3D - in this case the curve would look like:
Not really a curve…
The basic explanation is that when a player triggers a grind to occur - by a collision with the rail, we take the point of collision and attach the player to the PathFollow and then move the PathFollow up or down the Path.
In my Godot prototype, I’m using a mesh built as part of the level which is attached to a Path node - if the player’s grind detection area (the feet) collide with the mesh, then the Path and PathFollow activate. This contrasts with the approach I took in my Unreal Engine 5 project years ago, where Unreal Engine provided a spline mesh functionality. Potentially Godot provides this, I’m just unaware. I think I prefer the Godot approach because it allows more control of the level design by separating the mesh from the Path.
Grinding in the right direction
When a player lands on a rail, we need to ensure they will grind that rail in the right direction. Otherwise the game will play terribly and the player won’t be able to plan their movement.
If we start off looking at an example on a simple, flat, straight rail. In the below diagram the player’s velocity is represented by the purple arrow, denoted by v and the two points of the rail p1 and p2. Based on the velocity, we need to determine whether to set the player grinding towards p1 or p2. Visually, it’s easy to solve, the momentum of the player leads towards p2.
To calculate this in code, we first find the midpoint, m, between p1 and p2.
m = p1 + 0.5(p2 - p1)
From there we translate v to sit on the midpoint, and giving us the vector r.
r = m + v
With r, we compute the distance (d1 and d2) to both points p1 and p2, whichever point is closest to r, is the one the player needs to grind towards. This is all easily explained by a diagram:
Doing a simple worked example:
p1 = (0, 0, 0)
p2 = (1, 0, 0)
v = (0.1, -1, 0)
m = (0, 0, 0) + 0.5 * [ (1, 0, 0) - (0, 0, 0) ] = (0.5, 0, 0)
r = (0.5, 0, 0) + (0.1, -1, 0) = (0.6, -1, 0)
d1 = dist(p1, r) = 1.16619
d2 = dist(p2, r) = 1.077033
Note: for efficiency, distance squared can be used to avoid the unnecessary square root
As d2 < d1, the player is grinding towards p2.
The limitation with this design, is that it would not support Bezier Curves.
It’s untested, but off-the-top of my head it’s really easy, rather than using the p1 and p2 vectors to compute m and r we would just need to set p1 and p2 to path points just before and after the location where the player collides, and continue as above:
I think the next grinding article I do will cover, transferring the player’s current speed on attachment to the rail, as currently I’ve implemented grinding using a constant speed. This will probably be more specific to Godot, so I really need to decide on Godot vs Unity.