Scientific Ninja
 

Morimoto

2nd Jan 2010

Morimoto, which opened in Philadelphia in 2001, is named after its executive chef Masaharu Morimoto. I had wanted to visit the restaurant for some time. However, as I only return to the east coast once a year, I’d never really had the opportunity.

But this year I was able to secure a reservation for myself, my brother, and six of our good friends. As it turns out, the experience was unforgettable and well worth the wait.

Morimoto’s interior is smoothly modern. Narrow, but deep, the restaurant’s space is flanked by small tables along the walls, while larger booths occupy the middle section, interlocking with each other. The kitchen and bar are in the back of the restaurant, immediately behind one of the few tables the restaurant uses to accommodate big parties like ours. The chest-level interior walls are lit from within by lights that slowly rotate through a spectrum of colors.

It took time to peruse the menu and decide which of the tempting options to choose. I finally settled on the pork belly and the wagyu, with the “rogue beer flight” as my beverage.

Our food arrived staggered; some of us ordered apps or sushi, while others did not. The portions were such that most of us got to at least taste some of everything ordered, and somebody was always on-hand to finish off a dish that somebody didn’t have room for. Highlights included the beautifully-plated lobster ceviche salad, a delicious soba carbonara (soba noodles, bacon and scallops, white truffle oil) and a duck entree called “Duck Duck Duck.”

There were four beers in the sampler, each signature brews unique to the restaurant and ranging from a pilsner to a very dark, chocolatey ale. My favorite was the second-to-darkest — it was either the soba or hazelnut ale.

The pork belly was braised for ten hours and served on a bed of white rice porridge. The meat was extremely tender, and sauced with something that gave it a slightly sweet edge. It played well against the sharper, almost acidic tang of the porridge.

My entree was an eight-ounce cut of wagyu; extensively marbled beef, perfectly cooked. It was served with three accompaniments: a spicy ponzu sauce, a kind of Japanese barbecue sauce, and some wonderfully coarse crystals of Japanese sea salt. There was a side of pan-fried potatoes and a small salad, to help counteract the extreme richness of the beef. This steak was richer and more tender than anything I’ve ever had before. There was a texture to it, which I suppose comes from the fat content and provenance of the meat, that was quite unique.

Downsides? Well, it was crowded and as a result, a touch loud. There were at least two areas I saw available for more private dining experiences, however. I’ll try and get seated there when I go back — and I will absolutely go back: this was one of the best dining experiences of my life.

~ 1 comment ~

Rethinking the SlimDX Sample Framework

16th Jun 2009

The SlimDX sample situation is currently pretty disorganized: some of our samples are checked in to the Subversion repository, but others are just referenced from issues in our issue tracker. Not all the samples use our internal sample framework, either, and the framework itself has a few issues. But as SlimDX becomes more and more stable, and begins to really approach complete parity with the native APIs, we have an opportunity to revisit problems like this one and clean things up.

In the future, our intention is to create a firm split between official and community samples. Only the former will be checked into source control. The community samples will be hosted on our website somewhere, and when warranted we will fold appropriate community samples into the official distribution, after appropriate modification. One of the things we want to ensure is that the official samples have a uniform style (to make them easy to follow and compare) and that they meet certain quality criteria.

The other thing we’re going to do is refactor the sample framework itself. The current framework feels like a “mini-XNA,” which has caused some users to adopt it as a more generalized “game” framework, which is problematic for us. We do not want to be in the business of writing — more importantly, of maintaining — anything resembling an engine. There are other projects out there that fit that bill; SlimDX is a lightweight wrapper, and we want to preserve its minimalist ideals even across aspects of the project that are not the core library itself.

The sample framework should be about making it easy for us to build samples. The native SDK uses a pretty robust sample framework, and we’ve seen that hamper it as an educational tool. When the sample is trying to demonstrate some concept or technique, it should demonstrate that in the language of the API itself. API calls should be used as often as is reasonable in order to demonstrate ‘the simplest thing that could work,’ which is usually what a user is looking at the sample code for.

Basically, we want a relative lack of abstraction. Users are just going to peel away the abstraction layers anyway, so we’d just be creating more work for them.

It’s still going to be a framework, though! We’re still going to wrap the creation of the primary window and the ‘game loop,’ and we’ll provide extensibility points allowing a sample to customize the main phases of application execution: pre-initialization configuration, initialization and resource load, logic update, render update, and shutdown/cleanup. Simplifying such boilerplate code, code that is not directly related to the primary tasks one would use SlimDX for, is perfectly acceptable.

Similarly, we’ll probably wrap mesh loading (especially since there is no built-in method for creating ID3DX10Mesh objects from a file, and we’d like to use the same source assets when possible for homogeneity) and similar ancillary operations.

UI is another interesting issue. The native sample framework supports a simple collection of UI controls for tweaking parameters for a sample. I’d rather not implement (and thus maintain) a similar system in SlimDX, but the alternative of using Windows Forms controls means that tweaking won’t be available in samples running in fullscreen mode, which is a deal-breaker. So we’ll support a small set of basic controls useful for tweaking parameter values in the new framework as well.

These framework changes probably won’t make it into the next release (if indeed the next release is this month, as we suspect), but you should see them in the release immediately following that at the latest… as well as some other interesting changes. And of course, you can always live on the bleeding edge and work from the head of our repository.

