project scope triangle

When it comes to scaling features in game development there basically are two approaches: top-down and bottom-up. This means you can either put more or less effort into a feature to meet limits like time, money or quality (aka the project scope triangle).

So far I've used a top-down approach for making my game assets. It's simple: decrease asset quality when you run out of time. Seth Godin's N-1 blog post Shohreh MusicFree Mp3 Download was the main inspiration for this. After 2 years using it for Nordenfelt I dare to say:

Top-down scaling does not work for game assets.

The Top-Down Scaling Dilemma

Let me explain the problem by the following example:

While happily developing your game you take a look at your schedule. Shohreh MusicFree Mp3 Download Half of your asset budget is gone but only 30% of the assets are done by now. They have decent quality and also underwent some polish. So you need to complete the remaining 70% of the assets within the left (half) asset budget. The top-down scaling approach solves this problem: decrease the quality for the remaining assets. Problem solved.

Let's do the maths for this example:

  • it took half of the asset budget to create 30% of all assets in 100% quality => 0.3 * 1.0 = 0.3
  • to complete the missing 70% within the same time you have to decrease quality to 43% (0.3 / 0.7 = 0.43)

Finally you have to create the new assets in less than half the quality of the other assets. So you'll get a game with some polished and much more unpolished, drafty content. Put simply: pure inconsistency.

google maps inconsistency

You get it

 

The problem is that top-down scaling is like building the roof of a house first, mounting it on very high pillars and say "that high we have to build the walls". The Beatles Songs When it's time to downscale the quality of the underlying walls the house may not stand the roof's weight. Therefore this scaling method does not work when you create entities which depend on each other like game assets.

Basically the asset quality downscale approach does not save you from going out of time/budget. You would have to find the right quality level at the very beginning of a game project. This would make a mockery of quality scaling because you already have the perfect quality level for your budget.

Rather Scale Up

The alternative bottom-up scaling is way better suited for assets. First you have to settle for a minimum quality. The optimum: settle as low as possible, speak sellable quality. I've written about that here.

How to define "sellable quality"? Well, a good question. I thought my guts would tell me what's the minimum acceptable. Forget your guts! Instead head over to platforms like Desura, Indievania or Indiecity and check out which minimum asset quality sells. You will be surprised how low-fi you can go.

Using the new, as-low-as-possible quality should allow you to complete ALL assets in budget. When all assets are there and your game is sellable you can either starting selling it or exhaust the left budget for better quality. Improve the assets step by step but don't go too far to keep the overall quality consistent. Better polish many assets a little than polishing a few out of equality.

Positive Psychological Side Effects

The best thing about bottom-up scaling is: I sleep way better.

Before switching from top-down to bottom-up scaling I often missed deadlines and quality levels. Over time this had a strong impact on the psyche. It felt like nothing could be achieved in time and rendered deadline estimations senseless.

With upscaling I now can set a time frame and crank out minimum viable assets until the time is over. When there is time left I use it for polish. If not all assets could be done within the time frame: no problem. Not all of them are necessary and I made the most critical ones first.

Bottom-up scaling brought the fun back to my project.

 

Cheers,
Thomas

 

When I started out making 3D models for Nordenfelt I made all design sketches, 3D models and rendered sprites public. I thought it would be a clever way to get feedback what's good and what's not. Theoretically not a bad idea. Practically: it was worth nothing.

Due to the fact that I was a newbie game artist back then I was not aware of what's essential for game graphics. Nordenfelt's visual style is "simplified realism", alike Baldur's Gate. So I thought making somewhat realistic graphics would be sufficient. As you may guess the fuzzy word "somewhat" turned out to be a stumbling block.

Whenever I presented an asset people started to argue that the angles of the Gatling guns were not realistic, that steam engines need boilers or that there's no hint how the machine was able to float in the air (e.g. visible propellers). In my naivety I followed these hints and altered my assets for the sake of realism.

