deux options: si les cas "d'entrée imbriquée" sont au plus trois, quatre, j'utiliserais simplement des indicateurs. "Tenir un objet? Vous ne pouvez pas tirer." Tout le reste le sur-conçoit.
Sinon, vous pouvez conserver une pile de gestionnaires d'événements par clé d'entrée.
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
}
Ou quelque chose à cet effet :)
Edit : quelqu'un a mentionné des constructions if-else ingérables. allons-nous utiliser des données complètes pour une routine de gestion des événements d'entrée? Vous pourriez sûrement le faire, mais pourquoi?
Quoi qu'il en soit, pour le plaisir:
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
Modifier 2
Kylotan a mentionné la cartographie clé, qui est une fonctionnalité de base que chaque jeu devrait avoir (pensez également à l'accessibilité). Inclure le mappage de touches est une autre histoire.
Le changement de comportement en fonction d'une combinaison ou d'une séquence de touches est limitatif. J'ai négligé cette partie.
Le comportement est lié à la logique du jeu et non à la saisie. Ce qui est assez évident, à y penser.
Par conséquent, je propose la solution suivante:
// //>
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]();
}
}
// //<
Cela pourrait être amélioré à bien des égards par des gens plus intelligents que moi, mais je pense que c'est aussi un bon point de départ.
Nous avons utilisé un système d'État, comme vous l'avez mentionné précédemment.
Nous créerions une carte qui contiendrait toutes les clés pour un état spécifique avec un indicateur qui permettrait ou non le passage des clés précédemment mappées. Lorsque nous changions d'état, la nouvelle carte était activée ou une carte précédente était supprimée.
Un exemple simple et rapide des états d'entrée serait Default, In-Menu et Magic-Mode. La valeur par défaut est l'endroit où vous courez et jouez au jeu. In-Menu serait lorsque vous êtes dans le menu de démarrage, ou lorsque vous avez ouvert un menu de magasin, le menu de pause, un écran d'options. Dans le menu contiendrait le drapeau d'interdiction, car lorsque vous naviguez dans un menu, vous ne voulez pas que votre personnage se déplace. De l'autre côté, un peu comme votre exemple avec le transport de l'objet, le mode magique remapperait simplement les touches d'action / objet à la place pour lancer des sorts (nous lierions également cela aux effets sonores et aux particules, mais c'est un peu au-delà ta question).
Ce qui fait que les cartes sont poussées et sautées dépend de vous, et je dirai honnêtement que nous avons eu certains événements `` clairs '' pour nous assurer que la pile de cartes a été maintenue propre, le chargement de niveau étant le moment le plus évident (les cinématiques aussi sur fois).
J'espère que cela t'aides.
TL; DR - Utilisez des états et une carte d'entrée que vous pouvez pousser et / ou faire apparaître. Incluez un indicateur pour indiquer si la carte supprime ou non complètement l'entrée précédente.
la source
Cela ressemble à un cas où l'héritage pourrait résoudre votre problème. Vous pourriez avoir une classe de base avec un tas de méthodes qui implémentent le comportement par défaut. Vous pouvez ensuite étendre cette classe et remplacer certaines méthodes. Le mode de commutation n'est alors qu'une question de commutation de l'implémentation actuelle.
Voici un pseudo-code
C'est similaire à ce que James a proposé.
la source
Je n'écris pas le code exact dans une langue particulière. Je te donne l'idée.
1) Mappez vos actions clés à vos événements.
(Keys.LeftMouseButton, left_click_event), (Keys.E, e_key_event), (Keys.Space, space_key_event)
2) Attribuez / modifiez vos événements comme indiqué ci-dessous
Laissez votre action de saut rester découplée avec d'autres événements comme le saut et le feu.
Évitez if..else .. les vérifications conditionnelles ici car cela conduirait à un code ingérable.
la source
secret cloak
cela nécessiterait que des éléments tels que le feu, le sprint, la marche et le changement d'arme ne soient pas enregistrés et que la cape soit enregistrée.Au lieu de vous désinscrire, créez simplement un état puis réenregistrez-vous.
Bien sûr, l' extension de cette idée simple serait que vous pourriez avoir des états séparés pour déplacer, et ce, puis au lieu de dicter « Eh bien, voici toutes les choses que je ne peux pas faire tout en mode secret Cape, voici tout ce que je peut faire en mode Secret Cloak. ".
la source