Sunday, October 18, 2015

Game 1


When it comes to my own personal projects or ideas, I have a very hard time finishing anything. I love dabbling and tinkering and fulfilling some curiosity, but it is almost impossible for me to 'deliver'. So when I was recently motivated to play with some pixel art, what started as a small toy project pixel-art experiment evolved into a small game that was completed within a few weeks. This is the story of the process of making that game.


Inspiration

The original inspiration came from a few sources. Firstly, I recently attended the Seattle Retro Gaming Expo. Among other things, there were a few booths in which some indie devs were showing off their retro-looking games. Needless to say, I saw some mediocre pixel games that had been worked on for mere weeks before being presented in the Expo and felt I could do better. Secondly, I attended the Seattle Code Camp recently, at which there was a Unity demo in which the presenter created a Flappy Birds clone within an hour. It helped me remember how simple things can be behind the scenes and still look good and be fun. Finally, I have not yet dabbled with Unity 5 or any of the newer features even in previous versions, such as 2D sprite animations, new UI system, and HTML5 deployment.

Environment

One interesting aspect of this project was the environment. Since I didn't have Unity 5 installed on my laptop, nor the space to install it, I opted to try development on an Amazon Web Services EC2 instance that had GPU capabilities.  I had dabbled previously with that, having set up UE4 on there since it ran slowly on my laptop. Basically, this was a virtual Windows machine in the cloud with a GPU that I could remote desktop into and work on as if I was running Parallels locally on my Mac. In order to do work, I would start up my instance, remote-desktop into it, work for a while, then commit changes and shut down the instance. Interestingly enough, it was much better on my laptop's battery life than running Unity locally. Maybe I'll discuss this more in a later post.

Day 1

I'll discuss the first night in a bit more detail than other nights since it really set the tone for the entire project. I sat down on a single night with nothing but the inkling to play with pixel art in Unity. I knew I was just going to spend the night gratuitously slinging a bunch of animations and things together to make something. Anything.


It started with Googling around for sprite animations. I found and borrowed one of my favorites : the entire sprite sheet for Samus from Super Metroid. It took a while to figure it out, but I was able to create a simple animation of just the run cycle. Using just the run cycle was much simpler than trying to extract all animations and create a complex animation controller for all motion of the character, so I stuck with just the run and moved on. Next I Googled around and found some skylines of Tokyo and tweaked them slightly in a photo editor. Next, the ground was created by hand, and in order to make the runner look less obviously like Samus, I opted to tint the entire character black to hide detail. Keeping this silhouette aesthetic would keep the art extremely simple and help with keeping a fast pace of forward progress. Next, I added screen-space pixelation in Unity with a low-res render target. This approach was very configurable and added a sufficient amount of abstraction to Samus and the backgrounds. Finally, I added some other background elements that were parallaxed to give the impression of some distance and depth. After a few hours, I was pleased with the result of the runner with the scrolling background. I knew the pixelation effect wasn't yet perfect, but it was a good first step.


The borrowing of the art was great because seeing something that looked good immediately was a huge contributor to the motivation. And yes, I know 'borrowing' art is bad, but I am not going to make money on this, I'm making no claim about owning the art, and really, nobody is going to care since this is just a tiny toy project. Secondly, having constraints helped aid the design. That's to say, by wanting to abstract away the details of Samus and simplify most of the foreground art, the silhouette and extreme pixelation aesthetic came to life. Also, by not focusing on something interactive, and simply on something that animated, much of the initial complexity was reduced. Avoiding those pitfalls and following the inspiration of 'slinging' a bunch of things together to make something that looked good only perpetuated the motivation.

Continuation

It wasn't until after the first night that I decided it was something I could feasibly bring to completion. The first goal was to improve the pixelation effect, which was producing a strange wobble due to how the background was being discretized to the render target. Adding game elements would be less of a priority than the pixel effect, and honestly not quite as interesting.

The single biggest technical hurdle was proper pixelation. That was the whole impetus for the project, so that was the one thing I was determined to get as good as possible. The first version had a noticeable wobble or shimmer effect, and took a few days to understand where it was coming from and how to fix it. Here come some technical details.

Scene Organization

The scene is split into 2 smaller sub-scenes. One represents the 'world' where the objects reside, which is a 20 x 10 Unity units (meters) area where game objects reside as sub objects. The camera of the 'world' scene renders to a 128 x 64 pixel render target. It uses an orthographic camera so all of the size and speed scaling based on distance is handled manually. More on that later. The 'screen' scene simply renders that render target to a quad and displays it to the main camera.

The 'world' and 'screen' scenes in the hierarchy view 


20 x 10 Unity world space uints map to 128 x 64 screen pixels


Interesting to note, in the version that I released, the screen scene's camera frustum extends into the world scene, which probably means it's not culling the objects in the world scene behind, which could be a huge performance issue. Whoops.

Texture Filtering and Source Art Size

This topic is the single most important part for proper pixelation. Point-sampling was used by all textures and sprites to prevent the default texture sample filtering from making it look blurry. Without point-sampling, everything looks too fuzzy and objects have a semi-transparent boundary, which ruins the effect. Pixel art is very high-contrast, usually with no semi-transparent sections or borders. Secondly, keeping the source art of some multiple of the final resolution kept the objects from having semi-transparent edges and prevents the wobble effect. The following examples highlight the need for this. 
  • Bilinear filtering is on the source image and the image is a multiple of the screen pixel resolution. The result is intermittent blurriness as the background slides across the screen. Filtering calculates averages as the the image pixels map to different screen pixels over time, making it blurry.