I remember having a forum discussion about the cockpit shape of the player ship. A guy argued my design was flawed because there never has been any real-life aircraft with a cockpit shaped like my 3D model's cockpit. That was the moment I asked myself: What does all this not-plausible-in-real-life carping have to do with my game? It doesn't make an iota of difference to the gameplay.

final player ship design

The problem with showing assets in isolation is that the formative design rules are not obvious. Above all assets have to carry the gameplay. If necessary paint tanks and war planes in pink and red if it helps to distinguish them from the background (what would be disastrous in real war). It's irrelevant if propellers are too small to carry the weight of a hover bomber. It's more important that they don't cover other enemies which may become a real threat then.

Finally feedback on assets is just "noise". Everybody has a different taste and idea of what your assets or game should look like. Therefore it's a waste of time to adapt assets to other things than gameplay and overall style. As soon as people see your assets in motion and interaction with each other the scope of feedback jumps to a way more helpful level: how good the GAME is/looks.

Don't get trapped on the details level.

 

Cheers,
Thomas

 

Blogging is a little ungrateful. Many posts contain temporary stuff, just an opinion at a specific point in time. After your audience read it they will forget the most of it immediately. Or they send it to one of those social media sites and your blog traffic skyrockets for some days. In either way blog posts don't last long. Or do they?

Well, temporary posts are only half the story. When I'm looking at the statistics of www.blackgolem.com there are some exceptions. A few articles attract people all the time. They are about collision detection, pixel art and analysing graphic styles. All these articles were written in the first half of 2010. Let's have a look at the site's search traffic:

search traffic graph

That's interesting: search engine traffic increased slowly. There were no sudden visitor rushes when the articles came out. This means that articles can not be judged independently from their environment. As blogging went on and more incoming links appeared on the internet the interest of search engines raised slightly. That's expected growth but not exciting.

Then came March 2011. Since this month the search traffic curve has a steeper slope than before. What happened?

I started blogging more regularly.

Since February I wrote a post every 2 days. After a month I turned it down to posting just each fourth day. The funny thing is that there seems to be no difference between these intervals. Visitor figures did not drop. Just the fact that there is something new periodically improved the search engine traffic as well as the site's Technorati rank. I may be totally wrong here and some unnoticed coincident made www.blackgolem.com more prominent. But the desire to find the cause for this effect led me to this conclusion.

So what do you have to do if you want to get more traffic from search engines? Three things I found out so far:

  1. write helpful articles,
  2. write what people search for and
  3. keep blogging regularly

When people search for information they search for help. Providing help and education is the best way to build traffic. Finally people give a shit about you until you give them something valuable. It's a cruel world out there.

How can you find out what people want? For this try Google Insights or Google's Keyword Tool. Enter a search phrase you want to write about and find out if there is anybody looking for it. A good way to decrease shots in the dark.

Happy traffic gaining!

 

Cheers,
Thomas

 

After five months I'm finally back on this blog. Some people already were curious where the hell I went. I never left, I just did an experiment...

The Book Project

I always wanted to write books. I've started my first one at the tender age of 16, just to fight the daily boredom while waiting for the train home from school. It was a fantasy novel on which I spent every free minute for about 3 years. As far as I can remember I quit after 200 pages. The main reason was that its scope was way too large. It would have taken about 1000 pages to complete it. Another reason was the inconsistent writing style. I think puberty and it's hormone level fluctuations are to blame. It's funny how different you express yourself after a few years. And the final reason: everything was written on paper. It would have been a lot of work to digitize that. So there's just the script left, rotting somewhere in the attic...

In March this year, more than 10 years later (and hopefully out of puberty) I thought about writing another book. Just a little side project. A major motivation was to have something small I could finish in a short period of time. Nordenfelt has already been in the making for too long. So there was this craving for some success feelings.

Soon it became clear that running a side project in parallel to Nordenfelt wasn't applicable. They just would delay each other even more. So decided to freeze the game until the book is done. Back than I was thinking about 2 months. Man, was I wrong. As ever.

