Now that another Ludum Dare come and gone, ratings and all, this seems like as good a time as any to do a quick post-mortem of my team’s game Round-a-Bout. Our team’s third Ludum Dare game, Round-a-Bout is a little game about trying to make your way home, on a roundabout that just won’t end. Refusing to ever make a game similar to a past one, this time I had to learn how to drive. Now, let’s talk talk a little bit about what we tried, what worked, what didn’t, and what got cut.

“This time it’ll be easy”

Going into this Ludum Dare, all of my usual team (Girafalope, Trmrddr, Progs, and myself) wanted to have a pretty chill jam: do things we were confident we could do, take decent breaks, and just generally have fun. To my own disadvantage, I had just acquired a new guitar that I wanted to get the chance to use over the weekend. Surprising no one, this plan didn’t last.

How the hell do you make “Stuck in a Loop” fun?

The theme dropped, and we all went into a bit of a spiral of total writers block. Since our time on Frogbert, we’ve moved to a more structured brainstorming methodology for picking our game ideas: for ~10 minutes, we spew whatever came to mind onto post-its, then we group things up, and then we vote to see what wins and run with it. For The Long Night and Life Is Hard, this process of discussion and brainstorming took maybe an hour. This time around, it took us over two.

I was a little worried that, like “Out of Control” in the GMTKJam previously, the theme would be a bit of a trap. It’s super easy to make a game about being stuck in a loop, but it felt much harder to make one that doesn’t just make the player quit out of frustration at the repetitiveness. After a lot of discussion, the idea we launched off of was this:

“You’re trying to commute home after a terrible day of work, but every time you think you’ve found your exit, you discover you’ve found the wrong one, and the interchange gets more complicated.”

Driving Scares Me

Having never made a driving-based game before, the car controls were absolutely the part I was most concerned about getting to feel correct, and I easily could have spent the entire jam working only on this.

I spent the first few hours of the jam desperately trying to avoid doing a complex car simulation, hoping I could get something “simple” going by just shoving a cube rigidbody around with the right forces/parameters. I hoped I’d be able to get away with not having to model wheels, a transmission, etc… and still get “sick drifts.” I didn’t make it happen.

This is a different problem, but it’s so funny I had to include it

After a solid night’s sleep, I caved, and looked into how I could do a proper car simulation. I did a small bit of research and learned how to use the Unity’s built-in WheelControllers, which ended up being way more simple and easy to use than I had expected. Immediately noticing that the car flipped extremely easily, I lowered car’s center of gravity as low as I could possibly put it, and largely fixed the problem. Then I spent another two hours playing around with thrust forces, suspension strengths, friction coefficients, and everything else until the car felt more or less alright.

Originally, we had wanted to lock the car into permanent acceleration to really make things tough. As it happens, that made it far too easy to get stuck up against a wall, so we needed some way to back up. I had hoped to work in a handbrake for sick drifts, as well as a little reverse. Unfortunately, I definitely didn’t get a handbrake going, and our dinky little reverse (half the forward acceleration force) had to pull double duty as a brake.

Do I think the car controls well? No, not really, but it had perfectly serviceable driving mechanics, and we were able to quickly move on to the next part of the game: generating a map.

Even if the brakes kinda sucked

World Generation’s easier than I thought!

A significant part of our idea required having the map expand off of the exit you had just found, something we were all a little worried about implementing. The original plan was to have a variety of interchange types available, and tack them on randomly somewhat haphazardly, which was shaping up to be a bit of a complex mess.

Roughly the system we ended up using

To simplify for the jam, we ended up settling on only a single type of interchange: a four-exit roundabout, which we could align in a simple grid layout. This made things pretty simple for me: every piece of map took up the same space and had a potential exit on each side, making it exceedingly easy to just create a new GameObject a static distance away from the triggered exit.

Handling exit generation was a little bit more tricky. I maintain a list of every available exit in the world, add a new roundabout’s exits to the list on generation, and randomly select from the list to pick the “correct” exit. The trick came in with removal. My first thought was to remove the exit you activated to expand the map, but this ran into issues when placing a roundabout that connects to multiple pre-existing ones.