And now a word from the marketing department…

In other SlimDX news, Arcen Games, LLC recently released AI War: Fleet Command. I’ve posted about this on Twitter already, and SlimDX lead Promit Roy has beaten me to the punch on a blog post, but I’ll repeat it here for the benefit of other readers: AI War is the first game available for purchase that was built using SlimDX! This is very cool. Developer Chris Park has written about his experience evaluating rendering solutions for C# projects, explaining how he ultimately chose SlimDX… head over and give his blog a read, and when you’re done, be sure to check out the game itself!

~ leave a comment ~

M is for “Massive”

15th Mar 2009

Hobbyist developers don’t make MMOs.

Which isn’t to say they don’t try, or don’t think they are. But more often than not, they are simply misappropriating the terminology because they think it sounds cool, because it will attract more attention to their project. This is often true, although the attention they end up cultivating is typically the negative kind.

Here’s the thing: that first “M” in the acronym “MMO” stands for “massively.” That means the expected player base of the game is large. Not fifty players. Not a hundred players. Multiple hundreds of players is a bit closer to the mark, and ideally you’d want a number somewhere in the four-digit range. Building a game with the capability to support peak concurrency volumes like that involves two things that differentiate such games from others. First, it requires the technical acumen to build a network infrastructure — which consists of both software and hardware — that can scale appropriately. Second, it requires actual people to actually connect and play the game.

The complexity curve to achieve the first point, the technical scalability, isn’t linear. MMOs are involved… there are client programs, server programs (often multiple types… login and authentication, backups, core gameplay, tournaments and player-versus-player arenas, economics, web portals), physical machines that run those servers, and IT and security glue linking everything together and guarding against thirteen-year-olds with too much time on their hands.

It is extremely unlikely that any given hobby developer will have had the requisite experience with the problem domain to build all that out correctly — not the first time, at least. Because of that, their game will evolve in painful, halting stutter-steps. The first hurdle might be around the hundred-player mark. Perhaps it will be because the server is persisting character data too frequently, causing the database save to bottleneck and become unstable. Perhaps it will be because the core networking layer is using a naive mechanism for handling clients, and starts to thrash trying to service all the pending and active connections. It could be for any number of reasons, but something will break. It will need to be fixed, and then eventually something else will break, and so on and so forth. With time, dedication, and a healthy measure of elbow grease, one can end up with an MMO this way. It’s been done before, although it’s uncommon. Of course, this is assuming the players stick around through all the trials.

Which brings us to the real meat of the issue: the players. Getting players is tough for hobby and indie games, because they are competing for eyeballs that are far more likely to go to big commercial titles with huge publisher-backed budgets and similarly turgid marketing campaigns. Player base retention is tricky as well — typically this isn’t given as much thought, since the important thing for financial success is to dazzle a player enough to cause the cash to fly swiftly from the wallet, after which they are not given much thought. This doesn’t work in the long-term, of course, and it certainly doesn’t work in the MMO space, which is currently dominated by the monthly-fee culture. You’ve got to really keep players engaged to keep them coming back, especially if they are paying for the privledge and especially if your game is undergoing those inevitable growing pains discussed above.

The grim truth of the matter is that most hobby games will never achieve nearly enough players to actually scale and stress the system enough to be considered an MMO on the basis of that first, critical, “M.”

But then again, does it matter? There are generally two kinds of person who want to make an MMO as a hobby project: the guy with a lot of ambition and not a lot of grounding in reality who really does want to make the next World of Warcraft where you can participate in a massive online world in which you can do anything, even marry The Hulk, and the guy who really just wants to make a cute little persistant world of his own imagining that he can log in to and fool around in with his buddies. There is no hope for the former — and regardless, if they manage to get anything off the ground, it will end up being the latter kind of game anyway. The latter’s goals, though, are actually quite achievable — yes, they are misusing terminology by calling their projects MMOs, and they should be gently corrected. But there is no reason to jump down their throats, telling them how impossible their projects are and how they will never, ever have any success.

So the next time you see some poor newbie on your favorite game development forums or IRC chat asking for help on something related to an “MMO,” try to get a bead on just what it is exactly that is being built before flying off the handle. Jumping to conclusions, bashing them, failing to provide any kind of assistance, correction, or guidance — that’s just a disservice to entire hobbyist game development ecosystem.

~ leave a comment ~

COM and SlimDX, Part 2

17th Mar 2008

Earlier, in “COM and SlimDX, Part 1,” I discussed an early design decision that was made in SlimDX that prevented us from supporting the idiomatic C# pattern for disposal of unmanaged resources. In closing, I mentioned that our new solution, while a huge leap forward, still had some problems.

Today we’ll discuss them.

SlimDX supports the ability to share — in a sense — the native objects of other APIs using System.IntPtr, an opaque managed representation of a pointer or handle. All SlimDX COM objects expose their native pointer as an IntPtr. They can also be constructed from IntPtrs.