Bilinear filtering on source texture causes fuzziness


  • Point sampling is on the source image, but the source image resolution is not a multiple of the screen pixel resolution. The result is a wobble or shimmer as source image pixels are discretizing to screen pixels inconsistently over time. In this case, a 512 x 256 image is inconsistently mapping to a screen pixel resolution of 140 x 70, resulting in wobble.

Point sampling on source texture whose resolution not multiple of pixelated screen resolution causes wobble


  • Point sampling is on and the image is a resolution a multiple of the screen pixel resolution. This results in a proper and consistent mapping between image pixels and screen pixels over time.

Point sampling and source art resolution is a multiple of pixelated screen resolution results in good movement

Those with a keen eye will notice some of my background art suffers from some issues still. Namely, there are some semi-transparent edges on some of the parts of the building. It's true, I wasn't that diligent about ensuring pixel-perfect source art, and so there are a few small issues in the backgrounds.

Object Position Discretization

There were two types of objects in the scene. Backgrounds (mentioned previously) were simple scrolling textures on quads that were scaled to the entire 'world', that had texture wrapping on. Background props and enemies, other the other hand, were Game Objects that had a sprite renderer and had a physical position within the 20 x 10 world. Removing the shimmer / wobble on objects required extra work.

I tried and failed to find any built-in Unity solution to this, though granted I didn't look too hard. While there were options in the Sprite importer about specifying 'units per pixel' or whatever, which helps you define how the sprite would map to your world-space units, there didn't seem to be anything that could lock or discretize transform or physics positions to the screen pixel boundaries. 

I created a script to keep them snapped to the pixel grid kept the pixelated object consisted despite its position, rather than shimmering across screen pixels. Interestingly, at the time of writing this blog, I realized that script wasn't necessary because the source-art, already being a multiple of the screen pixel resolution and being scaled appropriately to match texture pixels to screen pixels, was already correctly preventing the wobble. But such a script would be necessary to allow for arbitrary pixel sizes or screen pixel resolutions if it was desired that the source art wasn't authored at a resolution that was a multiple of the final resolution. Though you could still have fuzzy borders in that case. In retrospect, the sprite asset's 'unit per pixel' attribute could potentially handle this correctly.

The rest is fairly simple. Scripts on the background props slide them to the left at a designer-specified rate per frame. The ones further back are smaller and scale more slowly. After a certain x-position threshold, they teleport to the right and recycle. Simple.

Other Gamey Junk

There are aspects of game development that I love and other aspects I find extremely tedious. I preferred to concern myself with the aesthetic, technical implementation of the aesthetic, and over-all look and feel for this project. This involved Googling for more borrowed art, such as the sprite animations for explosions and the weapon firing. I did not want to focus on the 'gamey' aspects, such as score, enemies, rotating projectiles, and player death / loss condition. I found those elements extremely tedious, though necessary to make it interactive and more appealing to a broader audience. Also, those aspects aren't particularly interesting to read about since any game has those hurdles to overcome. And that's also why the game isn't particularly interesting - no interesting enemy waves, or escalation of difficulty, or anything that makes for a good game. Oh well. Oh yeah, You can see some of the sprite sheets I found below.


Sound Effects

All sound effects were added within a few hours. Most sounds were found on Freesounds. Again, this goes in line with 'borrowing' art to make a lot of forward progress. The 'music' was constructed from scratch using Ableton Live. The shooting, explosion, footstep, and death / respawn sounds were from Freesounds, though the footsteps were massively modified in Audacity, and the death / respawn sound was the combination of multiple sounds, also done in Audacity. Also, the explosion and shooting sounds were played with slightly randomized pitch and volume to prevent it from sounding too unnatural. Ultimately, sound effects were one of the last things to go in, but they turned out pretty well and really helped add a final level of polish despite being done pretty quickly. But again, the implementation was nothing special.

Post-Mortem / Restrospective

Simply by writing this blog and attempting to explain how it worked, I discovered a few small issues with the pixelation that I spent a non-trivial amount of time and effort trying to work around. Had I been in less of a rush, I could have found simpler solutions sooner rather than trying to build around them.

What Went Right
  • Borrowing art - looking good early on helped with motivation
  • Working within constraints
  • Following motivation (local maximums for motivation) rather than having a grand vision for the entire thing I just worked on each little piece as I was inspired to address it.
  • Finishing something
What Went Wrong
  • Lack of a game/interactive design
  • Pixel shortcuts - working around some pixelation issues rather than finding the cause
  • Taking longer than expected - I had hoped to release within a week, but I didn't have time so I counted days that I worked rather than an actual amount of total time.
  • Borrowing art - lack of complete ownership
All commits for the project, giving a sense of the order things were done

Conclusion

Ultimately, over the course of 3 weeks, I spent about 9 or 10 separate days working on this (according to my version control commits). I feel like it was a decent use of time and the reception has been relatively positive. I know it's not the best game ever, but I'm proud of the outcome considering the time and effort put into it. I set out to do what I wanted to do, learn about pixel art games. Also, hopefully I'll have more small, interesting projects that I complete, and get in the habit of delivering. Thanks for reading! Stay tuned for more.

No comments: