An alternative to nested if statements?

Started by
6 comments, last by Ciao 14 years, 1 month ago
Hi, I'm making a real-time strategy game in XNA (or at least trying to), and the mouse serves as the primary input device. The exact action that the mouse performs at any given time depends on a particular combination of several different conditions. For example, if the mouse is over an object and I want to target that object, I would first need to check that the object can be targeted, that I have a friendly object selected that can engage the object, that the object is an enemy and not an ally, that the mouse isn't already busy doing something else like making a selection box, whether or not any modifier keys are pressed, etc etc. If any one of these conditions happen to be false, the action would be entirely different. The solution that instantly popped into my mind was to have a colossal mess of nested if statements, but that would be hideous and terribly difficult to read. Is there a more elegant way of accomplishing this? Perhaps something like a table with conditions for columns and actions for rows? Any advice would be appreciated.
Advertisement
Sounds like you are still a long way off from starting your game. Because if you had all of the subsystems for dealing with mouse events, like the GUI, the objects, the renderer, etc. you would probably have a better idea of what to do.
Usually you would create an event when there is a mouse click and pass the event to all of the subsystems that need to be notified. They would then handle it according to their own rules which of course may be dependent on other subsystems. But like I said you would already know that if you had alot more of you game written. So try to start with more basic stuff first so you have some idea what kind of controls you will need in your game.
One way would be to use inherited objects and virtual functions.

You may need to refactor some of your code to realize this setup but...

#1 - have a base class for all "clickable" objects and store them in some structure such as a list or a grid or something.
#2 - when the player clicks, find out which clickable object they clicked on.
#3 - when the object is clicked on you could call a virtual function "Object->CanClick" which returns whether or not you can click the unit.

Then you could do things like this:

bool CBasicFriendlyUnit::CanClick(void)
{
return false;
}

bool CBasicEnemyUnit::CanClick(void)
{
return true;
}

OR!

bool CBasicEnemyUnit::CanClick(void)
{
//don't let the player select dead enemy units
return m_fHealth > 0;
}

you get what i mean? It might take a little refactoring to set things up this way, and this example is kinda simplistic, but hopefully you smell what i'm cooking.
I would build a state machine since SM are not also a logical way to implement this but also fit the thinking model of "input modes" that the players will use.
If the only thing that comes to mind for you for a critical part of the interaction with this type of game is a bunch of nested 'if' statements, then perhaps you shouldn't be jumping in quite so deep so soon. On the list of problems you're going to face in making an RTS, this one is pretty low in overall challenge. How many games, and what types, have you made before (being honest)?


That said, what you probably want is a state-machine to control what mode of operation the cursor is in, and possibly also a state machine for what modifiers keys are active (they can optionally be made 'sticky' this way), along with some common type for clickable objects.

This clickable type should provide some method or property which tells the mouse handler what type of object it is (crops/trees/stone/gold that can be harvested in some way, animals that can be hunted, enemy units that can be attacked, non-enemy units, or your own units and constructs).


So it basically works like this:

You click on a Barracks you've built, and that places the control state into the state which exposes teh functionality of the barracks, letting you select which troop type it should generate, how many, etc (This is probably a specialized use of a more general technology tree / unit production system.) After the choices are made, the new units are encqueued for production, or you've clicked outside the dialog, cancelling the action. Control is returned to its normal state.

You click on a soldier, putting the control state into the "command" state -- probably some other things happen simultaniously, such as the unit's health and status being displayed in the UI, perhaps a dialog comes up to give the unit their "standing orders". If you click again on the unit, he's deselected (or perhaps this is how you get to the "standing orders" dialog); if you click a patch of grass the unit starts moving towards that location, if you click another unit, the information the second unit returns from its click handler determines which action will happen as a result -- if its an enemy unit, the soldier attacks; if its a friendly vehicle the soldier boards it; if its a friendly unit, perhaps they form a squad. Depending on the hotkey state, decisions may be made by default (holding 'm' moves, regardless of what the clicked unit was, etc) or whatever.

throw table_exception("(? ???)? ? ???");

Quote:Original post by Ravyne
How many games, and what types, have you made before (being honest)?


Three. Two of them were simple 2D games, one was 3D, all of them were done with TorqueScript, and neither of them fell anywhere near the RTS genre. So am I biting off more than I can chew? Sure I am, but this is merely a learning experience and I'm tackling each obstacle as it comes. Now please stop trying to dissuade me.

As for the mess of if statements, perhaps that was an exaggeration. It's just that these conditions must be checked at least every draw call as the mouse's image constantly changes to reflect what will happen if the user clicks a button. Therefore, I cannot rely on events triggered by button presses to set the mouse state as the mouse must know what it's doing before the button is even pressed. So these conditions are constantly being checked to make sure the mouse is using the appropriate image. I could forgo this whole 'dynamic image' thing and it would take care of the situation nicely, but I'm not going to do that.


Actually...
I was stuck thinking the only events I would use would be for button presses, but in reality I'd also use events for several other things like mousing over or off of an object, right? Do events ever fail to fire when they're suppose to? I mean, how reliable are they? Maybe events are all I really need.
Well, sure, but you never mentioned anything about mouse-over and other similar events before :) And I don't mean to dissuade, it was just an honest question -- your initial description about how you were thinking of handling things indicated to me that you were stuck in an 'immediate mode' mindset, which, for input doesn't scale past simple platformers and such. RTS games have quite complex input models, which its clear that you realize, but not clear that you knew where to go.


So really though I think its simple -- rather than waiting for clicks, you query every frame for whatever is below the cursor, which tells you what state the the mouse image is in (which almost certainly corresponds to what state the control state will move into if you click). In other words, a mouse-over changes the visual aspects of the control state to indicate which state will become active if you click. The click itself causes the control state to enter this logical state. The new logical state handles all the subsequent input events (mouse-overs, clicks) and handles them internally, according to the rules that are made active by that state. The control state that is active tells you, implicitly, the sequence of events that has occurred to lead up to where it is now, so you don't really have to "remember" and make all these decisions at the end... In stead, you make a series of small decisions (I clicked on type X, which led to state Y) that lead down a path which describes the full action sequence. When an action sequence is complete, it sets up the action and then returns to the original state. Some non-valid action (say, clicking outside of a modal dialogue, clicking "cancel" or "X") might either return you to the origin state, or step back a state, as appropriate.

throw table_exception("(? ???)? ? ???");

I realize my initial post omitted several important details. In fact, after reading over it, I see my question wasn't very clear at all. I apologize for that.

Anyway, thank you for your input (pun intended).

This topic is closed to new replies.

Advertisement