With the old SlimDX design, we only ran into two serious issues with this process. The first was that since IntPtrs are entirely opaque, we’d more-or-less trust the user to construct the correct type and pass the correct pointer. Sure, we performed a QueryInterface(), but that may not have been the best idea in retrospect — there are known scenarios (for example, see here and here) where DirectX objects don’t behave the way you might expect a COM interface to behave — and so our code might have occasionally failed in situations it should not have.

The second issue was that we used these IntPtr constructors internally, occasionally in scenarios where it was impractical to know the proper type to construct. Remember the GetDevice example from last time? In Direct3D10, there’s a similar method for resource views (GetResource()) that is used to get a pointer to the resource that the view object is looking at. The method is a member of the ID3D10View base class, so naturally we placed it in the analagous SlimDX.Direct3D10.ResourceView class. The implementation would have looked something like:

Resource^ ResourceView::GetResource()
{
  ID3D10Resource* resource = 0;
  NativeComInterface->GetResource( &resource );
  return gcnew Resource( IntPtr( resource ) ); 
}

The problem with this approach is that it logically “slices” the object. This is similar to slicing in, say, C++ except that the underlying object is not physical destroyed, only the way you can interact with that object reference. In other words, even if the ID3D10Resource* returned from the native call pointed to a derived ID3D10Texture2D, it would be an error to downcast the managed Resource reference to a managed Texture2D reference, because we statically constructed a Resource, not a Texture2D.

A solution to this is a factory-type construction process — fortunately for us, most of the types involved here had base class methods that would indicate the actual runtime type of the object somehow — even though that would mean we’d have to push most implementation of the method lower in the class hierarchy. That was the solution we were considering, until the object table was brought up, which neatly solved the whole issue for us.

Almost.

You see, there’s one final caveat. The reason the object table eliminates the need for a factory is that it stores the native pointer as the key, and the actual runtime managed instance as the value. Thus, the aforementioned code snippet becomes a table lookup instead of a gcnew statement. The problem rears its ugly little head when you consider the possibility that the table lookup can fail to find the key. At first that might seem impossible (and thus not worth handling) because the view can’t be created with a resource, and given the existence of the object table, the only way the resource can cease to exist before the view is if the programmer intentionally disposed of it. This would be a programmer error, which is generally something we do not concern ourselves with handling gracefully (that is, we’d throw an exception indicating the programmer is a dim bulb, rather than try to bravely soldier on).

Unfortunately, since SlimDX can exchange native resources with other APIs via IntPtr, it’s possible to get into this sticky situation rather easily — simply construct a new ResourceView using an exposed IntPtr. SlimDX will correctly handle the ResourceView, incrementing the native reference count and placing an entry in the object table. But SlimDX won’t know about the original resource, so if you call GetResource() on the view, SlimDX will not find the Resource’s key in the table. What will happen, in that case, is that SlimDX will treat the object as a new one, increment the native reference count, and add an object table entry. This is unfortunate because the user now has to Dispose() of that resource, even though they didn’t explictly allocate it with new.

We’re currently considering this a pathological use case, and aren’t addressing the issue. Users doing that kind of advanced interop with SlimDX will simply need to be aware of the problem and the non-standard call to Dispose() they’ll need to make. I do believe it is a fixable issue, but the fix isn’t in the March release since it will not adversely affect the majority of our user base — you’ll have to wait for June, if we’re even able to remedy it all. In the interim, however, it’s important to be aware of the dangers that lurk in the shady corners of the API.

~ leave a comment ~

COM and SlimDX, Part 1

28th Feb 2008

The last few months have been fairly busy, and a good portion of that is due to the upcoming SlimDX March release, which we expect to package and ship shortly after the official March DirectX SDK update becomes available. Since the DirectX team changed up their release schedule, there’s been a larger-than-usual gap in between releases, and we’ve taken advantage of that extra time to make some fairly major changes to SlimDX. There’s one issue of particular signifigance that I wanted to talk about in detail, since it’s important.

SlimDX is a managed wrapper for DirectX and other libraries. It’s written in C++/CLI, an offshoot of C++ whose niche is interoperability between managed and unmanaged code. Bridging that divide is never quite as simple as it looks, and SlimDX has certainly been no exception to that rule, even though it’s a relatively thin layer over the native APIs. One of the trickiest problems we’ve had to solve is how to to get SlimDX code to play nice on both sides of the fence: the managed side of the library needs to expose interfaces and semantics consistent with those of .NET, and the unmanaged side of the library needs to play by the COM rules that govern most of the native APIs we wrap.

In the COM world, the lifetime of objects are managed through reference counting. When you acquire a pointer to a COM interface, you call its AddRef() method, incrementing its reference count. When you’re done, you call Release(), which decrements the count and destroys the object if no other references exist. In languages like C++, where DirectX is most commonly used, you have rich support for the RAII pattern, which leads to smart pointer implementations such as CComPtr.

But C# doesn’t support automatic, scope-based, deterministic destruction. So, while incrementing the reference count correctly is a no-brainer, correctly decrementing it when the user is done with the object is a problem. C# has finalization, which is an entirely automatic, but nondeterministic, process for cleaning up managed resources. The framework also offers the IDisposable pattern and an idiomatic implementation thereof, which is intended for manual cleanup of unmanaged resources the garbage collector is not aware of: file handles, GDI handles, and in our case, COM objects.

