Sunday, August 30, 2009

The Rule of Seven

Andrew Doull, on his blog Ascii Dreams, suggests that game designers should follow a moral code -- mostly by not creating punishing or boring game mechanics. One of his rules he calls "The Rule of Seven":
A player should be at most presented with seven options at any one time.
The reason for the number seven here is a reference to the magic number seven, plus or minus two, described in a 1956 paper by psychologist George Miller. The core of the concept is that humans can remember about seven different things at a time; that we can distinguish between seven different qualities or quantities before our capacity to comprehend is compromised.

Miller's Argument

Miller notes that we can get around this: we can count way past the number seven itself, as well as use thousands of words written with 26 different letters, because experience and tricks (such as using arabic numerals in a base-10 system) let us expand the range of qualities that we can express and remember. The point is really that, when faced with a new, unfamiliar group of items, we are at first stuck dividing them into at most seven categories. Never studied trees before? Then you'd probably be able to identify or describe seven types. Never studied breeds of dogs, or types of land animals, or crops, or minerals, or types of architecture, or music? Our natural ability lets us stick them into seven groups. Maybe only five, maybe sometimes nine, depending on the person's intelligence and experience. Until we start studying the subject, of course -- and then we learn all sorts of attributes that let us learn about more types.

And so when you design a game that has new creature types, or treasure types, or places to go -- your players will only be able to distinguish between about seven of them.

Until they learn your game. And there's the rub: how long are they going to play your game? For a short game, one that you finish in ten to fifteen hours, your players are unlikely to learn a lot about your game world, or want to spend time and effort building an efficient mental model to distinguish between all the types of Frobozzes and Gromixes that you've invented, or even to remember if Mithril or Truesilver or Adamantite is the better armor -- even if they've seen those names before.

Roguelike games start with the player in town. They've got ten buildings to choose from, plus stairs to go down and villagers to talk to. The player doesn't group this into "fourteen things" -- that's what Miller answered. The player will see this as three choices: enter a random building, talk to a random person, or head down the stairs. Because there's ten buildings, you've broken the rule of seven: it will take some time for the player to learn what those ten buildings are. If there were only seven, they'd do it quickly. The time difference between learning three buildings, five buildings, or seven buildings is tiny; trying to distinguish between ten takes exponentially longer.

Game Design Ethics

So how does this impact the game designer? It'll stress your players, and possily frustrate them, if you constantly tax their memory. Running out of time and have to choose the right one of those ten shops? Users will fail because they couldn't remember correctly. If you had given them seven choices, players would be a lot less frustrated.

Andrew Doull's basic point about clicklets is that forcing the user to do something boring and repetetive, mindless, without choice or consequence, or in a taxing way is cruel. And the main reason to avoid cruelty is to make a fun game; something that players want to come back to.

Some games frustrate me needlessly. It really turns me off of the game. If I sit down to play a game and am then bombarded with dumbass, frustrating rules and mindless clicking to get what I want, then I feel like a product I purchased for the purpose of entertainment has lied to me, and is subjecting me to pain and frustration. I find that unethical.

An analogy for a moment: Is it unethical to kill someone if you didn't know that what you were doing would cause their death? Out in the real world, we call that manslaughter, and it might be involuntary, but it'll still get you convicted and thrown in jail.

A game designer that builds a frustrating system is still guilty of frustrating his users. It doesn't matter if you knew ahead of time or not. Not knowing is negligence; it indicates a lack of forethought, of consideration. It's inconsiderate.

The idea of the ethics of game design is that: a game designer shouldn't build systems that frustrate, bore, or needlessly confuse their users. (I don't mean all confusion; some jokes and puzzles rely centrally on confusion. Work with me, here.) A designer shouldn't build such systems whether they know they'll have that effect or not; a designer is responsible for building a good product, and for learning more about his art such that he avoids such sinful mechanics.

Thursday, July 23, 2009

Efficient Ellipse Drawing - Part 2

[images coming later]

In Part 1, I discussed drawing lines. Drawing an ellipse, pixel-by-pixel, shares some comments with line drawing, so I covered that there.

In this part, I discuss drawing a circle.

In the next part, I'll discuss complications and provide a more complete circle-drawing algorithm. The final part(s) will cover ellipses.

Symmetry

