два варианта: если случаи «вложенного ввода» не более трех, четырех, я бы просто использовал флаги. «Держать предмет? Не могу стрелять». Все остальное переоценивает это.
В противном случае вы можете сохранить стек обработчиков событий для каждого ключа ввода.
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
Килотан упомянул раскладку клавиш, которая является основной функцией каждой игры (подумайте и о доступности). Включение раскладки клавиш - это отдельная история.
Изменение поведения в зависимости от комбинации нажатия клавиш или последовательности является ограничением. Я пропустил эту часть.
Поведение связано с игровой логикой, а не с вводом. Что довольно очевидно, если подумать об этом.
Поэтому я предлагаю следующее решение:
// //>
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]();
}
}
// //<
Это может быть улучшено многими людьми, умнее меня, но я считаю, что это также хорошая отправная точка.
Мы использовали государственную систему, как вы упоминали ранее.
Мы создали бы карту, которая будет содержать все ключи для определенного состояния с флагом, который позволит проходить ранее отображенные ключи или нет. Когда мы изменили состояние, новая карта будет нажата или предыдущая карта будет удалена.
Простой простой пример состояний ввода: Default, In-Menu и Magic-Mode. По умолчанию вы бегаете и играете в игру. In-Menu будет, когда вы находитесь в меню «Пуск», или когда вы открыли меню магазина, меню паузы, экран опций. In-Menu будет содержать флаг отсутствия прохождения, поскольку при перемещении по меню вы не хотите, чтобы ваш персонаж перемещался. С другой стороны, так же, как в вашем примере с переносом предмета, магический режим просто переназначает ключи использования действия / предмета вместо того, чтобы разыгрывать заклинания (мы также привязываем это к эффектам звука и частиц, но это немного за пределами ваш вопрос).
То, что заставляет карты выдвигаться и извлекаться, зависит от вас, и я также честно скажу, что у нас были определенные «чистые» события, чтобы убедиться, что стек карт был чистым, загрузка уровней является наиболее очевидным временем (ролики также в раз).
Надеюсь это поможет.
TL; DR - Используйте состояния и входную карту, которую вы можете нажать и / или вытолкнуть. Включите флаг, чтобы сказать, удаляет ли карта полностью предыдущий ввод или нет.
источник
Это похоже на случай, когда наследование может решить вашу проблему. Вы можете иметь базовый класс с кучей методов, которые реализуют поведение по умолчанию. Затем вы можете расширить этот класс и переопределить некоторые методы. Переключение режима - это просто вопрос переключения текущей реализации.
Вот немного псевдокода
Это похоже на то, что предложил Джеймс.
источник
Я не пишу точный код на каком-либо конкретном языке. Я даю вам идею.
1) Сопоставьте свои ключевые действия с вашими событиями.
(Keys.LeftMouseButton, left_click_event), (Keys.E, e_key_event), (Keys.Space, space_key_event)
2) Назначьте / измените ваши события, как указано ниже
Пусть ваше действие прыжка остается отделенным от других событий, таких как прыжок и огонь.
Избегайте условных проверок if..else .., поскольку это приведет к неуправляемому коду.
источник
secret cloak
потребовалось бы такие вещи, как огня, спринтом, ходить, и менять оружие , чтобы быть незарегистрированными, и разоблачают быть зарегистрированы.Вместо того, чтобы отменить регистрацию, просто сделайте состояние, а затем перерегистрируйте.
Конечно, расширение этой простой идеи может заключаться в том, что у вас могут быть отдельные состояния для перемещения и тому подобное, а затем вместо того, чтобы диктовать: «Ну, вот все, что я не могу сделать, пока в режиме секретного плаща, вот все, что я могу сделать в режиме секретного плаща.
источник