<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>luminance &#187; engine design</title>
	<atom:link href="http://www.luminance.org/tag/engine-design/feed" rel="self" type="application/rss+xml" />
	<link>http://www.luminance.org/blog</link>
	<description>Programming and Game Development - Kevin Gadd&#039;s Blog</description>
	<lastBuildDate>Sun, 02 Oct 2011 00:15:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Two-character mechanics (#2) and tilesets</title>
		<link>http://www.luminance.org/blog/gruedorf/2009/03/20/two-character-mechanics-2-and-tilesets</link>
		<comments>http://www.luminance.org/blog/gruedorf/2009/03/20/two-character-mechanics-2-and-tilesets#comments</comments>
		<pubDate>Sat, 21 Mar 2009 02:53:48 +0000</pubDate>
		<dc:creator>Kael</dc:creator>
				<category><![CDATA[Gruedorf]]></category>
		<category><![CDATA[engine design]]></category>
		<category><![CDATA[platformer]]></category>
		<category><![CDATA[xna]]></category>

		<guid isPermaLink="false">http://www.luminance.org/?p=290</guid>
		<description><![CDATA[I spent most of my time this week working on editor improvements, and more two-character support. One of my first steps was to pull code out of the Player class into unique classes for each of the player characters. As a result, only one of the two characters can use a grappling hook, as intended. [...]]]></description>
			<content:encoded><![CDATA[<p>I spent most of my time this week working on editor improvements, and more two-character support. One of my first steps was to pull code out of the Player class into unique classes for each of the player characters. As a result, only one of the two characters can use a grappling hook, as intended. Next, I need to start building the other character&#8217;s special ability <img src='http://www.luminance.org/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>The editor work mostly went into loading art assets and properly handling levels with missing assets, since i&#8217;ve been getting more tiles from my artist of varying sizes and shapes. I ended up building a simple XML-based file format for describing tilesets, based on my serialization framework, so that I can easily point the engine at multiple textures to pull tiles from.</p>
<p>It works pretty well, despite only being a couple hundred lines of code, and can easily be adapted to work with the XNA content pipeline, so it&#8217;s likely I&#8217;ll stick with it. I&#8217;m considering adapting the approach to also work for character sprites, since right now I have to manually tag sprite frames with specially-colored pixels in an image editor, which is extremely awkward and easy to get wrong. Here&#8217;s a snippet from one of my tilesets:</p>
<pre>            &lt;TileStrip key="7" typeId="1"&gt;
                &lt;Id&gt;prison_column_ornate_{index}&lt;/Id&gt;
                &lt;Filename&gt;prison_columns.png&lt;/Filename&gt;
                &lt;TileSize width="32" height="208" /&gt;
                &lt;Border left="0" top="0" right="384" bottom="0" /&gt;
            &lt;/TileStrip&gt;
            &lt;TileStrip key="8" typeId="1"&gt;
                &lt;Id&gt;prison_column_wood_{index}&lt;/Id&gt;
                &lt;Filename&gt;prison_columns.png&lt;/Filename&gt;
                &lt;TileSize width="24" height="208" /&gt;
                &lt;Border left="100" top="0" right="292" bottom="0" /&gt;
                &lt;Margin left="0" top="0" right="8" bottom="0" /&gt;
            &lt;/TileStrip&gt;</pre>
<p>Simple enough syntax to be easy to write by hand in a text editor, but also not too difficult to load at runtime. I&#8217;m pretty happy with it.</p>
<p>One of the challenges in making use of the new art assets is getting all my tiles to line up properly. They have a large variety of sizes, so the algorithm I&#8217;m currently using to select tile positions isn&#8217;t a very good fit for my needs. Right now I&#8217;ve been having to make minor adjustments to my tile positions after I place them, but in the long run I&#8217;m going to want to come up with a tile placement technique that works well for tiles of all sizes.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.luminance.org/blog/gruedorf/2009/03/20/two-character-mechanics-2-and-tilesets/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fury² Post-post-mortem: Lessons learned</title>
		<link>http://www.luminance.org/blog/code/1986/10/20/fury2-post-mortem</link>
		<comments>http://www.luminance.org/blog/code/1986/10/20/fury2-post-mortem#comments</comments>
		<pubDate>Mon, 20 Oct 1986 21:37:50 +0000</pubDate>
		<dc:creator>Kael</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[engine design]]></category>
		<category><![CDATA[game development]]></category>
		<category><![CDATA[postmortem]]></category>
		<category><![CDATA[project management]]></category>
		<category><![CDATA[tl;dr]]></category>

		<guid isPermaLink="false">http://www.luminance.org/?p=206</guid>
		<description><![CDATA[Warning: This is an incomplete draft. I haven&#8217;t found time to edit this down and fix mistakes yet. Starting at the beginning of high school, through my few years of college, and into the first year of my professional career, I spent a significant portion of my free time building and maintaining a toolset and [...]]]></description>
			<content:encoded><![CDATA[<p>Warning: This is an incomplete draft. I haven&#8217;t found time to edit this down and fix mistakes yet.</p>
<p>Starting at the beginning of high school, through my few years of college, and into the first year of my professional career, I spent a significant portion of my free time building and maintaining a toolset and game engine called <a href="http://sf.net/projects/fury2/"><strong>Fury²</strong></a>. It was built as a way for me to quickly prototype games and ideas, to learn about game design and engine design, and most importantly, to let me build games that I didn&#8217;t think I could create using any of the tools that were already out there.<br />
It was a long and painful development project, and by the end, I realized that I had made numerous mistakes; despite that, I also gained many valuable lessons and learned things that I might not have learned any other way, and with the clearer perspective that hindsight brings, I consider it one of the things that made it possible for me to start a career doing things that I love without having to struggle with debt or worry about how I&#8217;m going to afford my next meal. For me, this project embodies the famous Nietzsche quote, &#8216;<em>What does not kill me, makes me stronger</em>&#8216;.</p>
<p>Learning how much sheer effort goes into the process of creating even a simple piece of software has changed me for the better in many ways, so I&#8217;m writing this article in hopes that I can share some of the lessons I&#8217;ve learned with others. Unfortunately, the benefit of hindsight also brings with it the fact that I don&#8217;t remember many of the things that happened during the 8 or so years I spent developing the project. I hope that despite this, you can benefit from what I&#8217;ve learned as much as I have.</p>
<p>Of course, I must preface the contents of this article with a warning: It&#8217;s written in fairly technical terminology, and does not have any particular narrative structure. I have tried to compile the most important issues and triumphs from the history of the project, and present them in a form that a programmer can understand. Regardless of whether you&#8217;re a programmer or not, I encourage you to respond via email or comments and let me know what parts of this you couldn&#8217;t understand, so that I can improve this article and make it worthy of your time.</p>
<p>- <em>Kevin Gadd</em></p>
<hr /><span style="padding-top: 5px; padding-bottom: 5px;"></p>
<h1><strong>The Fury² Game Development System: A post-post-post-mortem</strong></h1>
<p></span></p>
<p style="padding-top: 5px;">This article is inspired by the format of and spirit behind the many wonderful &#8216;Postmortems&#8217; published in Game Developer magazine. I will attempt to collect the most significant failures and successes that occurred during the life of my project, and try to give you a glimpse of what the project looked like and how it felt to use it.</p>
<p><span id="more-206"></span></p>
<p>To begin, the many failures and unexpected challenges I encountered:</p>
<h1>Mitigated Disasters</h1>
<h3><strong>Passing by Reference</strong> versus <strong>Passing by Value</strong>, and the evils of <strong>Variants</strong></h3>
<p style="padding-left: 35px; padding-top: 5px;">Originally all of the APIs exposed to script used the VB default of <strong>&#8216;ByRef</strong>&#8216; (pass-by-reference) with static types. This had the unfortunate side effect of disabling automatic type conversion when calling into those APIs from scripts. As a workaround, I changed the types of all those parameters to <strong>Variant</strong> and did the type coercion inside the APIs.</p>
<p style="padding-left: 35px; padding-top: 5px;">Bad idea. Performance took a hit, and passing the values by reference caused some extremely confusing bugs. In most cases, <strong>&#8216;ByVal&#8217; </strong>(pass-by-value) would have been the correct choice, but I didn&#8217;t do enough research to discover how to use ByVal correctly with the scripting interface. As a result I wasted a lot of time dealing with coercion-related bugs, and then had to sink another few weeks of effort into moving things over to the correct model (ByVal with explicit types, ByRef where appropriate) much later on, which broke some code. Even by the time I stopped maintaining Fury², some portions of the API still had to accept variants as arguments for backwards compatibility reasons.</p>
<p style="padding-left: 35px; padding-top: 5px;">Some of the worst bugs in my engine were a result of this decision as well &#8211; out of my top 10 worst bugs, one of them was caused by an object reference being passed ByRef (that&#8217;s right &#8211; a reference to a reference) down a stack of function calls, about 12 levels deep. All the way down the stack, a function assigned null to an object reference before returning &#8211; a reference that had been passed in ByRef. The end result was that the null travelled all the way up the stack and obliterated the contents of a member variable elsewhere, causing multiple data structures to get out of sync with each other. This was worsened by the fact that the variable in question happened to be part of a linked list that was shared with C++ &#8211; more on that later&#8230;</p>
<h3>Lacking understanding of <strong>COM</strong> (Component Object Model)</h3>
<p style="padding-left: 35px; padding-top: 5px;">As a result of VB6 being built on top of COM, I was somewhat familiar with its feature set and how to use it at the beginning of the project. Unfortunately, I lacked deep understanding of a number of important details that turned out to be critical to getting my engine to perform well and run reliably. The main problems I encountered were:</p>
<ul style="padding-left: 30px;">
<li><strong>Circular references</strong> &#8211; while I had a vague notion of what a circular reference was and how it could cause resource leaks, it took a while before I finally realized that COM provided no built-in mechanisms for freeing object cycles. This meant that somewhat far into the life of my project, I started noticing that I was leaking huge amounts of memory on transitions between different maps, when I shouldn&#8217;t have been leaking anything at all. The ensuing cycle-hunt (using manual inspection of reference counts, hand-coded weak references, and all sorts of things that are extremely painful to do in VB6) took a lot of time, and while it yielded results, I still never got to the point where my application was cycle-free, and the introduction of weak references late in development created numerous elusive crash bugs.</li>
<li><strong>Creation/destruction overhead</strong> &#8211; Due to the lack of good support for structs in the scripting framework I was using, basically every type in my engine&#8217;s APIs was a class, which meant it incurred COM reference-counting overhead. This ended up being extremely painful for plain-old-data types like rectangles, which were created and destroyed extremely often and stored in many places. Even after some heavy optimization (reusing rectangles and similar objects whenever possible, and implementing object pooling where possible) this was still a significant bottleneck that could have been avoided with a better design.</li>
<li><strong>Type library registration and versioning</strong> &#8211; I originally believed that I could deploy all my components using what VB6 called &#8216;binary compatibility&#8217;, where I could deploy a new version of a given type library that could be dropped in to replace the old one without breaking any existing binaries. This worked for a few months, but eventually I realized that it was impossible to maintain binary compatibility given VB&#8217;s restrictions and the rapid pace at which I was making changes to my APIs. Once binary compatibility went out the window, distribution and installation became a serious issue &#8211; having to register over a dozen type libraries for a given version of the engine was extremely painful, creating significant performance issues and making it impossible to run games without being an administrator. Later, I even ran into versioning conflicts with a couple Microsoft type libraries where the latest version of a library was not backwards compatible with the previous version, but the previous version had a crippling bug &#8211; which meant I couldn&#8217;t run on a machine with the old version, but I couldn&#8217;t forcibly upgrade it without breaking other applications.</li>
<li><strong>Apartment models and RPC</strong> &#8211; VB6 does a fairly terrible job of abstracting away the many nuances of COM. Other than type library versioning, mentioned above, the worst examples in this area are probably apartment models and RPC &#8211; both options are exposed to users of VB6 via radio buttons in the project properties window; unfortunately, the documentation almost completely glosses over the significance of these options, making it easy to select the wrong one and not realize your mistake until much later. I spent weeks developing my tools pipeline using COM RPC to communicate with a running instance of the game engine, resulting in some extremely confusing bugs and awful performance characteristics. It was only once I realized what was going on behind the scenes, due to the awful performance (why is this so slow? wait, the engine is running in *another process* and COM is marshalling all my data between processes&#8230; uh oh) that I understood the importance of those settings and learned to leave them set to the defaults. I had some utterly mindrending bugs where I was allocating GDI bitmaps or large arrays of data in an Engine class for a map layer or an entity, and passing the class instance across the RPC boundary to the editor, and then scratching my head in confusion when BitBlt or CopyMemory failed to do what they should have done, due to the fact that I had just marshalled pointers and handles across process boundaries in an unsupported way, without even realizing it.</li>
<li><strong>Quality issues</strong> &#8211; unfortunately, many of the implementations of COM that I had the misfortune of dealing with were subpar. Some were downright broken. I discovered dozens of bugs in various COM libraries and APIs, some of them ones provided by Microsoft themselves. In most cases my only choices were to hack around the bugs or reimplement the entire component myself, and I ended up choosing the latter. While most of the implementations in Windows itself and the VB6 runtime were fairly stable, some had extremely bad performance characteristics that necessitated writing my own implementations &#8211; and reimplementing COM library functions is *not* a walk in the park.<br />
One positive note in this section, though, is that I happened to stumble across a bug in PyWin32&#8242;s support for COM exceptions, and the maintainer was very quick to merge my fix into the trunk. This was one of my first real introductions to the value of open source.</li>
</ul>
<h3>The wrong model for rendering</h3>
<p style="padding-left: 35px; padding-top: 5px;">The object model I chose for my graphics API had some strengths, but it also had one primary weakness that caused most of the graphics-related stability and performance problems the engine had later on: I had no distinction between mutable and immutable images. During the early stages of the project, this was liberating, because it meant that I could easily whip up prototypes with complex visual effects and that it was straightforward to create reusable pieces of rendering code that could be applied to anything.</p>
<p style="padding-left: 35px; padding-top: 5px;">The downside, which didn&#8217;t become apparent until much, much later, was that having an object model where everything is mutable makes it extremely hard to accelerate graphics rendering using hardware, because neither OpenGL, DirectDraw or Direct3D offer any way to get high-performance mutable surfaces that can still be used as textures. I ended up sinking at least a month of coding time into getting acceptable framerates out of OpenGL acceleration, and even after that, some of my games and prototypes simply would not run well on hardware due to heavy use of mutable surfaces.</p>
<p style="padding-left: 35px; padding-top: 5px;">If I had taken a more conservative approach I would have had a much easier time adapting my code to exploit hardware acceleration. In many cases the mutable nature of images wasn&#8217;t useful at all, and I paid a significant price for providing it. Hardware textures had to have software backing stores at all times, and I had to jump through significant hoops to ensure that the two were kept in sync and flagged as &#8216;dirty&#8217; after rendering operations. Copying texture data back and forth between CPU and GPU was an enormous performance killer, especially on older hardware &#8211; hardware that would have otherwise been able to accelerate my engine just fine.</p>
<p style="padding-left: 35px; padding-top: 5px;">I also failed to abstract out my rendering operations in a sane manner until it was too late to do anything about it; the result was that hardware acceleration depended on a convoluted set of per-function &#8216;overrides&#8217; for the entire graphics API, all of which had to be hand-written for each rendering API and tuned to behave similarly to the built-in software implementations. All of the typical issues people are used to from working with virtual functions and inheritance applied here, except it was significantly worse due to the fact that I was implementing it from scratch without a concrete model or understanding of all the implications. Even after significant optimization and refactoring, the overhead this approach created was responsible for making my OpenGL accelerated renderer perform 2-5x worse than it would have otherwise, sometimes even worse than the native software implementation.</p>
<h3>Active Scripting</h3>
<p style="padding-left: 35px; padding-top: 5px;">Microsoft&#8217;s Active Scripting library made it really easy to get up and running, and provided a fairly solid feature set, so it was a good choice for a scripting platform initially. Unfortunately, as the engine matured, Active Scripting started becoming a bad choice. It lacked support for various portions of COM that I hadn&#8217;t realized I would need, and had some severe performance problems that were extremely hard to compensate for. It also had some sneaky bugs and reliability issues that took a long time to track down once discovered.</p>
<p style="padding-left: 35px; padding-top: 5px;">I also initially believed, based on some testing and documentation, that Active Scripting gave me a way to transition to a better language when the need arose &#8211; it was possible to use it to run Ruby, JavaScript or Python scripts instead of VBScript, which gave me a good path for transitioning to another scripting framework in the future if I so desired. In practice, this didn&#8217;t turn out to be true &#8211; there were subtle, crippling issues in the Active Scripting implementations of all three languages, leaving VBScript as the only feasible option. In the end, this miscalculation cost me weeks of time writing workarounds for Active Scripting bugs and trying to get other languages to work as well as VBScript did, and the cost of ending up stuck with VBScript is hard to estimate.</p>
<p style="padding-left: 35px; padding-top: 5px;">The lack of expressiveness that initially made VBScript &#8216;accessible&#8217; to new users and quick to prototype with also ended up being a hindrance. VBScript provided no usable implementations of concepts like delegates, interfaces, inheritance, exception handling, or strongly typed arrays &#8211; all things that don&#8217;t seem particularly important early on in a project, but become increasingly important as your architecture grows more complex and your performance requirements are more demanding. In some cases, the language provided a functioning implementation of a feature, but it was implemented in such a way that it was impossible to use it with native APIs, making it useless in the context of a game engine.</p>
<p style="padding-left: 35px; padding-top: 5px;">The worst example by far was delegates; at first, it seemed that the performance of invoking a script function by name was acceptable, so I built my event-handling model around having a list of event handler names to invoke at runtime. Later I began to realize that every invocation was resulting in expensive name lookups and type conversions, which resulted in severe performance hits when you were having scripts respond to thousands of events per frame. In the end I had to build a fairly complex code generation system to construct &#8216;trampoline&#8217; functions any time I needed to have scripts handle events, which resulted in a small but steady leakage of memory to generated scripts (which couldn&#8217;t be garbage collected or reclaimed).</p>
<p style="padding-left: 35px; padding-top: 5px;">Larger games also suffered from inefficiencies in VBScript&#8217;s runtime, with the cost of individual variable lookups increasing based on the number of variables defined in the entire game, due to a lack of support for usable namespacing or modules.</p>
<h3>Wide feature set and endless &#8216;scope creep&#8217;</h3>
<p style="padding-left: 35px; padding-top: 5px;">From the beginning, I had a habit of allowing the scope of the engine&#8217;s feature set to increase rather than stay fixed, and it took me a very long time to recognize the problems that this caused. By the end of its development, Fury² had nearly 200 unique classes spanning over 100000 lines of VB6 code, with thousands of unique methods and properties. Many of the classes interacted in ways that were unclear or unpredictable, and some of them had decayed into various forms of brokenness due to neglect. Near the end of the project I regularly discovered that I had just reimplemented a function or feature that already existed elsewhere in the codebase, which was a clear sign that things had gotten out of hand.</p>
<p style="padding-left: 35px; padding-top: 5px;">The most representative example was the &#8216;Engine&#8217; class, which would have probably been more appropriately named &#8216;KitchenSink&#8217;. Due to Active Scripting limitations, the Engine class held any and all variables or functions that needed to be accessible at global scope, which resulted in a steady growth of new public variables, properties, and functions, many of which were interdependent or affected the behavior of classes elsewhere in the codebase. Keeping all of the responsibilities and features of the Engine class in my head soon became impossible, and it was far more difficult than I had anticipated to factor out individual responsibilities into unique classes without breaking existing games or degrading performance.</p>
<p style="padding-left: 35px; padding-top: 5px;">Even after aggressive refactoring and elimination of dead code, by the time I put the project to bed, the Fury2Engine class was over 6000 lines of code, containing over a hundred member variables and hundreds of member functions. Almost every class in the framework held a reference to an Engine instance for one reason or another, making it difficult to destroy dead Engine instances to reclaim memory, and leading to subtle bugs where multiple instances were alive at once. Basically any change to a part of Engine was likely to break other parts of the codebase, unless the change involved adding completely new functions and variables (a downward spiral if I&#8217;ve ever seen one.)</p>
<p style="padding-left: 35px; padding-top: 5px;">While the wide feature set meant that it was often possible to build a functioning game prototype with a high level of polish in a few hours, it also meant that it was impossible to build such a prototype without finding at least one bug in the engine (usually more like two or three), and the resulting interruption to my flow almost completely outweighed the benefits I got from rapid prototyping. In many cases I used my engine to build a game for a competition with a short time limit &#8211; as short as 48 or 72 hours to build a game from scratch &#8211; and had a large chunk of content and code complete, only to find a showstopper bug hours before the deadline that ended up taking days to fix. The reasons for this weren&#8217;t immediately clear to me, but over time it became obvious that it was due to the breadth of the codebase.</p>
<h3>Large footprint</h3>
<p style="padding-left: 35px; padding-top: 5px;">As a result of using COM and VB6, the size of my engine&#8217;s binaries alone quickly grew into the megabytes. This was pretty severe &#8211; it meant that merely downloading my engine on a typical connection at the time would take as long as an hour, without even including a game. I went to great lengths to reduce the size of my binaries, by trimming out dependencies, applying code compressors like UPX, and attempting to eliminate as much code as possible from the largest sections of my codebase. In the end, though, the real problem was that the combined overhead of COM and VB6 meant that even simple code resulted in a large binary, and when you applied that overhead to my already large codebase, you ended up with something that was almost impossible to trim down to a reasonable size. For this reason I ended up having to apply UPX to all of my binaries to be able to get them to a size that was remotely small enough for a typical end-user to download, even after halving the size of some of them and eliminating entirely unneeded libraries. Even by the end of the project, the engine and development tools combined totaled around 12 megabytes of executable code &#8211; somewhat large by modern standards, and unreasonably huge by the standards of someone using a 56k modem.</p>
<p style="padding-left: 35px; padding-top: 5px;">The footprint also had less obvious consequences &#8211; the size of the binaries meant that more and more often, I ran into strange bugs in the compiler and debugger, and overall build times grew to the point that going from the IDE to a running version of my engine could take up to a minute. This severely impacted my ability to iterate and track down problems.</p>
<p style="padding-left: 35px; padding-top: 5px;">The worst side effect of this by far, though, was the impact it had on actually using the engine to make games. While the size didn&#8217;t really impact my ability to build a game, it meant that I had to somehow get dozens of megabytes of data onto a user&#8217;s machine before they could play my game. Attempts at reducing the download size typically resulted in compatibility issues, as I discovered that &#8216;optional&#8217; libraries weren&#8217;t so optional because particular OS versions didn&#8217;t include them, or included outdated versions. Troubleshooting these issues was next to impossible since getting a user to download a dozen megabyte installer twice is exponentially more difficult than getting them to download it in the first place &#8211; many of my users gave up and simply did not have time to help me track down their issues, so those issues went unsolved until I could reproduce them on a personal machine &#8211; if I ever solved them at all.</p>
<h3>Instability</h3>
<p style="padding-left: 35px; padding-top: 5px;">One of the most significant disadvantages to using VB6 was the near-complete lack of error handling mechanisms. The only viable options when building a large VB6 application were to hand-write complex error handlers at the bottom of every function, or &#8216;delegate&#8217; errors up the stack using On Error Resume Next. Unfortunately, both approaches are actually worse in practice than they sound on paper &#8211; hand-written error handlers could not easily delegate to common error handler implementations due to language limitations, which meant that they had to be manually copy-pasted throughout the codebase, or machine-generated by automated tools. Neither option was sustainable, which meant that I ended up using On Error Resume Next to effectively ignore errors in any part of the codebase where they weren&#8217;t expected.</p>
<p style="padding-left: 35px; padding-top: 5px;">Even with the most diligent approach to error-checking, ignoring errors by default is not a sustainable way to build an application, and when you&#8217;re trying to build a reusable library or game engine, it&#8217;s outright disastrous. Up until the very last day I stopped working on it, Fury² suffered from crippling, unpredictable stability issues where a minor failure somewhere in the application could result in a catastrophic failure seconds later in an unrelated piece of code, with few clues as to the actual cause of the problem. In many ways, this was as bad as the issues that C/C++ programmers can run into through misuse of pointers and unchecked operations, but I managed to end up with this level of instability despite using a language that offered fairly safe string and array primitives along with null reference checking.</p>
<p style="padding-left: 35px; padding-top: 5px;">With more foresight, the sheer impossibility of handling errors properly in this environment would have resulted in the project coming to a halt and never resuming, or being restarted in another language. Unfortunately, I opted to continue down the path I was on, methodically hunting down bugs one by one, adding thousands of unique error handlers and &#8216;armoring&#8217; my code against unexpected conditions by checking for nulls anywhere I had an object reference and validating the input to any given function. While the resulting approach of &#8216;defensive programming&#8217; did have benefits, it also resulted in significantly slower code and didn&#8217;t help improve the quality of the rest of the codebase &#8211; 1000 input checks don&#8217;t do you any good if you forget one in an important function.</p>
<p style="padding-left: 35px; padding-top: 5px;">This approach to error-checking was worsened by the necessity of integrating with native code written in C/C++ &#8211; while a null reference could cause Visual Basic code to fail to work, the impact on a snippet of C/C++ was far worse: Due to the fact that my native code was being called from Visual Basic, I couldn&#8217;t rely on C++ exceptions and had to resort to return codes, and all of the problems that entailed. Furthermore, in cases where my error handling was faulty or nonexistent, a null object reference in Visual Basic would often transform into catastrophic failures in C++ &#8211; overwriting random parts of memory with garbage, smashing the stack, or simply throwing access violations. All of these failures inevitably brought down the application, some of them even resulting in data loss from unpredictable crashes of long-running editing tool sessions.</p>
<h3>Reinventing the wheel</h3>
<p style="padding-left: 35px; padding-top: 5px;">As mentioned multiple times previously, I ended up having to reimplement many components that I had taken for granted. This included a large portion of the VB6 UI stack, and significant portions of the COM API and the VB6 runtime. This effort cost me a significant amount of time &#8211; many months of engineering work in total &#8211; and resulted in an experience that felt inconsistent and often lacked key features that I could have gotten for free if I had been able to use the stock implementations. Trying to reimplement complex UI primitives like cascading dropdown menus was an exercise in torment, where every bug fixed revealed two more bugs waiting to be fixed, and the sheer fatigue that resulted was one of the main reasons I ended up abandoning the project.</p>
<p style="padding-left: 35px; padding-top: 5px;">Among some the many things I had to reimplement from scratch to solve issues, here are a few of the major ones:</p>
<ul style="padding-left: 30px;">
<li style="padding-left: 30px;"> <strong>Listbox, Toolbar, Menu, Tab Strip, Scrollbar and Button controls</strong> &#8211; because the stock VB6 implementations had severe performance issues, crash bugs, and missing features. Most of these issues were the result of bugs in the VB6 wrapper code that talked to the underlying native APIs, so in some cases I was able to use the native APIs directly &#8211; the longest-lived example of this was the VB6 scrollbar control, which ended up being replaced by a very complex wrapper for the native Win32 scrollbar. In other cases the native APIs were simply too difficult to use, so I had to reimplement them from scratch &#8211; I invested a significant amount of effort into building a good toolbar component, and ultimately it was still not as good as its equivalent in modern toolsets like Windows Forms or SWT.</li>
<li style="padding-left: 30px;"> <strong>COM type library registration, loading, and unregistration </strong>- Yes, you read that correctly. I reimplemented a significant portion of COM and the related portions of the VB6 runtime to work around significant limitations and bugs in the stock implementations. This meant that I had to learn far more about the inner workings of COM and the Windows Registry than I ever wanted to know, and it meant that I had to resort to hooking dozens of native Win32 APIs in order to fool native COM components into thinking that they were running in a normal COM environment. The end result met most of my needs &#8211; I was able to load and unload COM libraries on demand without running as an administrator, and the performance of my COM implementation was generally superior to Microsoft&#8217;s &#8211; but it caused hundreds of issues with COM components that depended on quirky undocumented traits of the Win32 API, or components that were simply written incorrectly. All said, I spent at least a month coding and troubleshooting something that would have been almost completely free if I had been using almost any other comparable toolset.</li>
<li style="padding-left: 30px;"> <strong>Rendering </strong>- I ended up implementing my entire graphics stack from scratch, only using third party libraries in cases where I could absolutely rely on them or where it wasn&#8217;t feasible to implement things myself &#8211; I used GDI or DirectX or OpenGL to render my framebuffer to the screen, and an open source library named Corona to load images &#8211; but everything else, I basically wrote from scratch. I spent an extremely large amount of time discovering the many nuances involved in rendering, and struggling with the Visual C++ compiler trying to get it to turn my C into performant assembly so that my games would run at a solid framerate. Despite the benefits I got from having complete control over my graphics pipeline, in the end I would have been better off learning an API like OpenGL extremely well, and taking advantage of pixel shaders once they were available to get the precise control over rendering that I wanted. While I thought that software rendering would protect me from driver issues and hardware bugs, in practice, it actually didn&#8217;t, and I lost many weeks tracking down obscene bugs in GDI and DirectDraw drivers that made simple things like drawing a framebuffer to the screen produce incorrect results or fail to work completely, often without any error messages or debugging information. A single bug in the DirectDraw drivers for a common Intel chipset cost me over a week of testing and debugging due to the fact that I had no access to a machine with the problem, despite it preventing hundreds of people from being able to run my game.</li>
<li style="padding-left: 30px;"> <strong>Text</strong> &#8211; Despite the fact that VB6 used UCS-2 as its internal string representation, I ended up having to write my own UTF-8 decoder and decoder, along with many other assorted utility functions and my own text rendering/measuring APIs to actually get functioning unicode support so that localized text could be put into a game. This was a huge, mostly unanticipated cost, because VB6 appeared to have top-notch Unicode support on the surface but was actually barely serviceable.</li>
</ul>
<h3>Incomplete and incorrect knowledge</h3>
<p style="padding-left: 35px; padding-top: 5px; padding-bottom: 15px;">I learned a lot of things during the course of the project, but unfortunately, much of the knowledge I gained was incomplete, or subtly incorrect. This is a common problem when self-teaching, and in the case of a project like this, it&#8217;s easy to form bad mental habits that are hard to break out of later. I don&#8217;t think it outweighs the benefits of what I learned, but it definitely poses a continuing problem, as I often have to relearn concepts and techniques that I already believed I understood. If you&#8217;re a particularly opinionated or strong-willed person, this can be especially dangerous as you may never realize how flawed your understanding is, despite the fact that it will cripple your professional career and even your personal life.</p>
<hr />
<p style="padding-top: 10px; padding-bottom: 5px;">Now that you have an idea of the challenges I faced and the mistakes I made, it&#8217;s time to try and share some of my triumphs and hard-won lessons:</p>
<h1>Silver Linings</h1>
<h3>Rapid UI development</h3>
<p style="padding-left: 35px; padding-top: 5px;">Despite all its flaws, VB6 was absolutely the right choice for building the UI for my editing tools. It provided a near-perfect mix of ease of use, simplicity, and ease of integration that let me build highly usable UIs without having to sink time into authoring message handlers, digging through API documentation, or struggling with resource management. I could open the visual basic editor, create a new form, place some simple UI components on it (many of which I had written to abstract out common UI elements, like lists of generic game objects or property grids), and rig up functioning event handlers to them in a matter of minutes. In most cases, the majority of the UI code worked right as soon as I hit save, and the majority of the bugs that did make it into my UI were simple and possible to avoid with enough diligence.</p>
<p style="padding-left: 35px; padding-top: 5px;">VB6 also scaled up suprisingly well as my UI grew more complex. Despite a few crippling issues, for the most part, it performed admirably &#8211; I had hundreds of unique dialogs and tens of thousands of unique event handlers, and most of them did not require any maintenance once they were fully implemented. I was able to reuse an extremely large amount of code between individual dialogs, allowing me to build extremely usable and powerful UI primitives that made it even easier to build more UI in the future. Many pieces of UI that would have otherwise taken weeks of effort to implement from scratch were finished in a matter of hours, leaving me time to spend on user experience, reliability, and performance. To this day, the editing tools that resulted from this have extremely good performance, a smooth workflow, and a robust feature set &#8211; things that weren&#8217;t available in any of the comparable tools at the time.</p>
<h3>Ad-hoc, iterative design</h3>
<p style="padding-left: 35px; padding-top: 5px;">While my initial approach was to design large sections of the engine up-front and then implement them, I quickly shifted to an iterative approach where I designed simple solutions to problems as they arose, and built on them as I continued. I prototyped many of my features in minutes, sometimes even using VBScript to implement them without worrying about static typing or having to compile code. When those prototypes were meeting my needs, it was easy to move the scripts into my codebase and add the necessary static types to compile them to native code, which made for an excellent feedback loop and allowed me to kill many bad ideas without having to spend days implementing them first. Being willing to throw out bad ideas and bad code was much easier because the code took less time to write, and I hadn&#8217;t gotten attached to it. Despite the numerous stability and performance issues I encountered, by the end of the project I had an extremely versatile set of prototyping tools, with complex primitives like functors and mixins that wouldn&#8217;t have otherwise been available to me.</p>
<p style="padding-left: 35px; padding-top: 5px;">In almost every case where I applied this approach intelligently, the end results were great, even without the benefit of a rigid development process, unit testing, or the kind of prototyping tools I now have access to in environments like Python and .NET. Being able to prototype rapidly allowed me to maintain an extremely high level of morale and build a large amount of usable code in a short amount of time, which was essential for maintaining my interest in the project and being able to carry it along to the point that I did. If I had been prototyping in an environment with a longer, more painful edit/compile/test cycle, like C++, I probably would have abandoned the project after months, instead of the nearly 7 years I maintained it for.</p>
<h3>Software rendering and the lowest common denominator</h3>
<p style="padding-left: 35px; padding-top: 5px;">Early on, my decision to use software rendering meant that games and prototypes I built would run the same on any machine (provided they ran at all) without any need for drivers, libraries, or special hardware. I was able to test and demonstrate prototypes on absurdly low-end machines, like a 486 running Windows 95, and then go back and make changes to them using my Windows 2000 or Windows XP machines at home, without seeing any difference in rendering behavior or gameplay. The performance tended to scale badly on the low end, but the games always ran and were often playable on the worst machines if I was careful enough about how I built them, which was something I couldn&#8217;t get at the time using OpenGL or DirectX. As time went on, this benefit grew less and less important, but in the early stages of the project it was essential in allowing me to get important feedback on usability, gameplay, and the overall experience from people who didn&#8217;t have powerful computers or have the necessary hardware to run most PC games, and it minimized the time I spent having to track down bizarre issues with video drivers and video hardware.</p>
<p style="padding-left: 35px; padding-top: 5px;">Software rendering also gave me the ability to produce visuals that were out of the reach of most indie developers at the time, with the ability to combine various blending modes, filters, masks, and transformations to create scenes approaching the level of quality that I could get with layers and filters in photoshop. The effort involved in getting these visuals to run at a decent framerate is hard to overstate, but the end result was also extremely satisfying &#8211; to this date, a few of the rendering techniques I experimented with and used in prototypes have only been used in a scant few games, due the near-impossibility of implementing them on pre-DX9 hardware, and how difficult it is to implement them even on modern hardware.</p>
<p style="padding-left: 35px; padding-top: 5px;">The process of learning how to implement the many graphics primitives and techniques that I had taken for granted was an extremely valuable one as well, and helped shore up the many academic weaknesses that had resulted from my lack of formal math education. Becoming intimately familiar with image processing and the mathematical models behind lighting and other common graphics effects gave me a strong appreciation for the value of math that I would not have had otherwise, and made it easier for me to begin learning more complex techniques and algorithms to apply to hardware rendering and 3D.</p>
<h3>The desire to innovate</h3>
<p style="padding-left: 35px; padding-top: 5px;">I regularly spent weeks or even months in pursuit of an implementation for a wild idea or concept that I had, digging through relevant articles and books, experimenting with formulas, and simply fiddling with things to try and come up with something new or entertaining. As a result, I ended up wandering into areas of computer science, graphics, and other areas like physics and artificial intelligence that I would have never been exposed to otherwise. I literally spent an entire summer attempting to form an understanding of how light and color worked, building a complex lighting model that could run in realtime and produce realistic, smooth-looking colored lighting for 2D scenes. By the end of the summer I had rewritten individual algorithms hundreds of times, and created and abandoned dozens of different lighting models. The process was difficult, but it was also one of the most satisfying things I&#8217;ve ever done.</p>
<p style="padding-left: 35px; padding-top: 5px;">Without the framework I had in place for rapid prototyping, and the willingness to investigate dead-ends in the search for fun new things, I would have missed out on a lot of valuable knowledge and experience. Once I was done, the resulting lighting model ended up being the springboard I needed to build many of the prototypes and game projects I&#8217;m most proud of, and without having spent all of that time in pursuit of something interesting, I wouldn&#8217;t have been able to build those prototypes. Even today, working in a medium-sized engineering team on a somewhat &#8216;normal&#8217; application, the thirst for knowledge and willingness to keep trying to solve a problem that I gained from these efforts are extremely valuable.</p>
<p style="padding-left: 35px; padding-top: 5px;">The downside, of course, is that I didn&#8217;t have time to watch any of those TV shows people were talking about. Is the cast of LOST still on that island?</p>
<h3>Language interoperation</h3>
<p style="padding-left: 35px; padding-top: 5px;">The need to combine tools and libraries written in multiple languages required that I form a deep understanding of the issues involved in getting multiple programming languages and environments to interact. As a result, I ended up digging into various parts of my toolchain and into the inner workings of various language environments. By the end of it, I had acquired a working knowledge of assembly, a fairly in-depth understanding of COM, and a grasp of how complex tools I had previously taken for granted truly were. Debugging issues with stack corruption and library load failures resulted in learning how the Win32 call stack is laid out, how PE executables are organized, and how the OS actually loads libraries and executables &#8211; things I otherwise might not have ever had to know that have ended up being extremely valuable in my professional career.</p>
<p style="padding-left: 35px; padding-top: 5px;">In no small part due to this project, in the span of a decade I went from not knowing anything about computer programming, to being able to write and debug software in dozens of different languages and environments. Being exposed to so many different mindsets and approaches helped counteract the many bad habits that I would have otherwise formed while working on my projects, and made it easier for me to adapt to the often strange and rigid processes I&#8217;ve encountered working on video games and commodity software. Being able to recognize similarities to existing tools and languages is extremely valuable when trying to learn a new skill or tool for a job, and has helped me develop my career without the benefit of a college degree or a significant academic education.</p>
<h3>Collaboration and documentation</h3>
<p style="padding-left: 35px; padding-top: 5px;">As my engine and toolset grew larger and more people became interested in trying them out, I quickly learned the value of collaboration with both programmers and non-programmers, and the importance of good documentation. Maintaining such a large project for such a large span of time, and making it possible for the uninitiated to learn how to use it and build things with it, was quite possibly the largest challenge I faced during the project. As a result, I developed some extremely valuable habits and skills, learning how to document APIs, reproduce bugs, provide workarounds, and track changes. I got better and better at all of these things as time went on, and as my skills improved, the results improved as well.</p>
<p style="padding-left: 35px; padding-top: 5px;">Near the end of the project, multiple people had successfully built playable game demos and prototypes using the engine, despite the subpar documentation, lack of stability, and other numerous problems. The experience of providing them with support when they encountered problems, and helping then understand how to use my tools, helped open my mind to the challenges involved in designing truly useful tools, and the value of open and honest communication with both customers and coworkers. Without them, I would not have been able to accomplish nearly as much as I did, and my personal projects and career would have suffered for it.</p>
<p style="padding-left: 35px; padding-top: 5px;">In retrospect, the people who spent their time trying to decipher my often-cryptic documentation and spent time finding bugs and workarounds were as important to the project as I was, and it was a blessing to have their help. The downside is that none of them were able to complete a game using my tools, but even that taught me a valuable lesson: My personal standards of quality aren&#8217;t enough to produce a quality product &#8211; I need to understand my customers&#8217; needs and listen to *them*, instead of focusing purely on my vision of how things should be. The point of building tools is to improve the quality of others&#8217; lives, and enable them to do things they couldn&#8217;t have done otherwise; if they succeed, you succeed with them, and if they fail, the failure is yours as well. Out of all the lessons I learned, this may be the one I treasure the most.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.luminance.org/blog/code/1986/10/20/fury2-post-mortem/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