Circles are handy because they are symmetric. Since we're rendering a circle onto a square grid, the symmetry that's useful to us here is horizontal symmetry and vertical symmetry. We'll also make use of the fact that you can rotate a circle 90º and still have a circle. Combine all the symmetries together, and we really only need to draw one-eighth of the circle.
[insert image here]

Hence, most circle-drawing algorithms will only draw an eight of the circle, and use this symmetry to plot the other 7/8ths. Which eighth you draw is up to you. I'll be drawing the eight from straight up (like 12 o'clock) clockwise to 45º (1:30 on an analog clock).
void PlotEightPoints(int x, int y)
{
PlotPixel(x,y);
PlotPixel(y,x);
PlotPixel(-x,y);
PlotPixel(-y,x);
PlotPixel(x,-y);
PlotPixel(y,-x);
PlotPixel(-x,-y);
PlotPixel(-y,-x);
}
This code can be easily tweaked to draw points centered anywhere in the screen; just add a constant offset to x and y in each case. Something like:
void PlotEightPoints(int x, int y, int xCenter, int yCenter)
{
PlotPixel(x+xCenter,y+yCenter);
etc...
or, if this code is part of a class:
void PlotEightPoints(int x, int y)
{
PlotPixel(x+this.xCenter,y+this.yCenter);
etc...
Borders

A perfect circle on a square grid can either be centered on a pixel, or centered on the border between two pixels.
[insert image here]

It's easy to start the first one. Say our circle is centered at the pixel 0,0, and has a radius of 10. We can conclude that the pixels (10,0), (0,10), (-10,0), and (0,-10) are all points that we want to draw. There's no fractions here, and the logic is fairly simple.

The second example -- when our circle is centered on the border between two pixels -- is a bit more complex, but much of the same logic (below) is the same. I'll cover this case in the next post.

Tricks

The trick to circle drawing is to note that, over this eighth of the circle that we are going to draw, we're only going to draw one pixel per column. Furthermore, as we move from pixel to pixel, we'll either move straight to the right (dy=0), or diagonally down one pixel (dy=-1). Hence: we just need to figure out which of those two choices is closer to our circle!
[insert image here]

The formula for a circle (centered at 0,0) is
x² + y² = r²
where 'r' is our radius.

Let's say we plot our first point at (0,10). Should our next point be (1,9) or (1,10)? We can calculate the radius at those two points easily:
r = sqrt(x² + y²)
Here's another trick: we don't really need to do the square root. We can just pick the point such that (x²+y²) is closest to r², or 100 in our sample case. For (1,9) that value is (1*1 + 9*9), or 82, and for (1,10) the value is (1*1+10*10), or 101. Our goal is 100, so obviously 101 is closer.
[insert image here]

And now for the code:
void DrawCircle( int r )
{
int x = 0;
int y = r;
while (y >= x)
{
PlotEightPoints(x,y);
x++; // always move over one column
int rAcross = x*x + y*y;
int rDown = x*x + (y-1) * (y-1);
int acrossDelta = r*r - rAcross;
int downDelta = r*r - rDown;
int absoluteAcrossDelta = Math.Abs(acrossDelta);
int absoluteDownDelta = Math.Abs(downDelta);
if (absoluteDownDelta < absoluteAcrossDelta)
y--; // sometimes move down one row
}
}
This will draw your circle. In the next post in this series, I'll cover drawing a circle that isn't centered on a pixel, provide a code snippet for drawing a circle anywhere on screen, and handle cases where part of our circle is off-screen.

Friday, June 19, 2009

Cargo Cult Engineering

Process-oriented development achieves its effectiveness through skillful planning, use of carefully defined processes, efficient use of available time, and skillfull application of software engineering best practices. - Steve McConnell
I'll come back to that quote eventually, but today's post is on cargo cults.

In my experience, engineering teams succeed because there's one or two engineers on the project that are smart, hard-working self-starters, but most importantly follow sound software engineering principles and are capable of taking stepping back and getting the big picture.

Smart, Hard-Working, Self Starters

There's ways of assessing this stuff. Personally, I think gradations of these attributes are mostly worthless. Programmers of average intelligence won't be able to tackle huge problems, or get a lot of features done, but having a smart but "unwise" (using that word as a catchall for what I'll describe in the sections below) engineer is worse than having an average-intellect but wise engineer.

Hard-working is good. But I think easy to assess. And if they don't work hard once they're in place, you need to fire them. If you can't fire them, because, say, you're in France, then that sucks. Your next job will be to get them to quit. Try transferring them to the Siberian Office.

Self Starters are handy, but again I don't think it's at the top of the list. A semi-smart, hard-working, self-starting programmer that insists on overengineering everything, following a fancy & convoluted development methodology, and is unable to assess the importance (ie context) of the parts that he is working on will build lots of great code -- that won't help your product ship or keep customers happy.

What's your goal? Are you capable of assessing context? As a manager, you want programmers that help your bottom line. That means quality code, but it also means code that you need, and code that makes your clients happy. Happy clients are more important than pretty code.

The Big Picture

Whether it's figuring out how one method fits into a class, one class into a module, one module into a project, one control into a web form, a folder or set of files into a hierarchy, one product feature into the next release, themselves into the company, their company into the industry, the product into the market, etc etc -- good engineers are capable of taking a step back and assessing context.

Bad engineers do cargo cult programming. They see the artifacts of good engineers, but they don't understand the principles behind it.

Sound Engineering Principles

Lists are popular. "7 Habit of Highly Effective People", "Top 10 Ways to Ship Better Software," the lists of core rules in methodologies, and the very frequent "Five Ways to Fit into Your Swim Suit for Summer" type stuff.

Lists are easy to make. Just observe for a little while. Pretty much anyone can make lists.

But lists aren't principles. Principles are difficult to apply. They're easy to state, but the whole trick with principles is that they must take context into consideration. Principles must also exist in a hierarchy; for each principle, there must be an antecedent principle that sets boundaries. The antecedent says why a principle is important, gives a guideline for the boundaries of the principle (where it makes sense and where it doesn't), and establishes a benchmark by which to judge the execution of a principle.

Take choosing good names for local variable. This isn't just one floating point out of hundred of practices that make for good software engineering. The list-maker will take this point and stick it into his six-page bulleted list of "Best Practices."

As a principle, one chooses good names because it aids in human parsing of code. Let's chase the antecedent principles here. Human parsing of code is important because it makes maintenance (extension and debugging) easier. Maintenance happens -- so why is it important to make it easier? Because it increases the quality of software and reduces the cost of development. Why are those things important? Why are they important on this product? The answers for quality and cost vary from project to project, and I could answer them in the abstract, but how you answer this question is what settles the boundaries of the principle.

Cargo Cults

Cargo cults mimicked the habits that they saw American military men executing. They thought that the motions themselves were what caused the airplanes to land, and the cargo to show up on the beach. Likewise, the actions that McConnell outlines in that quote above -- skillful planning, use of carefully defined processes, efficient use of available time, and skillfull application of software engineering best practices -- are habits. These are good habits, but I don't think they capture the important traits at all.

Specifically, "use of carefully defined processes" implies that browsing to some Six Sigma website and then handing down the printouts to your engineering team is sufficient for project success. That's just mimicking the habits of successful developers; it's not good engineering.

Thursday, June 18, 2009

Builder Pattern vs Factory Pattern

Versus

The builder pattern is appropriate when object creation is more complex than just calling a constructor. The factory pattern is appropriate when you have a hierarchy of created objects and you want to abstract the mapping of creation parameters to a subclass.

These patterns are often used together. Many abstract factories that I've written use builder functions. Sometimes I'll put the builder function into a base class -- which means that I have a builder function that is actually an abstract factory, which might itself use builder functions.

Now for more detail:

Builder

A Builder encapsulates complex creation into a single method (or class). If creating an object is more complex than just calling the constructor, then all of the work that goes into creating the object can be moved into one method, and that method is the Builder. 'Builder' implies only one type of created object, but that is not necessarily so. Builder really just means encapsulating complex construction!

Say that you want a Widget object, and that creating one means making a DB query or loading something from disk, constructing the object (passing in the query results), then making a few more calls to set up the object before it can be used.

Instead of copying and pasting that creation code -- query, construction, setup -- every time you need to create a Widget, you move all of that crap into a single function. The Widget constructor probably takes a whole bunch of parameters; maybe those come from the database query. Maybe the Widget uses multi-phase construction. The Builder pattern helps hide all that.

If the setup and configuration isn't really part of the created class, ie if it doesn't make sense for that class to know about all the other crap that needs to be done, the builder function might go somewhere else. I think 9 times out of 10 my builder functions are static methods in the created class itself. Instead of calling the constructor I call the builder function, and probably make the constructor private.

I usually use builder functions, not builder classes. The separate functions in a builder class might each return the same object just with different configurations, or each function might return different subclasses.

In languages where multi-phase construction is the norm (instantiate an object then make a bunch of function calls to fill it out), refactoring the construction steps into its own method is an instance of the Builder pattern!

Factory

A factory can create several different types of objects, but it returns its objects via an interface (or base class) reference. Whereas a Builder encapsulates complex construction steps, a Factory encapsulates the decision-making that figures out which specific subclass to instantiate.

Factories are accessed through a single method; that's really the point. You call one function, and it creates either a Subclass1 or a Subclass2, returning it via IBaseClass.

The Gang of Four book (Design Patterns) names both a Factory Method pattern and an Abstract Factory pattern.

In the Factory Method pattern, the factory function is virtual, and different subclasses of the creating class return different subclasses of the created class. That is, you call one class (the factory) to instantiate the second class (the created). The factory class is actually a tree - base class and subclasses. You'll have something like:
virtual ICreated* IBaseFactory::Create(...params...)
So you have two class trees: the factories and the created objects. The two trees might be parallel, ie CFordFactory::CreateSedan returns an instance of CTaurus, and CNissanFactory returns CMaxima, etc etc. Or, the two trees might be disjoint: CFryingPan and CMicrowave return an instance of CFood, while CBlender returns an instance of CDrink (where both presumably derive from IConsumable).

You'll most likely use the Factory Method pattern when you have one hierarchy (the created objects), you're about to instantiate a whole bunch of objects, and you don't want to do a switch or if/else/else trees. So you move the creation into a new class tree, instantiate the factory subclass you want, and then use a method to create your objects.

Alternately, you might have a class that creates a bunch of objects, but that class is part of a hierarchy. Depending on which specific subclass you have, you'd get different created objects. That's the Factory Method pattern.

In the Abstract Factory pattern, you've actually got a set of factory methods. A food factory method, one for drinks, one for dishware, etc. More realistically, you might have a factory method for buttons, one for checkboxes, etc, where different factory subclasses create different appearances. In a game, you might have a barracks that will create an infantry, cavalry, and ranged unit, with different factories for each player race. Instead of disjoint classes for each type of unit, one class (IBarracks) will have three methods to create IInfantry, ICavalry, and IRanged units.

Besides subclassing, your abstract factory could be run off of some other logic. The factory function could do some magic to figure out which subclass to create. It could switch off of a parameter:
ICreated MyAbstractFactory.Create( Enum paramEnum )
or it could use static data or other state to decide:
ICreated MyAbstractFactory.Create()
Abstract Factory is a funky pattern. To use it, you'll want to be creating a matched set of objects. If you find yourself wanting to do that, Abstract Factory is your pattern.

see also
Bridge Pattern vs Strategy Pattern
Ownership, Aggregation, and Composition

Wednesday, June 17, 2009

Designing an MMO part 1

Designing a New MMO, Part I: Get Rid of Classes!

Everyone wants to make an MMO. They're fun games, and with WoW pulling in over a billion dollars a year it looks like an insanely lucrative market.

Except, of course, for all those failures. Like Tabula Rasa, which cost a hundred million to make and only brought in a sixth of that in revenue. Unless you've got 84 million dollars you don't mind never seeing again, jumping in should be done with care.

I like thinking about MMO design. I think it's like talking politics. It's not like me and the rest of the crew at the water cooler are going to run for office. Ultimately, the only effect each of us has is one vote -- out of millions. Does it really matter what I think about politics? At most, I'm influencing a dozen people. And I haven't yet converted any of them to the One True and Proper Political Party, so what does it matter?

It doesn't matter. It's fun, though. Likewise, us scrubs talk MMO design. It's an entertaining exercise.

Usually the first topic to come up is something like "classes are lame" or "levels are boring". I think this is a fairly fundamental discussion.

But I'll skip it, because Lum did a much better job than me. Go read that.

(There's no guarantee that I'll ever write a part 2.)

Thursday, June 11, 2009

Reusing End-Game Content

I read a post on end-game content over at Player Vs Developer and was reminded of a suggestion I made to Blizzard long ago.

There are basically three problems that I'll address in this post: players want new content, they want a variety of content, and devolpers don't want to throw away old effort or see great content go unused.

Quake and CTF

One issue I had with Warsong Gulch (a 10-vs-10 Capture the Flag PvP zone) was that the map got boring. This is one issue with FPS games -- many players like having different maps.

Back when I played Quake, there were a handful of maps that everyone played on, and it was interesting to continue playing on the same map, over and over. I was actually learning new things about the maps after a year of play; specifically, I was learning player behavior. Learning where things are on the map is the first step; then one develops patterns; then one learns what patterns the enemy has; and then a metagame starts where players start trying to deceive their foe about what pattern they are running, etc etc. In Quake, I was learning very specific timing patterns, and how to juke out other players and make them think I was somewhere else. I was counting on my opponent knowing the map so well that I could play against that knowledge.

This is like tennis, or basketball, etc. Everyone plays tennis on the same court, don't they get bored of the same layout game after game? The answer is obviously no; the game isn't about the court, the game is about the other player.

Not so with online games like Quake or Warcraft because most players (especially new or casual players) don't want to become PvP pros. And many other players resent having to learn a map well, and instead just want to win without putting in effort. Don't underestimate your players' arrogance. Many players that suck don't believe it; they blame their losses on bad map balance, or the fact that their opponent knows the map 'too well', or some other lame excuse. Players suck. People suck.

When I played Quake on the LAN at work, I would learn the maps quickly (I'm good like that), or I'd remember the map from online play. I'd grab the rocket launcher and red armor and then start tearing people up -- in part because I also played a lot and was a good player. They'd get frustrated or bored, because to them the interesting bit was the new map, not the game mechanics themselves. They wanted a slot machine, where sometimes they won. They wanted to win; they didn't want to earn the win.

My point is that a great majority of people that will pay for your product want variety, not challenge. Don't force them to play competetive tennis; they want wacky new rules and a roll of the dice.

So am I now just bitching about WSG because I want to see new maps? Not exactly. Quake was played on a handful of maps; WSG only takes place on one map. Every time you want to play Capture-the-Flag (CTF) in WoW, you have to go to WSG. The other PvP maps have different gameplay -- Arathi Basin and Eye of the Storm both have a Battlefield/Team Fortress-like base capture mechanic; Alterac Valley is a back-and-forth push to the enemy's base.

My PvP Suggestion

My suggestion to Blizzard was to make more CTF maps, then change the queue mechanism to be somewhat like Arena, so that when someone queued for "WSG", they'd really be queueing for CTF, and sometimes they'd play in Warsong Gulch, sometimes in Netherstorm Gulch, sometimes in Grizzly Gulch, etc.

The major problem with just adding those as separate queues is that it's hard to find players. Now, even with queues spread across an entire battlegroup, sometimes it's hard to find people to play in Alterac Valley. Imagine if there were three times as many PvP queues -- some of those games would never get started! Hence: group several WSG-type maps into one queue. You get more players funnelling into the same queue, and players get to experience a wider variety in online maps. (This is why most Counter-Strike and Team Fortress servers rotate through maps!)

Players Want More Content.

This issue with finding players is also a problem (now that a new expansion has come out) for old end-game content. Who wants to run Scholomance or Kharazan? Those instances are lame! There's level 80 content to do! As much as players want variety, they don't want to do irrelevant content.

Scholomance is old. It takes too long to do all those quests. Once you hit level 61, the content starts becoming trivial, and the rewards for the grind too small. The problem is the same for level 70 instances -- it was hard then to find a group that wanted to do Mechanar, or Arcatraz, or Botanica. There were too many places to go for there to be many people that want to do one specific instance.

One way to fix this is to rebalance Scholomance so that level 80s can do it. They did that with Naxx; it's a fun challenge for 80s and the rewards are appropriate. Yet if they did this to every 60 and 70 instance, it'd be a pain to find a group to do anything. It'd be the problem with Mechanar but far bigger. Especially with the way itemization works -- one person wants to get his hat from here, the next guy needs a pair of pants that drops off a boss there, and once they got their drops they'd never want to do the instance again. It'd be nearly impossible to find someone to do any one specific instance, just because there'd be so few people that want anything that drops from there!

One way to solve that is the token system used at the end of the 60 lifecycle and was fairly widespread in the Burning Crusade world: kill a boss, get a token that can be used by a handful of classes for a number of different armor slots.

Now imagine if you needed Keeper of Time rep for some level 80 gear that you could only get from the Keepers, but that you could get the rep from any of a half-dozen old instances (rebalanced for level 80) and that it also didn't matter at all which one you did. Now you could say "I want to do one of the Caverns of Time", and anyone that wanted Keeper rep could do it. It used to be that people wanted (say) Durnholde specifically because that's where their item dropped. What if their item dropped from all of those instances instead of just one boss in just one instance?

Now everyone could do Caverns of Time again. The developers could re-use end-game content, and players would have a wider variety of options for where to go. The developers could add in one or two new CoT instances, and maybe redo one of the old ones, and everyone (new and old players alike, ancient characters and brand new alts) would have a much wider variety of content to choose from. A group of five players could choose which instance they enjoy rather than which instance that itemization forces them to pick. Players would be far more likely to be able to play a new instance, instead of feeling forced to go do the same instance over again.

The downside to this, of course, is that maybe players are bored of the Caverns -- especially those that were playing before BC came out and have been playing since. I have a hard time believing that Bliz couldn't just redo each of those levels. Seriously. They're making billions of dollars a year on the game. And they could reset KoT rep to Revered, or maybe add something past Exalted, or add a new faction that automatically becomes Revered 0/21000 if the were Exalted before, etc etc, so players would have a reason to go back.

Players want new content. Players want varied content. Developers don't want to develop content, and then effectively throw it away because no-one is doing it any more.

The easiest thing to fix, really, is throwing away content. They removed Old Naxx from the game. They could remove Old Scholomance and who would know or care? Spending the money to develop New Scholomance would be trivial to them, it would be new content even to old players, and (with sufficient itemization eg through tokens) would give players a broader set of dungeons to explore, instead of hitting Kara week after week after week after week after zzzz....

Thursday, April 30, 2009

Complexity in Game Design

My Travian game is coming to a close, ie nearing its one-year mark. I've been poking around at other browser games to assess the competition, thinking about switching, and it reminded me of one of the lessons of game design that I picked up long ago.

I remember playing Warcraft 2 and thinking, "you know, this game would be even better if it had even more upgrades and building types and everything."

A few years later, while playing Kohan, I realized that I was very wrong. WC2 was so awesome because it wasn't any more complex. Kohan (another real-time strategy game) had a different, but relatively straightforward, combat model. It didn't add more building types, or more troop types, or more upgrades -- it just configured armies differently than WC2.

Complexity is great for World of Warcraft because people play that game for thousands of hours. Yet at the lower levels, the game starts out very simple. Starcraft, currently enjoying years of professional play in Korea, isn't any more complicated than Warcraft 2. Chess is much simpler than both.

What makes a game fun is the interplay of choices. With a ton of choices, sometimes randomness sets in and dominates play. "Is unit A better than unit Z732? What about Z731, or Q986? Gah there's so many, forget it! Just build unit A!?" It's difficult to figure out a good strategy (or to be happy with the strategy you chose) when combinations start spiraling up.

Warcraft 2 and Kohan and Starcraft all found a balance with a small number of troops and buildings. Even then, they gradually added all their options in over the course of the game. They don't throw new players into the deep end (the full game); they work up to it over 30 hours or more.

It's like getting decent at chess and thinking, "ok, now that I've learned how all these pieces move, what I need now is more pieces! A larger board!" What makes chess interesting isn't those new pieces; instead, the game changes. The focus shifts to strategy and positional play, thinking ahead and mind games, learning the books and the endgames.

Part of Travian's appeal is its simplicity. If the game got too much more complex -- twice the number of buildings, more complex combat, etc -- then it would be a much harder game to get into. Part of its appeal is its chess-like simplicity. Even in that simplicity there is a lot of interplay, since so much of the game works on an exponential curve.

Good designs are simple designs. Let the fun be in the interplay of a handful of archetypes, not in the mindless proliferation of abilities and powers and resources and buildings and technologies...