The first draft was finished within 80 hours. That's about 6 weeks in I-have-a-day-job-as-well life. Then came the famous last 20% of finishing the nearly 60 illustrations, proofreading, and writing the code examples. It seems to be a global rule that projects proceed slower the closer you come to the finish line.

Finally, after about 280 hours (ca. 22 real life weeks) the book is available as pdf e-book and as paperback:

book 2D Game Collision Detection

Check it out: www.collisiondetection2d.net

Lessons Learned

The following list is a brief recap of what this project taught me:

  • everybody you tell "I'm writing a book" thinks: novell - write one, people want entertainment (damned hedonists!)
  • no need to release all versions at once (pdf e-book, paperback, Kindle, etc.)
  • a project has no definitive finish line, it's more of a decision of what not to include
  • not sharing your everyday progress frees you from expectations
  • the 80/20 rule is everywhere!
  • time estimation for projects is worthless (speak: always wrong) - you can only change your feature list

As you can see, most of the points hold true for game development as well.

What's Next

As you may guess, I'll continue working on Nordenfelt. First of all the way of how I work on this game has to be changed. The most critical change is shrinking the focus. There are too many features in there right now. Most of them are not even visible to the player. That's the major problem. Up to now it was primarily engineering-driven. Most of the fancy mechanics in the engine don't bring the player any fun. It was just fun to implement them. The last blog post already explained the reasons for this deficiency.

Further I have to rethink the asset creation process. Handcrafting everything is no longer an option. It was cool for learning asset creation but the time has come to gear up. Automation, freelance artists, style changes, whatever is necessary.

Aside Nordenfelt I'll go on promoting the book. That's actually the most interesting part of the book project. And the toughest. For this reason I want to ask you, my fellow reader, if you could spread the word about the book? Any help is welcome.

Winter of Noise

The summer is over. Game dev goes on. So will this blog. The next post's silhouette is already on the horizon...

 

Cheers,
Thomas

 

 

 

 

... while you are working on your project:

three monkeys

 

I have a big problem with newly acquired knowledge, regardless if in game programming, design or art. Whenever there are articles or discussions about these topics on the net I have to read them. The problem: afterwards I want to apply the new wisdom (or nonsense) to my game. I feel obliged to do so. Otherwise the game won't be as good as it can/should/must be.

This behaviour is very dangerous. Adapting a running project due to "infiltrating" knowledge will send you into an endless improvement loop.

Examples:

  • new features seen in other games,
  • fancy graphics your game can't live without,
  • adapting your engine from single- to multi-threading - for the sake of it,
  • optimizing before you face performance problems (I confess myself to PE) or
  • data-drive as much as possible.

 

It's important to guard your running project from the latest craze and daunting competition influence. The most important purpose of projects is to deliver results. In terms of game development this is Rihanna Dimonds mp3 download the game itself. Started projects are dime a dozen. Finished projects are not. Prevent your game from stalling due to "I need that to" syndrome, also known as feature creep.

 

Cheers,
Thomas

 

Game design is like any other discipline regarding innovation. It's an evolution which builds up on ideas, trials and errors done by other people. It's like wandering an unknown mountain, going for the top. Trying out different paths can make you crest. Trying out the wrong path can lead you to fall to death.

In my past design attempts I had just one maxim:

Do something radically different.

A nice approach but there is innovation and iN0Vat1an. The former is a small step further, an evolution. The latter is something radically new, a revolution. The difference is subtle. Playing a shmup, for example, just by voice commands sounds ... Interesting? Weird? Impossible? It's hard to imagine how that should work. But using voice commands just for panic bombs wiping out all threats sounds applicable. Other input is done the usual keyboard/mouse/joypad way. The "just voice" idea is too far apart from the known input scheme. People won't get used to it. Or would they?

Innovation is hardly accepted when the new is too dominant. Man is a creature of habit. People like to get the same things over and over while they also expect growth and improvement. It's the natural advancement people are used to. Children growing up within 20 years is expected. Whereas persons raised over night (including a hunchback, dead bodies and thunderbolts) get hunted by fork-wielding mobs.

