Global Game Jam happened again, and this year’s theme was “Roots”. I started pretty early on this one, basically the day after the theme announcement, which happened on the 28th of January. To be honest, the theme kinda stumped me a bit at first, partly because I was doing some prep work prior to the jam that was focused on a hoverboard mechanic.
Eventually, myself and my friend struggled so much for ideas that we just looked up the definition of “Roots” to if there was anything that would give us any inspiration. And I happened to stumble across the following:
ROOT
COMPUTING: A user account with full and unrestricted access to a system.
Putting 2 and 2 together, I suggested making a hoverboard game in cyberspace.
Early on the idea was to have a player constantly go forward down a track, avoiding obstacles. That was a pretty general core plan. Problem one: downhill movement. As you can see in the video above, it kinda works but it’s slow and stutters a lot. Furthermore, the original turning code from the previous prototype would go against the constant downward trajectory I was looking for. But more importantly, the track I made wasn’t straight, so the constant forward force I was applying would send the player off the track soon after.
There was one solution I knew would work well enough to keep the player on track, and that was to place waypoints along the surface of the track and aim the player along those, while maintaining a forward force in relation to their rotation.
My first implementation was pretty horrible. A lot of stuttering movement and the player curving into the waypoints excessively hard, killing the speed and handling. My first implementation relied on rotating the player’s forward using Vector3.RotateTowards(). After some changes, I switched it to Transform.LookAt(), and that worked significantly better. But there was still a problem. In the original test, from the video above, the board would match the downward slope of the map and match the slopes on the side. With the new implementation, the downward slope is matched, but now the player stays upright when on the sides. There’s also still the issue of the player turning into the waypoints themselves, but it’s pretty rare.
The curved/uneven map used in the first level ended up being a real pain for me, partly due to the restrictions I placed on myself. The waypoint system was just the first problem, the second issue was scaling. Simply put, importing the level directly from blender was too damn small so I had to scale it up, and then rotate it a bit. But when it came to placing objects on it, that was just a crapshoot. Every obstacle on the first level is placed and rotated by hand, and each one has different values for rotation due to the unevenness of the model. It’s way the placement is so messy.
Gravity was the big issue. The short of the issue is that the player cannot get down the slope on their own gravity fast enough. So I did two things, added the forward force (Previously mentioned) and a downward force. This kept the player moving at a good speed and stop them from going flying off when they hit a ramp. It isn’t perfect though, it’s still very much possible to go absolutely flying.
There was something I discovered while rewriting the code, and well here’s a video of it.
While I was adjusting some code, I was looking at my turning code from my original hoverboard code from the first video on this post. It works by applying torque on a specific axis, the Y axis (Or Vector3.up). As a bit of a fool around, I changed that axis, and noticed that it that it would make the board flip around as if I was doing tricks. But with the first implementation of my code, I knew it wouldn’t be possible to do. So when I rewrote it, I made sure to accommodate this feature. As you can see, it really elevates the gameplay. It’s my favourite feature.
I previously mentioned that I was working with my friend on this, well that isn’t strictly true. Most of the game is a one-man-army effort on my part. Although we started early, my friend decided that his contributions to the game would be minimal until the absolute last minute, so in the extra week or so that we had, he spent 4 or 5 days doing nothing. This was a problem, because my friend and I have an arrangement where we switch roles for each game jam we take part in. This time it was me on the programming and him on the art. Unfortunately, when you require specific assets, like a track or obstacles, and the person in charge of that isn’t making them, it slows down development.
So I made most of the assets myself. Which was interesting. I’m not a great modeller, and you can tell that the assets below are pretty simple. What I have been getting good at is the material work. So despite the low detail look, the materials look pretty good.
I know it seems like I’m moaning about my friend here, but I was very frustrated at the time and I’m still upset about it because I think I could have done more if I wasn’t forced to spend my time generating art stuff.
After learning some lessons from making the first level, I decided that the second level should be a flat plane. There’s only one waypoint, right at the end of the map, so the camera no longer breaks and stutters trying to follow each waypoint. It also fixes a small issue with the lateral movement occasionally being slower than it should.
The big benefit though is significantly easier placement of obstacles. I no longer need to adjust each object individually to fit it correctly to the level’s surface, and additionally, I can bulk edit large amounts of obstacles easily. Level generation become much easier once I switched to this model.
But, it is less interesting to look at. With further experimentation, I might be able to find a good middle ground to have interesting downhill tracks but with the ability to add obstacles to it much easier. That said, I really can’t say enough how much better the second level feels to play compared to the first.
The last level is just a cutscene due to a lack of time. It’s also the only real contribution my friend made to the game. And it was a massive pain in the arse to implement. In fact, I actually finished the last level before making the second level.
But let’s get into what it is and the implementation, it’s quite a story.
The level is a rail going up a tree root. Not interactable at all, more-or-less a cutscene. The rail is a normal mesh and on top of it is a bezier curve. Here’s the problem, Unity doesn’t recognise a bezier curve as a mesh. So I had to find a script to export the curve as a series of coordinates into a CSV file, load it into the game as a text file via the resources folder, convert those coordinates into Vector3 data, making sure to swap the Y and Z axis for each one. After that, I then can to scale the vectors based on the scaling of the objects it needed to be planted on, and then convert it into world space.
Now, all of that is easier said than done, but it took me a considerable amount of time to get working. It was worth it in the end, but I really should have figured it out days prior. I had to make it with less than 24 hours before the deadline.
I like to say that a lot of what I do is a “Learning experience”, and that gives me some motivation to challenge myself during these events. And this was a pretty big learning experience. I learned a lot of manipulating the physics system, generating assets and models for a game (Moreso than the 7DFPS jam), and generally how to build this type of game.
Since GGJ ended, I’ve had this game on my mind and I want to keep working on it weirdly enough. At the very least, I want that first level to play smoother and fix the issues with the waypoint system, or find a better solution to the problem. Perhaps I’ll experiment a bit.
Anyway, that was the game and some of my experiences with GGJ this year. All in all, probably the most productive GGJ so far. If anyone reading this thinks I should continue developing this game, let me know. I would love to get more feedback.
That’s it for this post, bit a long one. Till the next one.
-Adam