Game Dev


An homage to Fatboy Slim's Bird of Prey music video.

Early Days: Quake and Unreal Tournament

A CTF level I designed for Unreal Tournament

My first exposure to video game development — and CAD — was with Quake, in the late '90s. I created a DM level. It was lost in a hard drive crash, and I don't have any screenshots. A few years later, I developed a CTF level for Unreal Tournament. The concept is that two houses in a quiet neighborhood — and the chunk of land they sit on — are somehow teleported into the Unreal universe, where they are suspended between two God-like figures. There is no escape, only combat. Stepping off the land results in immediate death.

I spent a lot of time adding details to the house. The kitchens have cabinetry and appliances. The bedrooms have beds, desks, computers, and closets with folding doors. The bathrooms have mirrors, the pools have diving boards, and the garage doors are counterweighted and open when shot or collided with.



Second Life

Two aircraft (seaplane, aerobatic jet) and two avatars (Plague Doctor, Rex) that I produced in Second Life. I made the HUD (lower left) as well. The numbered race gates (1:00) and reactor (5:00) are also mine. Music: Ulrich Schnauss - The Magic In You Rex, an avatar I designed in Second Life Code for a pathfinding algorithm (parallel terraced scan) used to navigate the world map

Designing levels in Quake and UT gave me a taste for how CAD and level design work. When I started playing Second Life in 2003, it was a very different experience. Rather than developing levels in an editor by yourself, you built things in a contiguous virtual world. You could watch other people building things in the same world. You could collaborate with them, in real time. To be sure, Second Life's parametric modeler can be somewhat clunky compared to other game design tools, but the shared environment added an incredible social dimension. In video games, the world is often created, exists for a few minutes, and then is destroyed. In Second Life, and other persistent worlds, it remains essentially forever.

Second Life is a very weird place, a sort of virtual Burning Man where every kind of hippy and free spirit can be found. People from all walks of life have been playing it since 2003. It has an in-world economy, and this economy involves exchanging virtual currency (called Linden Dollars, Lindens for short) for goods and services. An exchange called the Lindex allows trading Lindens for real money via PayPal. I was able to use this to make money, sometimes a few hundred dollars a month. (Not bad for a college student on a fixed budget!)

I did a lot of scripting in Second Life, which uses Linden Scripting Language (LSL). LSL is a simplified version of C++, with classes that act like state machines. For example, if you have a lamp, it might have a default state ("off", with the light bulb dark). That state would have a touch event that triggers it to go into an "on" state, which would have code that tells the light bulb to emit light. The "on" state would also have a touch event that tells the lamp to go back to the "off" state. In this way, every time you touch the lamp, it turns itself on or off. There are a lot of API calls to do things like figuring out whether the lamp is owned by the person who's touching it, so you could (for example) make a lamp that only you can switch on or off.

LSL scripts originally ran in a VM with 16K of RAM — and that was for everything: code, stack, and data. It was pretty easy to run out of memory, and trigger a stack-heap collision. Fortunately, you can have multiple scripts in an object, and they can communicate with each other over a simple RPC stack. In this way, you can divide a program into multiple pieces that can talk to each other. Eventually, LSL was upgraded to compile to .Net bytecode. That allows scripts to run orders of magnitude faster, at full machine speed. They also upgraded the memory segments to 64K.

One of my first scripts was a jetpack that would help avatars fly much faster and higher than they normally could. It has three levels of speed assist, and the longer you keep moving, the faster you go. The jetpack's appearance is very futuristic, and it includes built-in engine sounds. The original jetpack was designed for regular human avatars, and was adapted for a robotic dinosaur avatar called Rex.

Second Life was also my first introduction to heuristics. I wanted to create an object that could navigate around the world map, which has many blind alleys and other impassable areas. I used a parallel terraced scan to achieve this. I had never done anything like it before, and it was a lot of fun.