Since the COM interface represents an unmanaged resource that is beyond the control of the runtime’s garbage collector, implementing IDisposable is an obvious step. The idiom for IDisposable in C# stipulates that a disposable object should also implement a finalizer (even though this is, strictly speaking, optional) to call Dispose() on the off chance the user fails to do so, ensuring that the unmanaged resources will at least get freed up eventually rather than leak.

This, however, causes trouble for SlimDX. Finalization, you see, is performed in its own thread, which means that the Direct3D runtime will emit warnings in debug mode if the underlying Direct3D device was not created with appropriate flag for enabling multithreading. Allowing those diagnostics to appear is against our design guidelines, and forcing device creation to be multithreaded is a heavy-handed and boorish solution, especially since the flag has performance implications.

This effectively makes finalization completely off-limits to SlimDX, since we can’t know at compile-time if we’re allowed to release resources from other threads. In fact, SlimDX’s finalizers do absolutely nothing.

Part of the semantics of IDisposable are that object creation (via new) and disposal should exist as a pair. Additionally, the client should only expect to have to call Dispose() on objects the client has explicitly created. SlimDX’s original design made that impossible; to clean up SlimDX resources correctly, you needed to call Dispose() on every single reference to an object you ever got from SlimDX.

Recall that in Direct3D, most resources are associated with the device object that created them, and you can at any time ask the resource to return a pointer to its device. If SlimDX implemented IDisposable in the traditional way, you would only need one call to Dispose() for the device object; ideally that call would be made from the same context that originally created the device in the first place. But, as I’ve said, SlimDX did things a bit differently — not only did you need to dispose of the original device, you also needed to dispose of the device reference given to you by the buffer’s GetDevice() method, even though they were both references to the same object. We initially chose this design because it is as similar to the native semantics as possible — each new reference to an object incremented the reference count, and thus required a Dispose() call to decrement the reference count.

Another annoying side-effect of this design was that SlimDX objects rarely compared equal, even if they represented the same native object. In other words, foo.GetBuffer() == foo.GetBuffer() was never true.

For a while, we simply lived with the strange disposal model. But as we started wrapping up open issues for the March release, it became painfully obvious we were going to have to take some drastic measures and refactor large portions of SlimDX’s internals, because the designs were making the library extremely difficult to use.

So, after a number of involved design discussions in #gamedev, we came up with a new design for the March release. This time around, we’d use an internal hash table to track the native objects and map them to an existing SlimDX object. When a new object is needed (for example, when GetDevice() is called), we first look in our hash table to see if we already know about the native object in question. If we do, we return the existing managed wrapping object, otherwise we create a new one and store the mapping in the table.

Additionally, we manually call Release() on native objects whenever the underlying DirectX API implicitly calls AddRef() — in effect, we ensure SlimDX’s only reference to the native object is the one stored in the hash table. You can call GetDevice() as many times as you like, and it will always return the same reference and the net COM reference count will remain the same after the calls as it was before. As a result, calling Dispose() on the object returned from GetDevice() is unneccessary — the only reference that needs to be disposed of is the first, exactly the way IDisposable is supposed to work.

Unfortunately, while this new design is a huge step forward for SlimDX in terms of correctness and consistency on the managed side, there are still a handful of pitfalls. Thankfully, those pitfalls exist largely in edge cases that will affect a very small percentage of SlimDX users, as opposed to the original problem, which affected every SlimDX user. I’ll talk more about what those problems are next time.

~ leave a comment ~

Musings on Visual Shader Tools

30th Nov 2007

Recently, I started working on a visual shader construction tool; something along the lines of the Unreal material editor, the now-unavailable ShaderWorks, and the visual render path tools present in modeling packages like Maya and Blender. I also borrowed heavily from some concepts presented in Morgan McGuire’s “Abstract Shade Trees” paper.

Such tools are uncommon. Unreal’s tool is available only to those with an Unreal Engine license; other studios, such as the Project Offset team, Activision, and Snowblind Studios have tools that are proprietary. McGuire’s paper does not provide a demonstration application. Essentially, there are few such tools available to the public, although they are becoming more prevalent (FX Composer 2, for example, now includes such a tool).

My tool, which I’ve oh-so-creatively dubbed “Shade,” is more-or-less functional, although not exactly ready for public consumption. I still feel it’s an open question whether or not these kinds of tools are actually relevant or useful. The user interface for a tool like this involves, primarily, a directed acyclic graph; nodes in the graph represent operations, elements or states, and edges represent connections or dependencies between nodes. The actual execution path of the shader would be the topological ordering of the graph nodes. The crux, I think, is how the tool uses the nodes — where those nodes fit within the abstraction continuum between low and high level operations.

A low-level approach would have each node represent an operation (add, multiply, lerp, invert, et cetera). Connections between nodes represent the flow of data from one operation to the next. In my opinion, this is far too low-level to be of any use to anybody. There is no benefit to programmers, because they could probably type the code directly in a fraction of the time. Nor is there a benefit to non-programmers; while the tool is somewhat more accessible due to its visual nature, the user still essentially writing a program and the “programmer’s mindset” is still required to make the most of the tool.

