Monday, March 10, 2014

Mountain Climber Dev Log 03: Tetrahedral Rabbit Holes

So I decided to drop the mountain climbing theme. It's now about scaling a giant, Everlasting Gobstopper:

Okay, not really. That screenshot is the result of a spending some time down a programming rabbit hole. I ended up scrapping most of the work, but I figured I'd explain what I was going for and where I ended up.

A rabbit hole is an all-consuming task that derails your priorities. When you're down a rabbit hole, you tend to focus on the details of implementation and lose sight of the actual goal of the feature. 

My goal was to make the terrain feel less like a bunch of stairs that the player just hops up. I wanted the terrain to have longer, sloped surfaces that required the climbing pick to traverse. A secondary goal was to make the mountain look less like I lifted it straight out of Minecraft.

I started digging around on Wikipedia to find an alternative 3D shape to cubes that, a) was made of sloped surfaces, b) tessellated with no gaps, and c) fit on a regular grid, so my 3D array of voxel data was still applicable. I settled on a tetrahedral-octahedral honeycomb, which also happened to have an awesome name. I decided to horizontally bisect the octahedra into a pair of pyramids. That way, all the shapes would fit on a regular grid, and the downward facing pyramids would produce an occasional flat surface for the player to rest on. I was feeling pretty clever and excited to implement my solution. That's usually a rabbit hole red flag.

Eventually, after several sessions worth of trial and error, I got it working as I'd planned. You can see the various tessellating shapes in the screenshot above: red "upward" pyramids, orange "downward" pyramids, and green and blue tetrahedra. Unfortunately, you might also notice that it looks like a disjointed, fractal nightmare-scape. In theory, the honeycomb can produce nice, mountainous terrain under very specific patterns. In practice, it tends to produce lots of convex gaps and erratic spikes. I'd hit a dead end in the rabbit hole. 

At that point, I could have tried to finagle the nice, mountainous terrain I wanted from the shapes, perhaps using rules enforced on the voxel data that reduced the undesired arrangements. Instead, I poked my head out of the rabbit hole and reevaluated my goals. I really just wanted terrain geometry that was more fun to climb on and nicer to look at. I decided to go back to the cubic voxels and try deforming the vertices with a combination of Perlin noise, scaling, and a sin-function "bulge".

You can check out the results here in the current build. The terrain's still not perfect, but it's already more fun and visually interesting. More importantly, it was quick and easy to implement. I now also have a bunch of simple parameters I can tweak to produce further variation. I'd escaped the rabbit hole!

I should probably point out that rabbit holes aren't always bad. Yes, they're an inefficient use of time, but that might be fine if your project isn't on a schedule. If you're working on a hobby project that you're not in a rush to finish, rabbit holes can be very fun and satisfying. That's why they're so "treacherous" in the first place. I know several programmers who work on hobby projects primarily so they can spend as much time down rabbit holes as they please.

However, if you're concerned with keeping yourself on track, a good exercise is to periodically assess your goals. Is your current task in service to those goals? Is it taking longer than you'd expected? What's a faster, simpler alternative implementation?

My next goals are to add win and lose conditions to the game and further tweak the player movement and terrain. Hopefully I can hold myself to that!

Monday, March 3, 2014

Mountain Climber Dev Log 02: Climbing picks

I've posted a new build of the Mountain Climber game. Check it out:

The main changes in this build are the additions of the climbing pick model and animations.

Implementing the climbing pick animations turned out to be more interesting than I'd expected. I decided to animate the swing via code rather than animating them by hand, mostly because I wanted to say I'd lerped quaternions. Once I got a simple state machine implemented for the climbing pick (with states like "swinging", "pinned", "withdrawing", etc.), most of my time was spent tweaking the hard-coded positions and rotations for each state and the timing for transitioning between them. 

In order for the player to actually see the animation, I needed to add a time delay between when the player clicks the mouse and when the pick becomes pinned to the wall (the "swing time"). I also added a delay between when the player releases the mouse and when the pick becomes ready to use again (the "withdraw time").

Because the pick swing was no longer instantaneous, a new case was introduced where the player could miss a swing if they'd clicked the mouse when the pick was within range of a surface and then quickly moved the mouse away before the animation finished.

To handle this, I added an "overswing" state with an animation of the pick swinging downwards. When the swinging animation finishes, I check to see if the pick has a valid surface in range. If it does, I attach the pick to the surface based on the geometry's normal and the angle from the player. If there is no valid surface in range, I switch to the overswing state. A nice bonus is that it covers the case where the player swings the pick in mid air with no extra effort on my part. 

The animation delays also prevent a degenerative tactic that I'd found in earlier builds where you could click rapidly to easily scale a vertical surface. See if you can get the exploit to work in the build I posted earlier. I want the player to be able to scale a sheer surface, but I want it to feel taxing and dangerous. Let me know in the comments how you think the climbing feels in the latest build.

My next task is to overhaul the terrain generation. I might be saying goodbye to the cubic voxels...