After awhile, I got interested in flight simulation. There are many thousands of aircraft in Second Life, but the vast majority don't behave like airplanes in real flight simulators do. They feel more like UFOs than airplanes. I decided to build my own aircraft, and implement realistic flight physics. When I began this undertaking, LSL scripts were still confined to 16K and ran slowly, so it was very challenging to do. I had to split the program up into multiple scripts that communicated over RPC. This required a lot of string processing overhead, so it wasn't exactly lightning-fast, but it was just good enough. Aircraft control code could be in one script, physics simulation could be in another, automation could be in another, and so on. In this way, I was able to extend Havok (Second Life's physics engine) to produce very realistic aircraft physics simulations. Lift, drag, thrust, and gravity were all calculated. Front cross section, air density, coefficient of drag, ground effect, and other variables factored into the equations. The calculations were converted into forces and torques, and these were applied to the aircraft. As a result, my aircraft performed similarly to those found in "real" flight simulators such as X-Plane and Microsoft Flight Simulator. If you didn't go fast enough, they'd stall. If you stalled at a high angle of attack, the aircraft would begin to roll. Stall recovery worked the same as it did in real aircraft: push the nose forward, level the wings, and give it as much throttle as possible until the stall was broken.

Because my code overrode some of Havok's default behaviors, it could push the aircraft much faster than the normal limitations. Normally, vehicles are limited to less than 100MPH. One of my test vehicles made it about halfway to the sound barrier. This is difficult to achieve in Second Life because the land is in simulators, each of which handles a 256x256 meter chunk. Crossing from one simulator to another on vehicles can be hazardous, and often requires closing and restarting the SL client. My aircraft have some magic to help with this: they detect when they're about to cross over a sim border, disable physics, move the object over the sim border, and only re-enable physics once that step is done. This cuts down on having to restart the game.

In 2006, someone released a proxy that would allow desktop apps to read data from the stream between the Second Life client and remote servers, and to inject data into that stream as well. I had never written a single line of C# before then, but the proxy was written in C#. I decided to write a program that would read axis information from the players' joystick — up, down, left, right, throttle, brakes, etc. — and inject that data into the stream, so that it could be consumed by aircraft in the game. C# is very similar to Java, so it wasn't difficult to pick it up. It took only a few days to go from zero to a working app. In less than a week, I had an aircraft that could be controlled by a joystick with very high accuracy.

The enthusiasm I had for flight simulation eventually motivated me to take flight lessons. In April of 2007, about the same time I graduated from college, I earned my private pilot's license.


Java, Tic-Tac-Toe, SOLID, and Richard Feynman

An unbeatable game of Tic-Tac-Toe

When I took Java in college, the instructor assigned us a final project that I had a lot of fun with. It was a Checkers game for two human players. You had to be able to drag the checker from one box to another, it had to check for validity of the move (and to force you to move a piece if it had to be moved according to the rules), and it had to keep track of the score. He didn't ask for fancy graphics or music, but I just couldn't see doing a video game that wasn't visually interesting, so I added those things. I got an A.

Years later, I revisited the old Java applet. I overhauled it to play Tic-Tac-Toe instead, this time human versus AI. The AI can be forced into a draw, but it cannot be beaten. This is thanks to a recursive algorithm called Minimax.

All code — except for the music player, which I didn't write — was refactored for SOLID compliance. This had the effect of quadrupling the number of classes, but the code became much easier to understand and extend. I wanted to make sure that I really understood SOLID principles, so I used the Feynman method to teach them to myself. This involved watching many videos about it, taking copious notes, and explaining the principles to myself as well as I could. If I couldn't explain a principle adequately, that meant that I didn't understand it well enough, and I had to go back to my notes.

Whoops, looks like something went wrong.

(1/1) ErrorException

file_put_contents(): Only 0 of 184 bytes written, possibly out of free disk space

in Filesystem.php (line 122)
at HandleExceptions->handleError(2, 'file_put_contents(): Only 0 of 184 bytes written, possibly out of free disk space', '/home/solidox/solidox.xyz/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php', 122, array('path' => '/home/solidox/solidox.xyz/storage/framework/sessions/C7PDeRwTxh5b8UAt1ZwKAh4MSjmAUD2JPTg4acqd', 'contents' => 'a:3:{s:6:"_token";s:40:"4m9HnChl4EWTusJbRwCyOvnpKOaUn7eKMZLwQpTF";s:9:"_previous";a:1:{s:3:"url";s:26:"http://solidox.xyz/gameDev";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}', 'lock' => true))
at file_put_contents('/home/solidox/solidox.xyz/storage/framework/sessions/C7PDeRwTxh5b8UAt1ZwKAh4MSjmAUD2JPTg4acqd', 'a:3:{s:6:"_token";s:40:"4m9HnChl4EWTusJbRwCyOvnpKOaUn7eKMZLwQpTF";s:9:"_previous";a:1:{s:3:"url";s:26:"http://solidox.xyz/gameDev";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}', 2)in Filesystem.php (line 122)
at Filesystem->put('/home/solidox/solidox.xyz/storage/framework/sessions/C7PDeRwTxh5b8UAt1ZwKAh4MSjmAUD2JPTg4acqd', 'a:3:{s:6:"_token";s:40:"4m9HnChl4EWTusJbRwCyOvnpKOaUn7eKMZLwQpTF";s:9:"_previous";a:1:{s:3:"url";s:26:"http://solidox.xyz/gameDev";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}', true)in FileSessionHandler.php (line 83)
at FileSessionHandler->write('C7PDeRwTxh5b8UAt1ZwKAh4MSjmAUD2JPTg4acqd', 'a:3:{s:6:"_token";s:40:"4m9HnChl4EWTusJbRwCyOvnpKOaUn7eKMZLwQpTF";s:9:"_previous";a:1:{s:3:"url";s:26:"http://solidox.xyz/gameDev";}s:6:"_flash";a:2:{s:3:"old";a:0:{}s:3:"new";a:0:{}}}')in Store.php (line 128)
at Store->save()in StartSession.php (line 88)
at StartSession->terminate(object(Request), object(Response))in Kernel.php (line 218)
at Kernel->terminateMiddleware(object(Request), object(Response))in Kernel.php (line 189)
at Kernel->terminate(object(Request), object(Response))in index.php (line 58)