Yet at the other end of the spectum is the completly high-level approach, where nodes represent large, complex features of a shader. While this approach is probably easier to work with, it suffers from the fact that the abstraction is probably too high-level; so much so that there is little practical purpose in placing nodes in the graph in any arbitrary order. Only a handful of placements would produce a useful looking shader, and so a more efficient GUI for such a system would probably be a simple list box. The successful compromise, hopefully (as it’s what I’ve bet my implementation on) lies in giving the tool the flexibility to scale the abstraction of the nodes so that the graphics programmers can tailor the available node set to the style of requests they get from the art team.

A typical workflow bottleneck between artist and programmer manifests itself in the form of an artist’s request to a programmer for a new or modified shading effect. Usually the artist will be prevented from completing whatever he was originally working on until the programmer can implement the effect, and the programmer, obviously, must take time away from his current task to write the shader. Furthermore, implementation of the effect may require tedious and time-consuming iteration, even after the artist has initially signed off on the implementation.

An artist’s request tends to be of the form, “I’d like to put our snow shader on our new unit, but we want all units to self-shadow and the snow shader doesn’t support that, we need a new shader,” or “It would be cool if the building’s glow effect was tinted by its team color, and maybe we could have the intensity of the glow vary base on the viewing direction.” We can break these down into required features (snow, self-shadowing, glow, tinting, and Fresnel term) and relations among those features (apply self-shadowing to the snow, use team color as the tint input, et cetera).

The features themselves would be implemented by a graphics programmer using actual shader code. The key would be to provide flexibility in the way the tool handled the implementation of these features so that the programmer could, as mentioned before, tailor them to the language and abstraction level used by the team. This isn’t something that can be automated, really, and would definitely require some practice and false-starts to get right. The success or failure of the visual shader tool probably depends on whether or not the programmers can effectively use the tool to create the correct set of nodes for their project.

The artists would use the tool to define the relationships between nodes that the programmers have written. The workflow optimization would be when the majority of requests tended towards alterations of existing feature interactions (as typically happens later in the development process when art production begins to ramp up); at this point the artists would be reusing more of the existing feature nodes and would need to make trips down the programmers less often. The blocking artist-programmer interaction would only occur when an artist needs a completely new feature not currently supported by the engine.

By allowing the artist to experiment directly with shaders, instead of interacting with and waiting for the programmer, the impact to his workflow is minimal, which should translate to an overall minimization of the development time of a new effect; given the amount of the content generation required for modern game development, this can be a huge time saver. Of course, it should be clear such a tool benefits artists much more than programmers. Programmers would be getting the benefit of a more streamlined workflow (due to less frequent requests for shader development), of course, but they aren’t neccessarily getting any kind of improvements to the way they write shader code. But that shouldn’t be too big of a deal, since programmers are comfortable writing code and can do it efficiently.

It’s difficult for me to decide if these sorts of tools have a future. So much of their utility seems to rest on getting the node set correct, which is a delicate balancing act. A poorly constructed node collection can severely limit the usable combinations available, making experimentation with the node graph an interesting toy, but not neccessarily an effective tool for game development. It may very be that these kinds of tools aren’t popular because they just aren’t practical. It might also be that they’re simply hard to get right.

~ leave a comment ~

On Game Schools

27th Sep 2007

A “game school” is a learning institution that offers (exclusively or not) a degree- or certificate-granting program geared towards the formal study of something related to video games. What exactly this “something” is varies from school to school — sometimes its art, sometimes it’s some vague “design” focus, and sometimes there might even be some programming involved. My concern here is with the kind that covers programming – Full Sail in Florida and DigiPen Institute of Technology in Washington are two of the largest and most well-known of this particular breed. Of the entire collection of game schools, they’re probably among the most credible.

These schools are pretty popular candidates for kids searching for colleges, and it’s easy to see why. Game development has an appeal to it that conventional software development just… doesn’t. This makes a program that professes to teach you how to be a professional game developer sound pretty darn attractive. Alas, electing to attend one of these schools isn’t always the best choice. Before we go much further, I want to point something out – both in the interests of full disclosure and for the purposes of establishing some credibility on the subject. I’m not speaking from an external perspective here. I started college as a computer science major at Drexel University and then, having discovered after a number of semesters that the school wasn’t to my liking, transferred to DigiPen. I’ve also worked with, interviewed, spoken with or otherwise interacted with students, graduates and employees of most of the game schools out there.

Game development is just an extension of software development – games are, after all, a specialized kind of software. It follows, then, that in order to excel at game development, you should also excel at software development in general. This is the crux of my biggest problem with game schools. They focus very heavily on the games-specific aspect of the process, often neglecting huge swaths of knowledge, techniques and best-practices from the traditional, fundamental grounding of software development; since the programs are often so specific and focused, you also lose a lot of general education course and course options that can help shape you in a well-rounded individual with a strong ability for problem solving and for thinking on your feet. It’s an education focused on the goal, not on the journey. This is a shame, because the journey is where the real experience is.