frankenstein's creation

Will they like what we're creating here?

For innovation it's important to know what's already on the market. First of all you will get browbeaten because there is so much excellent stuff out there. How to compete with that? Well, this is the point where market research pays off. It shows you holes in the wide area of your genre. And there you should install your ideas. But keep close to the already known:

Do something radically different. But don't overdo it.

 

Cheers,
Thomas

 

PS: Frankenstein is the doctor, not the monster. Some people still mistake that.

 

Back in January I wrote a small design document (DD) for Nordenfelt including a rough time schedule. After it was finished I put everything together in a folder and put it in my desk. I had a good feeling because I've defined the road somewhat.

I think there were 3 times I looked into this folder down to the present day. Many unplanned tasks emerged like finding a decent graphics style or adding unexpected engine features. They invalidated the whole schedule and some points of the DD. Sure, I could have updated the docs but I did not. Why? Because closing the folder and putting it back on the shelf was easier, thinking: "I'll update it later, feature X is more important now". This way the docs become worthless over time and faded into obscurity.

Before releasing Nordenfelt 0.3 I did a complete overhaul of the folder. To my surprise the DD nearly was fulfilled. Many features are hidden at the moment but they already made it into the program (surfacing in 0.4?). The schedule was transformed into a small list of big tasks like "reactivate campaign mode" or "support multiple resolutions". As said here it's senseless to plan far away tasks in detail. Therefore only the most imminent point on the list is subdivided into smaller jobs. This makes milestone estimations nearly impossible. But I don't have a problem with this. Time estimation in software development never worked for me, only feature pruning/scaling did. Maybe that's topic for a new blog entry.

The most important part of the overhaul was making the schedule permanently present. The roadmap is now pined on a cork board in front of me. It lists the big points in order with rough dates. Details are figured out on the already mentioned stacked TODO lists, residing in front of the board, right beside my workstation. There are no other docs anymore gathering dust in the dark corners of my desk. Only what I can see at a glance.

Hidden stuff is useless stuff.

 

Cheers,
Thomas

 

I'm just remodelling the enemy classes in Nordenfelt's code. When I started writing them I pondered how to reuse components and classes in the long run. When it came to enemy AI I had the idea to separate enemy objects from their controlling AI. That's similar to the MVC pattern, especially concerning M and C (model and controller). The intention was to make many different AI controllers compatible with a specific enemy class. I imagined it like racing cars and their pilots. A pilot can drive multiple types of cars as long as she knows where the steering wheel is, how to switch gears, where the accelerator is, etc.

Nearly a year later I'm aware that this idea does not work out as expected. The enemy interface became too bloated for giving the AI controllers the information and access they needed. Nearly each enemy type had a specific controller so reuse hardly took place. The real reuse came from instantiating enemy classes with their dedicated AI using different property values. Quick, hacky interface extensions were made and never removed. This way the whole concept died a complicated, buggy and hard to maintain death.

There are just a few enemy types I have to rewrite now. A few enemy types in addition and the rewrite time would have skyrocket. The new approach is data-driven alternation which is successfully used throughout the whole engine. Why change a winning team?

Did you ever implement a fancy idea that turned out to be crap?

 

Cheers,
Thomas

 

Two months ago I wrote an article about collision detection optimization for Nordenfelt. At that time the basic problem emerged from the fact that the engine used rectangles for collision detection. In this article I want to explain why I dropped the rectangle approach.

Simple Collision Shapes

There are two simple solutions for collision detection: Using rectangles or circles. Testing rectangles for intersection is as simple as the following code shows:

bool Intersect(Rectangle& a, Rectangle& b)
{
return a.Left < b.Right && a.Top < b.Bottom &&
b.Left < a.Right && b.Top < a.Bottom;
}

Collision detection between circles is a no-brainer to:

