Chapter 2: The Control Loop


All games need a way for people or AI's to make characters do what they want. With the Control Loop players and AI's have a way to control their characters, and the characters always know what to do.

The Control Loop breaks down into four parts: A Controller, a CharacterInteractionModel, a ConditionPalette2d, and a Procter.

The Controller

The Control Loop starts at the Controller, which is simply an interface used for gathering input from an AI or a player. Controller itself has only one method: getAction(Object). The getAction(Object) method takes an Object as an argument in order to know in what format to return the action (or actions) that it's collected between the last update and this one. Ideally, the arguement sets the format for the action to be returned, which is then passed back by the controller (or the arguement is modified by the controller, discreation is obviously left to the programmer).

The Controller interface is intended as a place to attach all the various listeners for AI's and players, in order to keep the way input is handled neat and tidy. Typically controllers would have code set for filtering keystrokes and decoding those that could be interpreted, and would return the decoded messages when getAction(Object) is called. Similarly, they would handle mouse or other external input.

The Character Interaction Model

The CharacterInteractionModel, or "CIM" (pronounced "Chim", since it sounds better than "Sim", and the project title is Italian, so we might as well run with it) for short, has the the most important role in the Control Loop: It gathers the information from the Controller and determines what result it will have on the character that it's controlling. The CIM references its character for the character's stats, and contains a ConditionPalette2d full of the conditions for various actions to be performed.

The method that's responsible for it all is doTurn(). When called, do turn should first ask the controller for its input. The format in which it asks depends on the ConditionPalette2d conditions: If they are organized by number, than the CIM should probably ask with an Integer, if they are organized by name, use a String. What the CIM does with the input is largely left to the discretion of the programmer, however a ConditionPalette2d is provided in the CIM with the express purpose of turning that input into meaningful action. The intended way for the CIM to work may do some initial prep work on the input, but pretty quickly it passes it straight into the ConditionPalette2d as the name of a Condition, using the character as the "subject" argument.

"Why", you may ask, "do you have such a convoluted way to interprete that input? Whynot simply have the Controller do it all and ignore the CIM and the ConditionPalette2d?" The answer is actually rather simple: If you have a bunch of different characters, all who are simply templates of an orginal source (think back to fighting swarms and swarms of the same bad guys in good 'ol nintendo games) then each will need its own CIM. However, each CIM doesn't need it's own ConditionPalette2d (and in fact it would not be uncommon for a single ConditionPalette2d to be shared by an entire game). This reduces the ammount of memory that you need to have when throwing a hundred thousand bad guys at someone.

Similarly, if you had a bunch of enemies that used the same AI, a single controller could be used for them all (anyone whose played space invaders has seen a single AI control virtually all the enemies on the screen). For players, multiple CIMs with a single (or a couple) Controller(s) can provide an easy way to have following behaviour.

The ConditionPalette2d

When the CIM passes the action to the ConditionPalette2d, it's the condition palette's job to turn that into something meaningfull. Unfortuately, not a lot can be said about this process, since the condition palette is the heart of game logic, and by definition can only be talked about in specific instances of games, not in general.

When tutorial examples are put up for the project expect an in depth treatment of the ConditionPalette2d, but for now just take my word for it.

The Procter

Virtually everything eventually comes back to the Procter. The Procter is like a foreman, directing the actions of everything in the game. When it comes to the Control Loop, the Procter sends the initial request for the character's CIM to update, than waits patiently for a response, and then repeats for all the other characters. The Procter does get back a response from the CIM when the CIM is done updating that indicates (or can indicate) what the character is doing. This can be used by the Procter with another (or even the same, in some cases) ConditionPalette2d to figure out if more should be done to the character. Of course, the Procter itself has other things that probably need to be done on a turn by turn basis (e.g. checking to see if you hit the walls) that will probably get done regardless of whether or not the CIMs return anything.

Wrapping up the Loop

At this point you may be wondering why it's called the "Control Loop" and not the "Control"..."something else". Well the reason is because we really haven't talked much about the role of AI in the Control Loop. The neat thing about players is that they get all their feedback about what's going on through a completely different loop: The Draw Loop. AIs don't have that option. Consequentially, whatever AI is running the Controllers for non-player controlled objects needs to get feedback from the Procter as to what's going on. This is a hideously complicated undertaking considering the theoretical complexity of AIs. And, consequentially, I'm not going to talk about it; you're on your own in that regard.

Something that may have struck you (and will continue to strike you in future chapters), and something that deserves careful note, is that a lot of what I've written I have written in the passive voice. Why is that important? Because I had to: The majority of what I've said is not set in stone. This is a framework, not an engine, and virtually all the methods I've talked about are abstract, either as part of an abstract class or an interface. This means that there is no guarentee that two different engines made by this framework will be even remotely compatable. The whole purpose of having a framework below the engine is to make it more flexable, and better able to deal with different structures for games. That's the trade-off.

That having been said, there still is a reason for having a framework: It does help to standardize the flow of data within a game. This may seem like a trivial benefit, but in the next chapter you'll see exactly how powerful that can be, and how much overhead it can save in terms of memory usage and processing.

Chapter 3: The Draw Loop
Back to front page

Project Page

Forums


email me