Attending a game school can thus leave you with pretty big gaps in your education and skill set relative to your peers with their traditional computer science or engineering degrees. This might even cost you a job somewhere. It follows, then, that you’ll want to get a firm grounding in both the tradional techniques of computer science that form the basis of your craft, and of the particular application of those techniques in the domain of games. With that in mind, consider which of those two bodies of knowledge you’ll likely have more interest in studying on your own. Nine out of ten people will tell me that learning how to make games on their own a more attractive option. Those people will also usually agree that it’s much easier to discipline yourself to write games in your off-hours than it is to learn what order notation or lambda calculus is really all about. The logical conclusion then, is that these people should probably be considering a traditional computer science (or similar) degree, and should study game development as a hobby. Besides, it’s pretty hard to effectively apply the craft to making games if you’re still learning what exactly the craft is all about.

This brings me to another issue – occasionally, people want to go to a game school because they think they’ll be able avoid all those scary computer science topics. I find myself wondering what these sorts of people think game development is really like. Did they really buy those commercials about tightening up the graphics on level three? Game development is software development, my friends. You can close your eyes, stick your fingers in your ears and shout all you like, but that isn’t going to change reality. If you’re a poor software developer – and you probably will be, if you don’t learn what all the other software developers learn – you’re going to be a poor game developer. You can’t be in it just because you love the industry. You need to love the industry, of course – if you are unfortunate enough to work on a project with serious crunch time, you really need to love the industry. But you have to want to be a part of what the industry really is, and to do that you have to know what the industry really is.

Now, some people will be capable of bridging the gaps left by a game school, but not everybody. It takes some considerable discipline to force yourself to study a subject you don’t have to study; one that you might not even think is useful. Sometimes the danger is in the very fact that, since you’re essentially teaching yourself, you don’t even know what you don’t know or what you should be looking in to. I do think that game schools are right for some people – I’ll touch on what I felt were some of the positive aspects of my DigiPen education below – but I think the majority of people should be practicing game development as a hobby while they complete a good, traditional educational program. There’s another benefit to be had from that choice, as well: remember how I said that most game schools are so narrowly focused that they leave little room for elective coursework outside the scope of games? Cross-curricular courses make you a well-rounded person, and thus more attractive to a lot of employers – when you have a varied background, you add diversity to the skill pool of the studio, and you’re more interesting to interact with, professionally and around the water cooler. If you’re one-dimensional, if you’re one of those foaming-at-the-mouth-oh-my-God-I-live-and-breathe-games types, nobody’s going to hire you. You’re creepy, get a life.

While working on this article, Ravuya raised an excellent point that I want to share. The original context was an IM conversation, so I’ve taken the liberty of removing my useless interjections and paraphrasing somewhat. I’m fairly sure he won’t sue me:

It’s more like an education driven by the love of the industry, not even the goal. The students are in love with the cultural artifacts of the industry (Nintendo, et cetera) and want to emulate it, not improve it. The schools’ practices are outmoded, but they refuse to adjust or augment them out of fear of losing the ‘magic’ that they’re emulating, since they’re so in love with the industry itself, and not their sanity, development talent or free time. They almost slavishly refuse to go a ‘different way’ unless forced to do so by the industry. The problem with vocational schools in general is that they hire so many people with industry experience they have no appreciation for what other industries can teach.

He managed to articulate a concept that I was having trouble figuring out how to put into words myself; I’m especially fond of the last point. The practice of having former or current professionals teach is usually regarded as a plus, something with a positive impact — but we don’t often consider the negative impact that recirculation of knowledge can have, as well. Many game schools are overly concerned with producing graduates that will maintain the status quo — although this the case everywhere, as Ravuya continues, “in a lot of ways, computer science education is the same way — it can’t change academia because it’s focused on being academia, so a lot of massive failures stay in academia to screw up the next generation of students.” Sure, there’s the lecture on quality of life and crunch time now and again, but this isn’t really drilling to the heart of matter. Most such schools have students band together in teams and build games over a lengthy period of time (usually a semester or a year). These projects usual suffer from the same failures in estimation, planning, and infastructure that larger, real-world projects will suffer from, and yet the reward (the grade) is still based on the finished project, and not the road the team took to get there. It’s a great little microcosm of the industry, but it’s not helping anybody become better software developers.

On balance, though, game schools aren’t all bad. My goal here isn’t to give you the impression that game schools are worthless scams (although some of them are; you can usually tell because they have TV spots). My goal here is to make sure you understand what you’re getting into. It’s shocking how many people show up at DigiPen on the first day of classes, only to fail out within a year or so. It’s not because DigiPen’s curriculum is particularly difficult – it’s no more or less work than most. No, they fail because they didn’t know what they were doing. They were so enamored with the idea of making games that they couldn’t, or didn’t even try, to see DigiPen’s program for what it was: a four year computer science degree that’s very light on the computer science and very heavy on the practical application of programming and math as it relates to games. Most of those drop-outs thought they’d be studying to be game designers, sitting around all day discussing how to balance their dream MMORPG or designing levels for Counter-Strike: Source, or something. Don’t make the same mistake.

But I promised I’d leave you with some positive points regarding my experiences at DigiPen, so here they are, in closing.

DigiPen, at least, has a really good computer graphics program. If you’re interested in graphics, this is a definite plus, as most other schools don’t provide very many computer graphics options to undergraduates. DigiPen has courses dedicated to the fundamentals of modern graphics programming theory (as in, what goes on beneath the sheets that DirectX or OpenGL give you). They also have a number of upper-levels electives in the area, some stuff that other schools only offer as part of their MSc or PhD programs.