bool Intersect(Circle& a, Circle& b)
{
Vector2D distance = a.Center - b.Center;
return distance.Length < a.Radius + b.Radius;
}

The calculation of the distance length is rather expensive due to the need for a square root (length = Squareroot(x*x + y*y)). Therefore we use a common trick and compare the squared values (length*length = x*x + y*y):

bool Intersect(Circle& a, Circle& b)
{
Vector2D distance = a.Center - b.Center;
float radiusSum = a.Radius + b.Radius;
float squaredDistanceLength = distance.x * distance.x +
distance.y * distance.y;

return squaredDistanceLength < radiusSum * radiusSum;
}

Detecting points inside a rectangle or circle are special cases where the point is a rectangle/circle with an area equal zero:

bool Intersect(Rectangle& r, Vector2D& v)
{
return r.Left < v.x && r.Top < v.y && v.x < r.Right && v.y < r.Bottom;
}

bool Intersect(Circle& c, Vector2D& v)
{
Vector2D distance = c.Center - v;
float squaredDistanceLength = distance.x * distance.x +
distance.y * distance.y;

return squaredDistanceLength < c.Radius * c.Radius;
}

I don't know how good triangles would fit into collision detection. I did not investigate this shape. The simplicity of rectangle and circle was enough for me.

A Wrong Choice

Back in the days when I planned to use pixel art for Nordenfelt I chose rectangles as physical representation atoms. Airplanes, turrets, ships and other war machines have angular shapes. So rectangles were the first choice. The only drawback were rotations. Only axis-aligned rectangles provide simple collision detection routines. But pixel art is not rotatable anyway (without ugly distortions) so that was no problem.

