Process User Input

Coordinator
Dec 12, 2008 at 3:12 AM
Another design hurdle I'm tackling is how to interpret and process user input (mouse/keyboard).  The following is pseudo code I'm fleshing out.  I'm going in circles about whether to interpret all the input first (via something akin to the InterpretInput method) and then process it OR just process it on-the-fly as needed OR try another option.  I've not put enough time towards this subject to figure it out yet.

As always, your input is greatly valued.

Troy


private void Update()
{
...

this.Input_InterpretInput();
if (this.Input_WasEscapeKeyPressed) { // debug mode only
this.Exit();
} else if (this.Input_WasGuiObjectLeftClicked) { // e.g. Quit, Air/Ground toggle, File, Next/Previous
this.Input_HandleGuiEvent();
} else if (this.Input_WasViewportClicked) { // i.e. user clicked within viewport for unit actions
this.Input_HandleViewportInput();
}
...
}

private void InterpretInput()
{
//this.IsInAirUnitMode
//this.IsInGroundUnitMode
this.Input_WasEscapeKeyPressed = ...
this.Input_GuiIDLeftClicked = ...
this.Input_WasGuiObjectLeftClicked = (this.Input_GuiIDLeftClicked != -1);
this.Input_AirUnitIDLeftClicked = this.GetAirUnitAtMapLocation(this.MouseHexX, this.MouseHexY);
this.Input_GroundUnitIDLeftClicked = this.GetGroundUnitAtMapLocation(this.MouseHexX, this.MouseHexY);
this.Input_WasUnitLeftClicked = (this.Input_UnitIDLeftClicked != -1);
this.Input_UnitIDLeftClicked = this.GetUnitAtMapLocation(this.MouseHexX, this.MouseHexY);
this.Input_WasUnitLeftClicked = (this.Input_UnitIDLeftClicked != -1);
this.Input_HexLeftClicked = if (this.MouseHexX != -1 && this.MouseHexY != -1) { new Hex(...)
this.Input_UnitIDHoveredOver = ... (must consider air/ground toggle)
this.Input_WasViewportClicked = ...
this.Input_DidUserLeftClick = Mouse...
this.Input_DidUserRightClick = Mouse...
}

// note: LC = left mouse click, RC = right mouse click, CSU = currently-selected unit
private void HandleViewportInput()
{
switch (this.SelectionModes) {
case SelectionModes.NoUnitSelected:
if (this.Input_WasUnitRightClicked) {
this.Unit_DisplayStats(this.Input_UnitIDRightClicked);
} else if (this.Input_WasUnitLeftClicked && Unit[this.Input_UnitIDLeftClicked].Owner == this.CurrentPlayerID) {
this.Unit_Select(this.Input_UnitIDLeftClicked);
}
break;
case SelectionModes.UnitSelectedButNotMovedOrAttacked:
// if RC then deselect the CSU
if (Input.RightClicked()) {
this.MapUnits[this.SelectedUnitID].Deselect();
}
// if CSU.IsAirUnit() and LC on another friendly, unmoved air unit then select new unit
// if CSU.IsGroundUnit() and LC on another friendly, unmoved ground unit then select new unit
// if LC on movable hex then temporarily move unit
// if air unit and LC on enemy ground unit in same hex and can ground attack then attack ground unit
// if air unit and LC on adjacent enemy air unit and can air attack then attack adjacent air unit
break;
}

Dec 13, 2008 at 8:26 PM

Hi Troy,

The first that came to my head was that:

private void Update()

{
    ...
    this.Input_InterpretInput();
    ...
}

private void Input_InterpretInput()
{

    MapRegion where = Input_InterpretMapRegion();

    switch(where)
        case MapRegion.Map:
            //state machine of map
        case MapRegion.MainMenuGrid:
            //enable/disable grid

        case MapRegion.MainMenuEnd:
            //end turn

 ........
}

private MapRegion InterpretMapRegion()
{
    MapRegion where = MapRegion.None;

    if (Mouse.X > ......&& Mouse.Y > .....)
    {
        where = MapRegion.Map;
        currentHex = getHex(Mouse.X, Mouse.Y);
    }
    else if (Mouse.X > ......&& Mouse.Y > .....) //Left Window (GUI)
    {
 
 if (guiMainMenu)
 {
  if(X..Y)
             where = MapRegion.MainMenuGrid;          
          else if....
              ....where = MapRegion.MainMenuBuyUnit;
          else if..........

 }
 else //guiUnitMenu
 {
  if(X..Y)
             where = MapRegion.UnitMenuCancel;          
          else if....
              ....where = MapRegion.UnitMenuRenameUnit;
          else if..........
 }
    }
   return where;
}

but thinking it again maybe it's better to do it your way. Not sure...


Edgar. 

Dec 16, 2008 at 7:30 PM
Another option is to derive from Unit into a GraphicalUnit that has an IClickable interface that has two methods, bool handleClick(button, hexLocation.

That way you can just walk through your list of objects (possibly in Z order) and see if any of them can handle a mouse click. If it handles it, it returns true, otherwise it returns false.

Cases where you have a selected unit and you click on the gound, may need to be passed to the selected unit to process.

In that case, you might want to call selectedUnit.handleClick(...) before calling the handleClick of the unit clicked on.

Something like

hexLocation = getHex(mouseLocation);

if (hexLocation == mainPanel)
  mainPanel.handleClick(hexLocation);
else
  if (!selectedUnit.handleClick(mouseButton, hexLocation))
    clickedUnit.handleClick(mouseButton, hexLocation);

This may help keep from needing a huge case statement to handle the input, and push the responsibility down to the graphicalUnit.

It's a thought anyway.

Ralph
Coordinator
Dec 17, 2008 at 11:27 PM
Thanks for the suggestions, gents.  I'm still pondering the various ways to handle this.  I hope to flesh some prototype code for handling input this weekend. I want the prototype code to remain fairly simple but I also want to experiment a bit with what we'll use in the actual app.  I think Ralph's mechanism is more the way to go in the real-world alpha app--it sounds promising.

Troy