两种选择:如果“嵌套输入”情况最多为三种,四种,则只使用标志。“拿着物体?不能开火。” 其他任何事情都在过度设计。
否则,您可以保留每个输入键的事件处理程序堆栈。
Actions.Empty = () => { return; };
if(IsPressed(Keys.E)) {
    keyEventHandlers[Keys.E].Push(Actions.Empty);
    keyEventHandlers[Keys.LeftMouseButton].Push(Actions.Empty);
    keyEventHandlers[Keys.Space].Push(Actions.Empty);
} else if (IsReleased(Keys.E)) {
    keyEventHandlers[Keys.E].Pop();
    keyEventHandlers[Keys.LeftMouseButton].Pop();
    keyEventHandlers[Keys.Space].Pop();        
}
while(GetNextKeyInBuffer(out key)) {
   keyEventHandlers[key].Invoke(); // we invoke only last event handler
}
或达到这种效果的东西:)
编辑:有人提到了难以管理的if-else构造。我们将为输入事件处理例程提供完全数据驱动的功能吗?您当然可以,但是为什么呢?
无论如何,对于它而言:
void BuildOnKeyPressedEventHandlerTable() {
    onKeyPressedHandlers[Key.E] = () => { 
        keyEventHandlers[Keys.E].Push(Actions.Empty);
        keyEventHandlers[Keys.LeftMouseButton].Push(Actions.Empty);
        keyEventHandlers[Keys.Space].Push(Actions.Empty);
    };
}
void BuildOnKeyReleasedEventHandlerTable() {
    onKeyReleasedHandlers[Key.E] = () => { 
        keyEventHandlers[Keys.E].Pop();
        keyEventHandlers[Keys.LeftMouseButton].Pop();
        keyEventHandlers[Keys.Space].Pop();              
    };
}
/* get released keys */
foreach(var releasedKey in releasedKeys)
    onKeyReleasedHandlers[releasedKey].Invoke();
/* get pressed keys */
foreach(var pressedKey in pressedKeys) 
    onKeyPressedHandlers[pressedKey].Invoke();
keyEventHandlers[key].Invoke(); // we invoke only last event handler
编辑2
Kylotan提到了按键映射,这是每个游戏都应该具备的基本功能(也要考虑可访问性)。包括键映射是另一回事。
根据按键组合或顺序更改行为是有限的。我忽略了那部分。
行为与游戏逻辑有关,而非输入。考虑一下,这是显而易见的。
因此,我提出以下解决方案:
// //>
void Init() {
    // from config file / UI
    // -something events should be set automatically
    // quake 1 ftw.
    // name      family         key      keystate
    "+forward" "movement"   Keys.UpArrow Pressed
    "-forward"              Keys.UpArrow Released
    "+shoot"   "action"     Keys.LMB     Pressed
    "-shoot"                Keys.LMB     Released
    "jump"     "movement"   Keys.Space   Pressed
    "+lstrafe" "movement"   Keys.A       Pressed
    "-lstrafe"              Keys.A       Released
    "cast"     "action"     Keys.RMB     Pressed
    "picknose" "action"     Keys.X       Pressed
    "lockpick" "action"     Keys.G       Pressed
    "+crouch"  "movement"   Keys.LShift  Pressed
    "-crouch"               Keys.LShift  Released
    "chat"     "user"       Keys.T       Pressed      
}  
void ProcessInput() {
    var pk = GetPressedKeys();
    var rk = GetReleasedKeys();
    var actions = TranslateToActions(pk, rk);
    PerformActions(actions);
}                
void TranslateToActions(pk, rk) {
    // use what I posted above to switch actions depending 
    // on which keys have been pressed
    // it's all about pushing and popping the right action 
    // depending on the "context" (it becomes a contextual action then)
}
actionHandlers["movement"] = (action, actionFamily) => {
    if(player.isCasting)
        InterruptCast();    
};
actionHandlers["cast"] = (action, actionFamily) => {
    if(player.isSilenced) {
        Message("Cannot do that when silenced.");
    }
};
actionHandlers["picknose"] = (action, actionFamily) => {
    if(!player.canPickNose) {
        Message("Your avatar does not agree.");
    }
};
actionHandlers["chat"] = (action, actionFamily) => {
    if(player.isSilenced) {
        Message("Cannot chat when silenced!");
    }
};
actionHandlers["jump"] = (action, actionFamily) => {
    if(player.canJump && !player.isJumping)
        player.PerformJump();
    if(player.isJumping) {
        if(player.CanDoubleJump())
            player.PerformDoubleJump();
    }
    player.canPickNose = false; // it's dangerous while jumping
};
void PerformActions(IList<ActionEntry> actions) {
    foreach(var action in actions) {
        // we pass both action name and family
        // if we find no action handler, we look for an "action family" handler
        // otherwise call an empty delegate
        actionHandlers[action.Name, action.Family]();    
    }
}
// //<
比我聪明的人可以在很多方面改善这一点,但我相信这也是一个很好的起点。