At game schools, you do tend to get experience on projects of non-trivial size. Now, you want to be careful of the Doghouse Analogy here: you’re not building large projects, in fact you’re probably only barely approaching the size and scope of some small casual game projects. Nonetheless, the projects are usually much larger, and longer, than those you’d be doing at a traditional school, and you also get some good experience working with teams. That alone can go a long way, because so many people getting into computer science or game development don’t have the greatest social and communications skills.

Finally — perhaps, most importantly — you get a nice, ready-baked collection of industry contacts when you graduate. Game schools have more networking opportunities than traditional schools, in terms of meeting people in the industry itself. Plus, you’ve got all your friends and former teammates who will graduate with you. They’ll get jobs at various studios, and you’ll have a nice network all set up. It’s a big head-start over what somebody graduating from a traditional school might have.

But you’ll notice that none of those points have to much do with being a good software developer, or even (with exception of the graphics programming) with the quality of the education, and for the most part they’re all achievable anywhere you go to school, as long as you have the motivation.

~ leave a comment ~

Write Games, Not Engines

30th Aug 2007

To begin with, the term “engine” (specifically as it related to the game development world) has no strict definition. Therefore, in the interests of keeping everybody on the same page, I’ll define the term as I intend to use it in this article. An “engine” is a collection of robust, reusable software subsystems (possibly including both code libraries and tools) designed to facilitate the development of actual games by addressing specific requirements. The requirements tend to be broadly-defined: rendering, audio, physics, et cetera. Particularly ambitious engines that address multiple broad requirement groups tend be to known as “game engines” rather than just “graphics engines” or “physics engines.”

Now that that’s out of the way, let’s turn to the real issue: how to build engines, and more specifically, if you should even bother.

For any number of reasons, many neophyte game developers (and even some moderately experienced ones) seem to think that a game engine is a required, critical part of a game. They think that — in keeping with the mechanical analogy from which the term “engine” comes from in the first place — like a car, a game with no engine will simply not run. But that mechanical analogy starts to break down when you take it this far, because when you look at the reality of the situation, a game engine is about reusable components. Much like you can build a car with custom parts or generic ones, you can build a game with custom components or reusable ones. The fact that you must have an engine to build a non-trivial game is a fallacy, something perpetuated largely by people who don’t know any better.

Nonetheless, every so often comes the obligatory post on GDNet by somebody who has decided its time for them to write their engine, so they can start making some real games. These posts usually consist of requests for resources about the engine design and development process, and in fact chances are that if you’re reading this, it’s because I caught you making just such a post and linked you here so that I could crush your dreams with a minimum of effort on my part.

So my advice to you, if you’re trying to write an engine, is: Don’t. No matter what your reasons are — it doesn’t matter if you’re writing an engine so you can write your dream game, or if you’re writing an engine because you think it will be a good learning experience, or any number of similar reasons. They’re all wastes of time. You can sit down and write a game without writing a pre-written engine, and in fact this is very often the better approach, regardless of why you want to write an engine. The entire development process goes much more smoothly if you are focused on writing a game instead: a game is much easier to identify requirements for, much narrower in focused, much more rewarding when finished, and much, much more useful.

Most hobby developers who “finish” an “engine” that was designed and built in isolation (with the goal of having an engine, not a game, upon completion) can’t ever actually use it, and neither can anybody else. Since the project didn’t have any goals, any boundaries, or any tangible applications, it essentially attempted to solve aspect of the chosen problem space and consequently failed miserably. These “engines” tend to be little more than an inconsistent and messy amalgamation of random functionality that isn’t terribly practical, robust or reusable. Furthermore, the actual viewable end product of these “engines” tend to be a cute little map (loaded from one of the popular BSP formats) you can run around in and observe some cute little models (also loaded from a popular model format) rendered with some fairly basic lighting effects (maybe some per-pixel normal mapping or parallax mapping). Maybe you might even see some collision detection or animation. These projects are just tech demos, and not particularly compelling ones at that. There is nothing to them, and they feel rather flimsy and inflexible.

The solution, even if you really want to make an engine, is to make a game instead. I can hear you crying out in protest from here, but just bear with me for a minute. The game does not have to be an epic production. It does not have to be GTA5, Quake 17, the next Elder Scrolls game, or a WoW-killer. It just has to be a game, with well-defined (and ideally well-thought-out) gameplay and well-defined developmental scope. A game is much easier to identify requirements for, as I’ve already mentioned, and it is also a practical application of those requirements. Once you have made one game, make another. Then another. Each time you start a new project, you should identify functionality that could be used and pull it out into a common library of base code. You’ll probably have to refactor some of your code to remove explicit dependencies on other code or data that is game specific, but this is a good thing. It will help you generalize what is generalizable in a way that you can still test the generalized functionality against your finished game and confirm that it still works (obviously you might have to modify some game-specific code to adapt to the increased generality, as well).

If you repeat this process long enough, after a few games you’ll have the beginnings of a solid collection of reusable functionality that has been proven to have practical applications. You’ll also have a set of far more interesting demonstration applications that could also double as test harnesses for your engine. This method of growing an engine (rather than manufacturing it from whole cloth) is superior because it helps you shape your goals, it forces you to actually think about the problems you will face and their practical implementations, it forces you to think about the separation between the domain-specific logic of the engine and the game. It makes you learn from your mistakes rather than pretend that mistakes cannot be made.

