top of page

Island Level Select Screen Mock-Up

Keyboard Controls:

Enter, Space, or E to start 

A and D or Arrow keys to switch between level previews

Backspace, Escape, or Q to return to zoomed out view

Gamepad Controls:

Start or Bottom button to start 

Left stick or D-pad to switch between level previews

Left button or Right button to return to zoomed out view

This was a solo Unity project/interactive art piece that I worked on-and-off on for a couple of months. All of the models, textures, shaders, animations, and programming were done by me.

The inspiration for making this was my love of the sort of miniature chibi-ified depictions of game overworlds you often see in start and level select screens. Since I'm a big fan of model trains, I thought it would also be a fun opportunity to make a digital train table of sorts.

Down below I go into detail about the design process and development, as well as show some behind the scenes pictures.

Initial Planning and Layout 

While I started the project with a rough idea of how I wanted to structure the island, I had a hard time figuring out how make it flow in a way such that the train would neatly pass through each area and do a continuous loop. My solution to this was ultimately just to block everything out with cylinders in Blender and iterate until I landed on something that worked.

In a happy accident, I stumbled upon a layout that allowed for each area to be visible from the front view of the island pretty early on. This was very appealing to me and something I wanted to preserve in the final composition. While doing so was sometimes difficult and forced me to rethink several design choices, it also inspired new ones.

For instance, I initially had plans for large mushrooms to be a prominent design element in the haunted woods area, but I found that they obscured the desert too much. However, when I removed them, I had the idea to include a shipwreck that could be seen through this newly opened up window in the terrain, which I think looks a lot better.


Some of the early iterations

The final design (just in case the game imbed didn't work)

Shaders and Visual Effects

Here's a compilation of some of the various shader and visual effects I created for this project, along with insight into how some of them work. 


The star twinkling effect provides some vibrance to the night sky, and is largely pulled off using this fun polka-dot mask and a couple of sine functions.


Figuring out how to make a convincing water spray shader for the head of the geyser was something I struggled with for a bit. However, the core of the effect I came up with is really just comprised of a mask that gets horizontally scrolled in opposite directions, coupled with voronoi noise used to cut additional bits out along the edges and hide obvious periodic repetition.


The ocean sparkle effect is perhaps the biggest "cheat" I pulled off. By multiplying scrolling noise by a vertex color gradient along the edge of the ocean mesh and passing it through a step function, I create these blobs that look like light bouncing off the horizon from a distance. While it may not make for a convincing water shader up close, it luckily doesn't need to.

Making the ghosts in the haunted woods actually look good took me longer than I care to admit, and the solution somewhat-obviously ended up being to make them particle trails. 


A better look at the train animation and the spline the train follows. Unfortunately, because you can't import splines from Blender, I couldn't just reuse the one I made to create the train tracks and instead had to remake it in Unity.

The cannon animation makes use of a particle system burst and some custom smoke meshes.

The Camera System

Setting up the camera system for previewing each area on the island was pretty straightforward, albeit a bit more labor intensive than I expected.

At the heart of it, I use a Cinemachine state driven camera that handles most of the camera blending for me. I give it a list of virtual camera blends and an animator that has a state correlated to each virtual camera, making it easy to switch between them in code with the Animator.Play function. This works especially well for going between individual area preview cameras.


However, switching to and from the camera that spins around the island is a bit trickier, as simply doing a normal blend results in the camera clipping through the terrain if the blend target is across the island. To solve this, I set a corresponding rotation angle for each area that the spin camera will rotate towards before starting its blend to that area's virtual camera. I also snap it to this angle whenever I'm about to blend back to it.

While this sounds easy on paper, most of the work was actually fine tuning everything to look and feel good. This involved writing a lot of code to dynamically change blend lengths and start times so camera transitions wouldn't look choppy or feel too fast or slow. I also did some subtle UX work, such as making the transition speed between areas ramp up as you continue to hold down the arrow/WASD keys or left or right on a gamepad control stick. 

Third person perspective of the camera to better show what's happening during transitions


The list of all the camera blends


An easily configurable manager script handles the camera and area transitions

Area-Specific Lighting

I think this is maybe my favorite system because it really makes the map feel dynamic and alive. It was also actually fairly easy to implement and hook up to the camera system.


In short, each area preview has an associated lighting profile and the scene lighting lerps between them basically any time there's a camera blend. An enum keeps track of what time of day it's supposed to be for each area, which can be set to day, sunset, or night. While it has no bearing on the actual lighting, it does influence the skysphere and what lighting profile the spinning camera will use if you switch to it, with there being a "default" lighting profile for each time.

Some screenshots of the island at different times of day can be seen down below.

area previews.png

A scriptable object stores all the data for each area

bottom of page