Blog 730: The Art of Frobbing

The player Neutral Passive in Warcraft III is a special bonus player. As the name suggests, units owned by Neutral Passive are neither allies nor enemies to any other player; they offer their services equally to whomever happens to be nearby. As befits my tendancy to reimplement Warcraft III (to eventually allow for similarly structured RPG scenarios), I am now working on adding my own Neutral Passive characters.

Needless to say, due to many holes I’ve dug myself over the last three years of developing this game, this is not so easy. The key stumper at the moment is that clicking a neutral unit should not trigger the player to attack them. Instead, it should trigger a friendly reaction. When is an attack not an attack? When it’s a frob.

Frobbable

If you edit anything inside Deus Ex, then you’ll soon discover the odd little word “frob”. I believe it’s originally System Shock 2 or Thief parlance, but it pretty much means “to interact with”. The great thing about “frobbing” is that it encapsulates multiple different activities under one convenient (and short) word. In Deus Ex, to frob something is (by default) to right-click on it. If that thing is a person, you start talking to them. If it’s an item, you pick it up. If it’s a computer, you start reading it. All these things, activated by the same mechanism but with different results depending on the context.

My game requires that many objects be frobbable. I actually have two kinds of frobbing implemented already; frobbing switches (activating the end-of-level lift), and frobbing items (to consume or equip them). The problem is that I did these mechanisms badly. Independently of each other.

Click

Handling user input in video games is not trivial. What to the player is a simple tap of the keyboard is the key being held down for a period of time to the game. If I naively put an “if button is down, do thing” condition in, then a tap of a key that lasts barely half a second could still be detected as the button being down more than 30 times (or, ideally, even more often). To frob, however, is to interact with something precisely once.

So before we even consider what action a frob should trigger, we first have to strain out the singular moment of frobbing from that storm of input noise.

Click

I fairly recently wrote a little snippet class which handles this sort of thing. It’s not particularly exciting; the jist is that it tracks the upness or downness of a single button both immediately and one frame in the past. By comparing these two values, I can tell if the button was up and is now down (the start of a tap), if it is still down (being held) or if it has just been released. That means I can ensure that my frob action is executed only once, regardless of how long or short the user’s tap or click is, by attaching it to a change in the button state rather than pure downness.

(I have actually shifted a lot of actions to when a button is released rather than pressed, so if you misclick but realise your mistake, then you can move your cursor away and release the button when it’s not over the thing and thereby ‘cancel’ an accidental action; or you can hold the button down until ready to time an action more precisely.)

The extra problem for me is that the frob control is the same button as used for your hero’s primary attack (this is the same scheme as Nox, so I know it’s viable). Not only do I have to be careful about when the button went up or down, I have to disambiguate between clicking with the cursor over nothing or an enemy (which means you still want to swipe your swords) versus clicking on an item or neutral character (which means you want to frob the thing).

Click – HAI!

My two existing kinds of frobbing — item pickups and hitting switches — were both implemented as completely separate mechanisms, copied and pasted, repeating that disambiguation logic (… it was the quickest and easiest choice at the time). The first thing I needed to do, then, was to unify these systems so I could cut out all that old chaff in favour of a single execution path. One frob to rule them…

Hooking onto Unity’s component-based paradigm, I’m considering a thing to be “frobbable” if it has a “FrobAction” component attached to it. Anything that is “Selectable”, that will show the targeting crosshairs when moused over, can also be frobbable (but doesn’t have to be). This is secondary to the faction system — only allied or neutral actors can be frobbed, enemies can only be attacked.

The advantage of this system is that I can get different frob results very easily. A consumable repair kit gets a FrobTakePickup. A sword gets a FrobTakeEquipment. A lever gets FrobSwitch. Finally, and the point of all this madness, the new repairbot gets FrobGiveOrder — the order being to repair the frobber. Phew, we got there in the end.

Click

There are a lot of advantages to this approach. It means I can take some logic out of the already overloaded Unit class and shift it into the frob action; for example, working out whether or not a unit can pick up a consumable, like denying use of a repair kit if you are already at maximum health. It becomes the responsibility of the frobbed object to deal with all the mechanics of being frobbed, rather than centralising those myriad possibilities into the frobber.

Let’s face it — there are going to be a million different frob results in the end. Some things will affect the player’s meta character, some will affect the unit they’re currently controlling,  and a thousand other rhings. Putting all that logic into the frobbed object (frobbee?) rather than the frobber will make things so much tidier and hopefully pave the way for lots of wonderful things.

Advertisements

And you tell me...

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s