~ 3 comments ~

The Dangers of Benchmarks

29th Aug 2007

Jack and Jill run a race. They’re both the same age, wearing the same track shoes and other similar running attire. The weather is favorable — a pleasant, windless summer afternoon, and they’re running on that fancy red rubberized track. Jill soundly beats Jack by a margin of about half a second. Therefore, girls are faster than boys.

Obviously there’s a problem here. The race was as fair and controlled as it could be, and the results indisputable. The conclusion drawn from those results, however, is obviously bogus. It seems painfully apparent in this case, but strangely when you change “Jack” and “Jill” to “C++” and “C#” (or any two arbitrary languages), people happily accept the drawn conclusions even if they are just as bogus as the conclusion about the superiority of the female form on the racetrack.

Let’s make one thing clear, here: performance benchmarking actual languages is not possible. Languages themselves are little more than a grammar and some rules for various things all nicely wrapped up in a thick document that comprises the standard for that language. They don’t have an intrinsic speed, except in the form of possible complexity and efficiency guarantees for algorithms in the language’s standard library. When people (including myself) discuss the relative performance of languages, the topic at hand is really particular implementations of those languages being applied to particular solutions to particular problems within a particular context. In most cases this distinction is implicitly understood by all parties involved, but it’s worth mentioning here because, well, you never know.

Keep in mind as well that performance is very fickle, influenced by a vast array of subtle factors. It is therefore very difficult, and in some cases downright impossible, to obtain reliable, meaningful performance benchmarks that can hold up in general. Note my emphasis on “particular” above: we’re dealing with specifics, here. The results of a specific test (Jack and Jill) do not necessarily imply anything about the general case (boys and girls). You cannot really make definitive unilateral claims about the performance of programming languages. Benchmarking can only provide conclusive results within constraints that are based on the context of the benchmark; it is consequently very important to know what the context was. Based on that context, you can determine for yourself if the benchmark has any relevance to the problems you need to solve.

So far I’ve been focusing on benchmarks that are sound, but whose results are being misconstrued, but there is another danger. The benchmark itself might be flawed. This can happen to anybody, even experienced veterans (a recent article in the professional magazine Game Developer contained a particularly flawed benchmark on the relative performance of managed and unmanaged code, for example). In most cases, these flawed benchmark come from programmers who only know one of the two subject languages well. Consequently, the test case in their “native” language is well-written and well-optimized, employing language-specific idioms and techniques that increase performance, while the other test case tends to be a direct port of the first with only the minimal syntactical changes required to get it compiling and running. The test case for the unfamiliar language might end up employing idioms that make no sense in that language, or relying on assumptions that no longer hold (for example, in C++ dynamic memory allocation tends to involve a linear walk through the available blocks of memory to find a free chunk of the appropriate size, which is much less different than the simple increment that tends to comprise allocation in C#). If those assumptions are not the subject of the benchmark itself, they can interfere with the results and skew the benchmark beyond usability.

Keep this in mind the next time you see an unqualified claim that “language X is faster than language Y.” Even if the claim is accompanied by a detailed description of the benchmark and its results, analyze the results for yourself and question them if necessary. Judge the results as what they are – results from a specific situation – and don’t fall into the trap of assuming that the specific proves the general.

~ leave a comment ~

Rewrite or Refactor

28th Aug 2007

Often, inexperienced programmers will reach a point where they decide that their existing code base is fatally flawed in some way. Perhaps they’ve learned some new techniques that cannot be cleanly integrated into the code, perhaps they’ve stumbled across a requirement they cannot address, or perhaps they simply don’t like the naming conventions they used.

Whatever the reason, the programmer will often decide that the solution is a complete re-write of her code base. “This time,” she says, “I’ll get the design right the first time! My new code will be better, cleaner, more robust, more generic, and overall more powerful and less buggy!”

Unfortunately, rewriting your code from scratch is very often a bad idea. A developer who is uncomfortable with the state of her code should refactor it, not rewrite it. The key difference between the two is that refactoring is about making many, usually small, iterative changes to the existing code in order to make the old implementation conform to a new design. Ideally, one would preserve the ability for the code to compile, run, and/or be consumed by client code as much as possible, within reason (adopting a new design may, of course, involve breaking the public interface of the subsystem).

Why is refactoring better? Refactoring actually forces you to think about the problems. It forces you to look at the current design, the current problems you have with that design, and what you could do to address those problems. When you don’t think about the problem, and choose instead to nuke the entire subsystem from orbit and rebuild from scratch, you’re probably just going to make the same mistakes again; you’re rewrite will most likely be superficially different from the original (for example, you might have more consistent or different looking naming conventions), but nothing more. Refactoring allows you to learn from your mistakes. Rewriting just blissfully ignores your mistakes, all but ensuring history will repeat itself.

Refactoring is an important skill to master. In the real world, full-on rewrites of existing subsystems are almost never cost-effective, so refactoring ends up being the only real sane option.

~ leave a comment ~
Copyright © 2007-2010 Josh Petrie. Powered by WordPress, hosted by WebFaction.