This says a lot about my development flow, honestly.

The Solution I ended up implementing was to, after tossing the new exits onto the list, scan the list for pairs of exits which were too close together (about 7 units away, in our case) and delete them. Thanks to the size of our roundabouts, this allowed us to catch the doubled-up exits formed by linking roundabouts without accidentally removing anything else. Not the fastest method probably, but it was simple and worked.

List<ExitController> toRemove = new List<ExitController>();
for(int i = 0; i < newRA.exits.Count; i++) {
    for(int j = 0; j < potentialExits.Count; j++){
  	float dist = Vector3.Distance(
  		newRA.exits[i].gameObject.transform.position,
  		potentialExits[j].gameObject.transform.position);
  	if (dist <= 10.0f){
  		toRemove.Add(newRA.exits[i]);
  		toRemove.Add(potentialExits[j]);
  	}
    }
 }
foreach (ExitController exit in toRemove){
  Destroy(exit.gameObject);
  potentialExits.Remove(exit);
  newRA.exits.Remove(exit);
}

AI’s hard too

“Spawning is a work in progress”

Alright, so. I walked into this gamedev cycle wanting the obstacle cars to function like actual traffic. I wanted them to actually drive, change lanes, avoid hitting each other, and even change between roundabouts. As I discovered after an hour or so of trying: that’s incredibly difficult to do, especially in two days.

So I stopped trying to over-engineer, and didn’t do it. Instead, taking advantage of the fact that all the interchanges are roundabouts, I parented all the cars to the roundabout they’re in and just rotate them around at different speeds based on distance from the circle. One notable extra bit I had to add to sell the illusion was to re-orient the cars after the rotation to make sure they’re still facing forward relative to their movement.

float commonAngleMath = Time.deltaTime;
if (!rotateCW) commonAngleMath *= -1.0f;
foreach( GameObject car in AICars){
  AICarController carCont = car.GetComponent<AICarController>();
  if (!carCont.alive) continue;
  float dist = (12.5f - Vector3.Distance(car.transform.position, transform.position))/12.5f + 0.5f;
  car.transform.RotateAround(transform.position, Vector3.up, commonAngleMath * dist * carSpeed);

  Vector3 newForward = Vector3.Cross(
  car.transform.position - transform.position,
  transform.up);
  if (rotateCW) newForward *= -1.0f;
  car.transform.forward = newForward;
}

Surprising only myself, no one felt the game was worse for not having actual traffic AI. Heck, for all intents and purposes, it does.

Sound? Honestly kinda fun

A jaunty little piece of background music

Disaster struck Saturday and Progs, our audio person, had to drop out of the jam after only having time to put together a short audio loop we could use for our gameplay. It proved an opportunity to finally try my hand at making some videogame sound effects. We were going with a pretty irreverent aesthetic with our game, so I made a list of all the sounds I wanted, grabbed my acoustic, and noodled around for about two hours to try and put something together.

My one tool for making the sounds I needed

I recorded it with an extremely closely miked AT2020, and had to learn a little bit about how to use my DAW of choice: Reaper. Thankfully, all I really needed was a handful of tracks I could use to layer some effects. I wish I could tell you anything about my process for creating the sounds, but I “composed” what I did exceptionally quickly in kind of a fugue state. I truly did just make different noises until things felt right. I made use of the percussive elements of the guitar, harmonics, pick scraping, and pretty much any technique you can think of.

Maybe the best sound I came up with: a fun explosion
It’s hard to hear in the game, but here’s what plays when you score points.

How we did

We submitted Round-a-bout pretty comfortably on Sunday evening, with maybe our smoothest release yet. It’s by far our most on-theme showing, and we all ended up pretty pleased with what we’d managed to put together. Like with every game we’ve made, it was another incredible learning opportunity for me, and I’m looking forward to doing even better with my next go at Ludum Dare.

If you’re interested, you can try Round-a-bout here.

Round-a-bout Post-mortem
Tagged on:     

Leave a Reply

Your email address will not be published. Required fields are marked *