A week ago, I started working on a street racing themed, ultra-casual mobile infinite runner game. Not terribly original, I know, the app stores are filled with this sort of thing after all. But the point here is to actually release something, not to be original or profitable. I've been dicking around in Unity for close to ten years now without ever actually completing or releasing any actual games. Surely, if I haven't learned enough to release a simple mobile game by now, I'm probably never going to.
In a similar vein, this post marks the beginning of my Devlog, what I hope will become a semi-regular blog series where I talk about whatever I've been doing lately related to software development.
Even though the game is going to mostly play and feel like an infinite runner, and I'll probably have an infinite mode available, the main gameplay loop will be built around racing.
There's going to be a start, a finish line and AI cars to compete with. The player will want to use slipstreams, boosts and obstacles at least somewhat strategically to overtake the AI cars. When you win the race, you get more money which you can use to upgrade your car so you can win more races.
This is supposed to be a hypercasual game, so the controls are very simple. You simply swipe left or right to change lanes. In the future there will probably be some buttons on the screen to activate special boosts. Jumping over obstacles and NOS for a temporary speed boost come to mind.
The art's mostly from the Asset Store, made by everyone's favourite Synty Studios and a few other free assets from random Asset Store publishers.
Most of my projects where I decide to do most of the art myself seem to end up getting procrastinated to oblivion, so the new challenge I'm giving myself is how much art I can get away with not doing myself.
To balance this out a little bit and to prevent this game from being a complete asset flip, I'm writing all the code myself. If it's not written by me or by Unity (and thus kind of a part of the engine), it's not going into the game. I've tried working with some code assets before, and I've come to feel they're generally not worth the headache of compatibility issues, bugs and just assets getting abandoned by their creators.
Obviously, your mileage may vary, but since I'm primarily a programmer anyway and have had those ten years to learn all this stuff, I'll stick to doing my programming myself. Also, I do have the benefit of years worth of personal utility libraries, like my dependency injection library that's available on GitHub and many others that I haven't open sourced.
The biggest overall decision I made was to have the player be stationary on the Z axis while the world moves around the player. The player's car only moves along the X axis when changing lanes and perhaps later on the Z axis if I add jumps.
The environment works by spawning new environment segments ahead of the player, which then steadily move towards the player and get despawned after they're behind the camera and no longer visible. I tried making the environment pieces fully independent of each other but for some reason they ended up not spawning flush to one other, leaving gaps in the terrain. Some kind of timing issue I'm sure, but I couldn't be bothered to actually figure it out, so I made it so that the oldest terrain piece acts as a kind of locomotive that pulls the segment behind it forward, which pulls the one behind it forward and so on.
Pickups, obstacles and any other junk on the road works similarly, just with everything being independent of each other. An alternative would be to parent them to the road segment on which they spawn, but that doesn't really give me anything extra at this point, plus the current approach works if I decide to use the same code for a game that doesn't have roads. An infinite runner with airplanes, for example.
One problem doing things this way is that the scenery pops in very noticeably unless you spawn it a really long distance ahead which wastes performance on rendering things the player can't really even see. Alteratively, I tried angling the camera down so the player can't see as far forward, but that's a problem of its own.
So, instead I cobbled together a shader which bends the world down exponentially the further along the Z axis the geometry is, which creates the appearance of scenery appearing from behind the horizon. Not exactly realistic but works with the cartoony graphics I'm going for anyway.
The downside here is that since it's a custom shader, I have to create a custom material with that shader for every object that I want to bend. I'm guessing it's be possible to accomplish this globally across all objects with the scriptable render pipeline, but I don't know how to use that for anything than material replacement which won't work here because I want different objects to have different materials, just the same shader.
I could have tried to make all this work with physics simulation but that can be kind of fiddly at times. For a simple game like this, having everything pretty tightly locked down and on rails feels like a better way to keep everything under control and to eliminate any chance of a physics glitch breaking the game.
With that comes the problem with collision detection. For collision triggers to work, one of the colliders must have a non-kinematic rigidbody attached to it. That's not possible here because I wanted to keep everything tightly under control and not use physics simulation for anything other than maybe some special effects later on.
Because this is such a simple game, however, everything in the game world is facing along the Z axis which means I'm able to use the object's axis aligned bounds for simple collision detection. I'm using
Physics.OverlapShereNonAlloc to get a list of nearby objects for the player car and checking each if their bounds intersect with the bounds of the player car.
If you have any comments, corrections or general feedback, you can email
feedback -at- mattihiltunen.com and I may feature it in
a future post. I apologise if I'm not able to respond to you personally.