due opzioni: se i casi di "input nidificato" sono al massimo tre, quattro, utilizzerei solo i flag. "Tieni un oggetto? Non puoi sparare." Qualcos'altro lo sta ingegnando troppo.
Altrimenti, è possibile mantenere una pila di gestori di eventi per chiave di input.
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
}
O qualcosa in tal senso :)
Modifica : qualcuno ha menzionato costrutti if-else ingestibili. andremo completamente guidati dai dati per una routine di gestione degli eventi di input? Potresti sicuramente, ma perché?
Comunque, diamine:
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
Modifica 2
Kylotan ha menzionato la mappatura dei tasti, che è una caratteristica di base che ogni gioco dovrebbe avere (pensa anche all'accessibilità). Includere il keymapping è una storia diversa.
La modifica del comportamento in base a una combinazione o sequenza di tasti premuti è limitante. Ho trascurato quella parte.
Il comportamento è legato alla logica del gioco e non all'input. Il che è abbastanza ovvio, arrivando a pensarci.
Pertanto, sto proponendo la seguente soluzione:
// //>
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]();
}
}
// //<
Questo potrebbe essere migliorato in molti modi da persone più intelligenti di me, ma credo che sia anche un buon punto di partenza.