After some graphical experiments I dropped the pleasing idea of pixel art - sob :( - and switched over to rendered 3D models and higher screen resolutions. Now rotations became possible and were included right away. This created the demand for rectangle rotation which was solved by including polygons for physical representation. Complex algorithms and some trickery were needed to make them usable for the engine.

Circles have a huge advantage over rectangles and polygons: Rotation is faster (rotation of one center compared to rotating every polygon corner) and does not change the shape, related to axis alignment. I thought about using circles instead of polygons. Laziness stopped me pondering.

Profound Refactoring

Integration of the new power-ups (coming in Nordenfelt 0.2) created a refactoring demand for state machines, animations and sprite management. Why not kill two birds with one stone and replace the awkward polygon shapes with circles? So I dived deep into the "physics" code again.

After four days of refactoring the results manifested as hierarchical circle structures. These structures can easily be scaled, translated and rotated. The hierarchic struture speeds up collision tests. When the envelope circle does not intersect neither do the inner circles:

 

ship_boundary

Bonus

Another advantage of circles over polygons and rectangles appeared while coding: Line intersection is a breeze and can easily be extended with line thickness:

float GetSquaredLength(Vector2D& v)
{
return v.x * v.x + v.y * v.y;
}
float GetDotProduct(Vector2D& v1, Vector2D& v2)
{
return v1.x * v2.x + v1.y * v2.y;
}

bool Intersect(Circle& c, Vector2D& p1, Vector2D& p2, float lineThickness)
{
float virtualRadius = c.Radius + (lineThickness / 2);
float squaredVirtualRadius = virtualRadius * virtualRadius;
Vector2D f = c.Center - p1;

if(GetSquaredLength(f) < squaredVirtualRadius)
return true;

if(GetSquaredLength(p2 - c.Center) < squaredVirtualRadius)
return true;

Vector2D distance = p2 - p1;
float t = GetDotProduct(f, distance) / GetSquaredLength(distance);

if(0 <= t && t <= 1)
return GetSquaredLength(f + (t * distance)) < squaredVirtualRadius;
else
return false;
}

This intersection would be much more complex, slower and uglier for rectangles and polygons.

Line intersection is needed e.g. for shots which need to check their trace. Otherwise they may fly through an object without detecting the collision:

 

shot_tracing

Conclusion

I don't know if there are better collision detection solutions out there. I bet there are. Finally hierarchical circle structures are a beautiful, fast and easy to understand method for body representations. It also applies to 3D very well. Simply add axis Z in the vectors and it works in three dimensions. Try this with polygons :)

 

Cheers,
Thomas

 

The last few days where designated for optimization. The last published improvement for collision detection was a local improvement which only affected a small part of code. It fixed the frame rate drops so far but upcoming levels will have many more bullets and enemies. Therefore I went on making more improvements. Currently I'm working on a high level space partitioning solution (thanks to Paul for the tip) and some pooling mechanics.

In this article I want to share some thoughts about optimization.

Don't Guess, Measure

When you experience low frame rates your instincts want to blame something visible. Graphical performance problems are common but not always guilty. Other bottlenecks like file access, data loading or complex calculations can be problems to.

Find out what exactly kills performance by measurement. You may be surprised! Pointing at a detailed sprite or mesh and condemning it is just technological witch-burning. It won't solve your problems.

Distribute Work Load

If you recognize single peaks in the performance graph you may be able to divide this selective stress into smaller loads and spread it over several frames. Artificial intelligence is well suited for this. NPCs don't need to "think" in each frame. Set a threshold for the number of tasks per frame and delay undone tasks to the next frame.

Load distribution over multiple CPU cores is the modern approach here. It has advantages in performance but is not trivial to implement. If you understand how to use threads (which needs much experience): use them. Otherwise you have to stick to the synchronous approach (no problem for simple games) or find a technology which supports asynchronous programming. Stackless Python may help out.

Bring Everything Into RAM Before The Action Starts

Many frame rate drops in Nordenfelt occurred because sprites were loaded while the level was already running. Synchronous file access (e.g. image loading) or expensive calculations should be done before the real-time action starts. Show the player a cool loading screen and gather all the stuff you need in the background.

Use References

Always keep in mind how big data is you pass around. Data bigger than the native pointer size (4 bytes on 32 bit machines, 8 bytes on 64 bit machines, you get it...) should be passed by reference.

The Nordenfelt engine had a function returning a copy of a list of pointers to enemies. The problems with this function were that it made a list copy and was called very often per frame. This wasted many CPU cycles. Returning a reference to the list made the enemy update code 5 times faster. A little "&" was the difference.

Pool Massively Used Stuff

Nordenfelt or shmups in general are good examples for this. They have to manage a heavy load of bullets. Loading a bullet at launch and deleting it after impact is a straight forward solution but can bring your game onto its knees. Better prepare a pool of bullets in advance and fetch them out as you need them. Put 'em back when they are no longer needed. Combine all your bullet sprites on one big texture, load it once and keep it in RAM.

Centralized Knowledge Rules

Game objects are predestinated for OOP. OOP has a simple paradigm: Objects should be responsible for themselves. Following this guideline every NPC has to check for itself which other NPC it can see or if it collides with them. For X NPCs this would make X*(X-1) checks for visibility and collision.

When we centralize these processes we can cut calculations short. The main advantage is the awareness and reduction of redundancy:

  • Visibility checks are halved to (X-1)*X/2. When A has free sight to B, B has free sight to A as well.
  • Collision checks can be grouped. Character A won't collide with character B when they are in separate rooms.
  • Collision checks in groups can be halved the same way as visibility checks.

There are many more examples how centralization can improve performance. It somehow hurts the OOP paradigms but they are irrelevant in the end. No player will blame you for this. They will blame you for stuttering game-play.

Know Where Optimization Is Useful

Good programmers get a feeling for code which may be called often. Try to make such code efficient. But don't invest to much time in advance. Profiling the running game may show you unexpected bottlenecks.

A rule of thumb for optimization focus:

Optimize the most used, then optimize the most seen.

 

As mentioned above I'm still busy optimizing for a fluent game-play experience. There are some days of work left here. I will drop a line when it's done.

In the meantime: What are your experiences? Share your thoughts on code improvement. I'd welcome learning from you.

 

Cheers,
Thomas