Golf Project – Map Generation Pt. 1

Golf Project – Map Generation Pt. 1 (Concept & Basics)

One of the core motivations behind starting this project was, strangely enough, when I had a shower epiphany about a neat way to generate golf courses. It happened several months before ever starting this project.

I remember I was playing a game of Neo Turf Masters (old school arcade golf game) with some friends in Discord. Can’t remember how we got there (probably talking about Apex), but we started joking about how to turn golf into a battle royale game.

There’s all sorts of silly problems to solve: how is it live and competitive, how do players interact, how do you “shrink the ring”, and so on. I thought nothing of it at the time, but the idea was stuck in my head, and a couple weeks later, I had a rough tech spec for this feature.


Initial Goals & Concept

Let’s lay out the desired end goals for this map generation system, and the reasons behind each goal.

Goals & Reasoning

#1: Quick, simple & flexible mapmaking tools for making golf courses

A golf game, much like any puzzle game or platformer, should have a wide enough range of courses & mechanics to keep people interested. To enable this, the tools for making courses need to be robust and not painful to use. I really want to avoid any back-and-forth exporting between the editor and an external 3d art program.

#2: Map data that can be procedurally modified and randomized

Required for roguelike “campaigns” / potential battle royale feature. Even outside of these two features, it would be nice as a way to enhance replayability. Most golf games already have both wind and the hole’s location on the green randomized, so this isn’t that much more.

#3: Map mesh collider physics that are precise enough for golf

Golf courses, being mostly natural landforms, need to have a great deal of small, subtle bumps, hills, and elevation changes. To accommodate these kinds of regular, small bumps, the map mesh collider is going to have to be very, very dense (high poly). On a mesh with coarse collision (low poly), small bumps are going to be so crude as to nearly be impossible. Here’s a visual example of what I mean:

Here’s a small bump on a mesh with a lot of vertices. Extra vertices means this hill is smooth and subtle.

The same bump with 1/3rd the vertices. The crude and blocky faces would be very noticeable.

#4: Fast map transitions

It’s an arcade golf game, so the level transitions have to be lightning-fast to keep the player’s attention. This is in nearly direct conflict with the previous goal, but that’s engineering for you.

My basic idea to satisfy goals 1 & 4, was to represent golf courses as vector shapes with splines, and then generate the map mesh at runtime with the vector data. Golf courses are mostly just long, squiggly lines. That’s pretty easy to represent with vector shapes. In fact, a lot of golf courses render their course maps as vector art, so there’s an easy frame of reference for us to use, which helps satisfy goal #1.

One of the core goals with this approach was to have dynamically modified courses. Since the map shapes are all vector information (transforms, forwards, scales, etc.), you could modify the map by tweaking any of that data and then re-generating the map (satisfying goal #2). With this, you could, for example:

  • Shrink or grow the rough / fairway / green / OB
  • Move, grow, or stretch sand bunkers / hills / trees
  • Twist, pinch or pull the fairway entirely

Using the aforementioned “battle royale golf” idea as an example, this would allow you to send “damage” (think scoring lines in Tetris) to other players in the form of extra bunkers, thinner fairways, or more intense wind.

The main question for this idea is, can this approach be accomplished while also satisfying rules #3 and 4? At the time, I didn’t know a great deal about mesh generation, and it’s also not clear just how granular the collision needs to be / how performant it is. Some things you can only find out by doing.


Planning & Implementation

So I’ve got a rough idea and vision; now, how to go about doing it? My immediate action plan was as follows:

  • Get some basic spline math in, with some visualizer gizmos on the points
  • Use a main spline and some distance offsets to generate a “fairway” outline
  • Fill this outline in with vertices at regular intervals
  • Generate a fairway mesh by sowing rows of verts together into tris

This is pretty barebones compared to the full desired feature set, but you gotta start somewhere. I didn’t have much experience with spline math or runtime mesh generation, so I figured it was best to start small and slow. Here’s some progress photos:

Basic splines and debug spheres visualizing a path + outline between points. First try’s a little rough.

Some better debug visuals, and a more reasonable vert step, but still some path following issues.

Fixed a lot of path following issues, and the tighter turns aren’t flaring out as badly. Good progress.

Some technical notes on the above…
  • In my initial approach, I was calculating non-continuous cubic bezier splines between each path point (the green dots), using the start, start + forward, end + back, and end points as p0 – p3, and then *also* doing cubic bezier splines for the outline offset (the blue dots). The main path does this fine, but the outline offset would produce worse asymptotes as a consequence of being non-continuous (my guess). I switched the outline from being cubic to being quadratic (simply aiming to hit the calculated midpoint as the middle term), and it ended up looking nice.
  • There’s also an issue of these bezier splines having variable velocities; by that, I mean that the points are not evenly spaced out positionally despite having evenly distributed steps on the spline. I eventually solved this issue in a really basic way by just combing back through the spline and snipping out verts that were too close to one another.
  • Finally, from a tools perspective, each individual “path point” could have its own interpolation curve that dictates how to interpolate from one path’s offset radius to another. This ended up being too fiddly and complex for anyone to bother really using, so I ended up nixing that later and just replacing it with a library of some basic eases.

Ok, we’ve got the spline and path outline working; now the mesh. This part was much more tough, and involved a LOT of notebook scribbles. I didn’t know a great deal about how to calculate mesh verts and faces correctly, so definitely a big learning step here. More progress shots:

Hmm. That doesn’t look right.

Ah, that’s much better.

Mesh details showing the uniform rows of faces.

Got some good progress already, but it’s worth noting down some things I see as concerns / points to research more in depth, later:

  • Wherever the fairway curves, the vert rows tend to pinch on the inside curve; this is a consequence of the row “sowing” I’m doing. It might be better to find a different way to generate the faces; something like generating a general grid, and then nixing the points outside the fairway.
  • This method lets us easily make fairways with super smooth outlines; however, smoothness doesn’t look natural. Might be useful to look into some ways to add small perturbations and edge noise to the fairway, to get it looking more “real”.
  • One small gain on this approach so far is that the “minimum distance between rows” value is completely customizable, and as such, the face density (and inversely, mesh size / complexity) of a course can be changed anytime. This is nice from a prototyping standpoint insofar as I don’t need to stress so much over setting a “max / min tricount” standard.

In the next post, I’ll get terrain physics working, go over the various offset points that can be used to modify the fairway, and also some upgraded tooling.