Приветствую всех.
Данный пост будет о том, как написать очередную простейшую игрушку на языке программирования FBD (конечно данная реализация языка программирования отходит от стандартов FBD, но большинство «особых фишек» не используется, поэтому написанная программа может быть легко перенесена на классический FBD в любой системе), загрузить программу в контроллер и предаться воспоминаниям о начале двухтысячных.
Недавно с одним из коллег зашел разговор про начало двухтысячных, и почему-то вспомнились первые наши мобильные телефоны Siemens и игра Wappo. В итоге сама собой родилась идея написать эту игрушку использую средства проектирования ПТК «Квинт 7». Т.к. больших усилий и кучи времени на это не требовалось, то вот результат:
На все это дело затрачено две половинки обеденных перерывов (совместно с распитием кофе) и пара часов вечером на рисование картинок в MS Paint.
В игре «Wappo» размер игрового поля 6х6 клеток. В начальной позиции на поле располагаются:
При этом игрок за один ход может сдвинуть своего персонажа на одну клетку в любую сторону при условии, что на пути у него нет стены. Монстр же передвигается на две клетки за ход по направлению к персонажу игрока. При этом приоритет отдается передвижению по горизонтали. Если монстр не может двигаться (у него на пути стоит стенка), то он пропускает ход.
Игра считается законченной победой, если игроку удалось довести своего персонажа до клетки с домиком, и поражением, если монстр своим ходом встал на ту же клетку, где стоит персонаж игрока.
Начнем, как обычно, с технологической программы для контроллера.
Вся программа будет состоять из пяти больших блоков:
Рассмотрим подробнее каждый из этих блоков.
Общий вид алгоритмов управления игрой:
Тут все просто. Для управления используются два алгоритма «РучСелектор». Первый алгоритм служит для запуска и останова игры а так же для подачи команд на передвижение человечка. Селектор работает по принципу «один из n». Длительность формирования выходного сигнала — 1 мсек (т.е. один цикл контроллера). Первый выход (кнопка «старт») взводит триггер «Идет игра». Пока триггер взведен происходит обработка всех остальных команд. Одновременно этот же выход подает команду на блоки управления ходами для установки начальной позиции уровня. Второй выход сбрасывает все триггера в игре (в том числе и главный триггер «Идет игра») и очищает все элементы памяти.
Второй алгоритм «РучСелектор» используется для выбора уровня. Как видно из картинки, на данный момент реализовано три уровня. Соответственно, если количество уровней будет возрастать, то одновременно нужно будет увеличить модификатор алгоритма. На выходе второго селектора стоит простейшая схема, которая проверяет выбранное значение и в случае, если игрок запустил игру не выбрав уровня (значение на выходе равно нулю), принудительно присваивает первый уровень.
Еще два алгоритма «видимость» и «блокировка» используются для отображения/скрытия некоторых элементов графики, а так же для установки блокировки на подачу команд игроком. Зачем нужна эта блокировка будет пояснено чуть ниже.
Общий вид макроса «клетка»
Сама по себе обработка условий примитивна. Как мы с вами чуть выше выяснили, каждая ячейка формирует признаки запрета движения в определенную сторону а так же признаки победы и поражения в игре. Дальше все эти признаки просто собираются по логическому «ИЛИ» для использования в алгоритме обработки ходов.
Сам блок обработки хода игрока очень прост:
Две идентичные нитки для обработки движения по вертикали и горизонтали.
Рассмотрим подробнее первую:
Вот и вся обработка. Просто и понятно.
Тут еще проще, чем с ходом игрока:
А осталось там не так уж и много, всего несколько алгоритмов.
— алгоритм «Исходная позиция»:
В нем задаются координаты стенок, домика и начальная позиция персонажа игрока и помидорки. На текущий момент таких позиций три (всего три уровня), но просто добавив модификатор к алгортиму и вбив новые координаты, можно увеличить число уровней до трехсот без каких либо переделок основной программы.
— блок распределения текущей координаты персонажа игрока и помидорки:
Тут тоже все просто. Соответствующий вектор передается на текущий номер строки, далее раскладывается на биты и получившиеся логические сигналы уходят на свои алгоритмы «Ячейка».
После того, как все блоки у нас готовы, остается их только скомпоновать (т.е. красиво расположить в поле) и программа готова:
Вот на эту графическую часть ушла большая часть времени. Нужно было искать в интернете картинки, подрисовывать их под свой размер. Некоторые картинки найти сходу не получилось и оказалось, что их проще и быстрее самому в Paint нарисовать. Но так или иначе с набором картинок было покончено и пошла сама отрисовка игрового процесса, которая заняла минут пять. Делаем один элемент (клетку). Добавляем ему анимацию содержимого и стенок. Копируем 35 раз и получаем готовое поле. Обрисовываем вокруг кирпичной стеной, добавляем пару надписей и кнопок для управления и все готово.
В данном примере мы сделали классическую игру, однако беглый поиск по ютубу показал, что в игре еще много чего придумано. Сделать это на основе приведенной выше программы не составляет труда, однако я не стал этим заниматься, чтобы не загромождать основу.
Попробуем навскидку оценить, что и как из дополнительных возможностей можно реализовать:
Программа готова и отлично работает. Основа программы позволяет расширять ее без кардинальных изменений. При этом на само «программирование» затрачено совсем немного времени. Более того, программирование на языке FBD (графический язык программирования стандарта МЭК 61131-3) просто, понятно и не требует глубоких знаний. Все что нужно, это владеть элементарной логикой и математикой на школьном уровне и знать, как работают простейшие алгоритмы типа триггеров и счетчиков.
На этом все. Надеюсь было интересно.
Данный пост будет о том, как написать очередную простейшую игрушку на языке программирования FBD (конечно данная реализация языка программирования отходит от стандартов FBD, но большинство «особых фишек» не используется, поэтому написанная программа может быть легко перенесена на классический FBD в любой системе), загрузить программу в контроллер и предаться воспоминаниям о начале двухтысячных.
Недавно с одним из коллег зашел разговор про начало двухтысячных, и почему-то вспомнились первые наши мобильные телефоны Siemens и игра Wappo. В итоге сама собой родилась идея написать эту игрушку использую средства проектирования ПТК «Квинт 7». Т.к. больших усилий и кучи времени на это не требовалось, то вот результат:
На все это дело затрачено две половинки обеденных перерывов (совместно с распитием кофе) и пара часов вечером на рисование картинок в MS Paint.
Постановка задачи
В игре «Wappo» размер игрового поля 6х6 клеток. В начальной позиции на поле располагаются:
- Домик (или выход) — цель игры провести своего персонажа в эту клетку не попавшись противнику. В нашем варианте данная клетка обозначается домиком с газоном и синим небом;
- Монстр — противник, который стремится поймать игрока. В нашем варианте это помидорка;
- Игрок — персонаж игрока, которого нужно провести в домик. В нашем варианте это сырный человечек;
- Стенки — препятствия между клетками, запрещающие проход игроку и монстру в соседнюю клетку.
При этом игрок за один ход может сдвинуть своего персонажа на одну клетку в любую сторону при условии, что на пути у него нет стены. Монстр же передвигается на две клетки за ход по направлению к персонажу игрока. При этом приоритет отдается передвижению по горизонтали. Если монстр не может двигаться (у него на пути стоит стенка), то он пропускает ход.
Игра считается законченной победой, если игроку удалось довести своего персонажа до клетки с домиком, и поражением, если монстр своим ходом встал на ту же клетку, где стоит персонаж игрока.
Реализация
Начнем, как обычно, с технологической программы для контроллера.
Вся программа будет состоять из пяти больших блоков:
- Управление;
- Обработка хода игрока;
- Обработка хода помидорки;
- Обработка ячеек поля;
- Обработка условий;
Рассмотрим подробнее каждый из этих блоков.
Управление
Общий вид алгоритмов управления игрой:
Тут все просто. Для управления используются два алгоритма «РучСелектор». Первый алгоритм служит для запуска и останова игры а так же для подачи команд на передвижение человечка. Селектор работает по принципу «один из n». Длительность формирования выходного сигнала — 1 мсек (т.е. один цикл контроллера). Первый выход (кнопка «старт») взводит триггер «Идет игра». Пока триггер взведен происходит обработка всех остальных команд. Одновременно этот же выход подает команду на блоки управления ходами для установки начальной позиции уровня. Второй выход сбрасывает все триггера в игре (в том числе и главный триггер «Идет игра») и очищает все элементы памяти.
Второй алгоритм «РучСелектор» используется для выбора уровня. Как видно из картинки, на данный момент реализовано три уровня. Соответственно, если количество уровней будет возрастать, то одновременно нужно будет увеличить модификатор алгоритма. На выходе второго селектора стоит простейшая схема, которая проверяет выбранное значение и в случае, если игрок запустил игру не выбрав уровня (значение на выходе равно нулю), принудительно присваивает первый уровень.
Еще два алгоритма «видимость» и «блокировка» используются для отображения/скрытия некоторых элементов графики, а так же для установки блокировки на подачу команд игроком. Зачем нужна эта блокировка будет пояснено чуть ниже.
Обработка ячеек поля
Общий вид макроса «клетка»
Содержимое макроса и краткое описание
Макрос имеет 5 входов и 7 выходов:
Макрос имеет 5 входов и 7 выходов:
Входы:
- Стенки — пять логических признаков наличия у ячейки стенок сверху/справа/снизу/слева, а так же признак того, что в этой ячейке располагается «домик». Эти признаки статические и задаются один раз при старте уровня;
- Позиция — ненужный вход. Оставил его только для наглядности. Смысл в том, что каждая клетка должна проверять возможность движения в соседние клетки и выдавать соответствующие блокировки (запрет_вверх / запрет_вправо / запрет_вниз / запрет_влево). Но блокировка движения возникает не только тогда, когда между соседними клетками стоит стенка, но и когда клетка находится на краю поля. Например, из клетки с координатами (1;1) запрещено движение вверх и влево (т.к. там больше нет игрового поля). В текущем варианте для проверки этого условия в макросе используются 4 алгоритма сравнения, которые сравнивают координату клетки по строке и столбцу с 1 и 6 и формируют соответствующие запреты. Однако внимательные читатели сразу скажут, что можно упростить алгоритм, убрав эти сравнения и назначив у крайних клеток «виртуальные стенки» со стороны края поля. Однако задачи сделать идеальный алгоритм перед нами не стоит, поэтому решено было оставить некоторую избыточность в задаче получив при этом лучшую наглядность для примера;
- Ход игрока — логический признак того, что в данный момент происходит ход игрока. По данному признаку проверяются блокировки отдельно для игрока и помидорки;
- Cheese — логический признак того, что в данной клетке находится персонаж игрока;
- Tomato — логический признак того, что в данной клетке находится злая помидорка.
Выходы:
- Запрет_U/R/B/L — запрет движения в соответствующую сторону;
- Выход — основной параметр клетки. Используется для отображения состояния клетки в графической части. 0 — клетка пустая, 1 — в клетке персонаж игрока, 2 — в клетке помидорка, 3 — в клетке домик. Данный выход формируется последовательно тремя алгоритмами выбор (алгоритмы 5,6 и 7);
- Убежал — логический признак того, что персонаж игрока попал на клетку с домиком и уровень пройден;
- GameOver — логический признак того, что помидорка догнала персонажа игрока и игра проиграна;
Обработка условий
Сама по себе обработка условий примитивна. Как мы с вами чуть выше выяснили, каждая ячейка формирует признаки запрета движения в определенную сторону а так же признаки победы и поражения в игре. Дальше все эти признаки просто собираются по логическому «ИЛИ» для использования в алгоритме обработки ходов.
Обработка условий
Несколько слов об остальных алгоритмах (с алгоритмами «ИЛИ» вопросов быть не должно).
Так вот, помимо нескольких алгоритмов «или» в обработке используется несколько триггеров и алгоритм «Задержка». Главный триггер тут «Ход игрока». Данный триггер взводится при старте игры. После совершения удачного хода игроком данный триггер сбрасывается, и начинается обработка хода помидорки. Для того, чтобы действия помидорки были более или менее понятны, применен алгоритм из трех подряд задержек на 200 мсек каждая. Как только игрок совершает удачный ход, сбрасывается триггер «Ход игрока» и одновременно импульс по обратному фронту поступает на задержку в 200 мсек. После отсчета времени помидорка делает первый из двух шагов и запускает следующий таймаут опять на 200 мсек. Затем помидорка делает свой второй шаг и опять запускает таймаут на 200 мсек. После третьей выдержки времени триггер «Ход игрока» взводится и управление передается игроку.
Все вышесказанное сделано только для того, чтобы ходы противника были не мгновенные, а отображались последовательно на экране. Это гораздо приятнее и удобнее, чем видеть мгновенную смену позиции. На время всех этих задержек выставляется признак блокировки, чтобы игрок видел, что компьютер «думает» и не нажимал кнопочки управления лишний раз. В логике все нормально и никаких лишних команд не пройдет, но у человека это обычно вызывает недоумение: «Как так? Я жму, а оно не реагирует!». От подобных ситуаций лучше избавляться.
Несколько слов об остальных алгоритмах (с алгоритмами «ИЛИ» вопросов быть не должно).
Так вот, помимо нескольких алгоритмов «или» в обработке используется несколько триггеров и алгоритм «Задержка». Главный триггер тут «Ход игрока». Данный триггер взводится при старте игры. После совершения удачного хода игроком данный триггер сбрасывается, и начинается обработка хода помидорки. Для того, чтобы действия помидорки были более или менее понятны, применен алгоритм из трех подряд задержек на 200 мсек каждая. Как только игрок совершает удачный ход, сбрасывается триггер «Ход игрока» и одновременно импульс по обратному фронту поступает на задержку в 200 мсек. После отсчета времени помидорка делает первый из двух шагов и запускает следующий таймаут опять на 200 мсек. Затем помидорка делает свой второй шаг и опять запускает таймаут на 200 мсек. После третьей выдержки времени триггер «Ход игрока» взводится и управление передается игроку.
Все вышесказанное сделано только для того, чтобы ходы противника были не мгновенные, а отображались последовательно на экране. Это гораздо приятнее и удобнее, чем видеть мгновенную смену позиции. На время всех этих задержек выставляется признак блокировки, чтобы игрок видел, что компьютер «думает» и не нажимал кнопочки управления лишний раз. В логике все нормально и никаких лишних команд не пройдет, но у человека это обычно вызывает недоумение: «Как так? Я жму, а оно не реагирует!». От подобных ситуаций лучше избавляться.
Обработка хода игрока
Сам блок обработки хода игрока очень прост:
Две идентичные нитки для обработки движения по вертикали и горизонтали.
Рассмотрим подробнее первую:
- Алгоритмы «И» 69 и 70 проверяют сборку условий для движения вверх и вниз соответственно. Если взведен первый триггер «Идет игра», взведен второй триггер «Ход игрока», пришла команда с третьего для движения вверх или пятого для движения вниз выхода управляющего Селектора и нет блокировки движения в данную сторону, то
- На алгоритмах «Выбор» 71 и 72 в зависимости от сработавшего условия передается либо "+1" либо "-1" к текущей координате;
- На алгоритме «Выбор» 73 проверяется, что к нам пришел сигнал начала игры и нужно выставить исходную позицию. В противном случае передается команда, сформированная ранее;
- На алгоритме «Сложение» новое значение складывается с хранящимся на обратной связи значением и получается новая координата;
- На алгоритме «выбор» 75 проверяется, что к нам пришел сигнал сброса (второй выход управляющего селектора) и в случае его прихода обнуляется хранящееся на обратной связи значение;
- С выхода алгоритма «Сложение» новая координата персонажа игрока уходит на блок «Распределение», через который попадает в соответствующую клетку;
- Алгоритм «ИЛИ» 85 нужен для определения того, что у нас был удачный ход. Т.е. если все четыре условия на одним из четырех «И» собрались, то игрок сделал удачный (удачный не в смысле «хороший», а в смысле «разрешенный правилами») ход, и нам пора сбрасывать триггер «Ход игрока» и передавать ход помидорке.
Вот и вся обработка. Просто и понятно.
Обработка хода помидорки
Тут еще проще, чем с ходом игрока:
- На алгоритм «ИЛИ» 89 (который тут кстати не нужен и оставлен только для наглядности) приходит задержанный на 200 мсек импульс с обратного фронта по сбросу триггера «Ход игрока» (помните такую штуку в блоке обработки условий?). Далее сравнивается текущая координата помидорки и текущая координата персонажа игрока. На основании сравнения формируется команда "+1" или "-1" для хода по горизонтали и вертикали. Далее проверяются условия для хода по горизонтали (алгоритмы «И» 93 и 94, здесь проверка аналогичная с обработкой хода игрока за тем лишь исключением, что триггер «Ход игрока» должен быть сброшен) и команда через аналогичную цепочку выборов передается на алгоритм «Сложение» 101;
- Если условия на возможность хода по горизонтали не выполнились, то этот инверсный признак подается на алгоритмы «И» 106 и 107, где проверяются остальные условия возможности хода по вертикали;
- После того, как ход сделан, идет отсчет таймера еще на 200 мсек (алгоритм «Задержка» 90), после которого вся логика проверяется еще раз для второго шага помидорки и запускается третий таймер (алгоритм «Задержка» 91) на 200 мсек;
- По срабатыванию третьего таймера взводится триггер «Ход игрока»;
- Еще одно отличие от схемы обработки хода игрока заключается в том, что здесь использованы алгоритмы «Сложение» 98 и 111 для определения направления движения помидорки (вверх-вниз или влево-вправо). Т.к. одновременно оба условия на сравнении у нас выполнится не могут, то можно просто складывать наши команды (одно из слагаемых всегда будет равно нулю), получая на выходе нужное направление движения.
Пару слов о том, что осталось за кадром
А осталось там не так уж и много, всего несколько алгоритмов.
— алгоритм «Исходная позиция»:
Вид алгоритма
В нем задаются координаты стенок, домика и начальная позиция персонажа игрока и помидорки. На текущий момент таких позиций три (всего три уровня), но просто добавив модификатор к алгортиму и вбив новые координаты, можно увеличить число уровней до трехсот без каких либо переделок основной программы.
— блок распределения текущей координаты персонажа игрока и помидорки:
Распределение текущих координат
Тут тоже все просто. Соответствующий вектор передается на текущий номер строки, далее раскладывается на биты и получившиеся логические сигналы уходят на свои алгоритмы «Ячейка».
Общий вид техпрограммы
После того, как все блоки у нас готовы, остается их только скомпоновать (т.е. красиво расположить в поле) и программа готова:
Прикручиваем графическую часть
Вот на эту графическую часть ушла большая часть времени. Нужно было искать в интернете картинки, подрисовывать их под свой размер. Некоторые картинки найти сходу не получилось и оказалось, что их проще и быстрее самому в Paint нарисовать. Но так или иначе с набором картинок было покончено и пошла сама отрисовка игрового процесса, которая заняла минут пять. Делаем один элемент (клетку). Добавляем ему анимацию содержимого и стенок. Копируем 35 раз и получаем готовое поле. Обрисовываем вокруг кирпичной стеной, добавляем пару надписей и кнопок для управления и все готово.
Исходный рисунок (игра еще не запущена):
Выглядит как мешанина элементов, где ничего не понятно. Но нужно принимать во внимание тот факт, что при запущенной программе часть элементов будет скрыта в зависимости от условий, и в целом картинка будет простой и понятной.
Выглядит как мешанина элементов, где ничего не понятно. Но нужно принимать во внимание тот факт, что при запущенной программе часть элементов будет скрыта в зависимости от условий, и в целом картинка будет простой и понятной.
Запустили:
Вот тут видно, что после запуска режима просмотра данных из контроллера часть элементов стала невидимой, тем самым устранив мешанину, получившуюся у нас в режиме рисования.
Вот тут видно, что после запуска режима просмотра данных из контроллера часть элементов стала невидимой, тем самым устранив мешанину, получившуюся у нас в режиме рисования.
Играем:
Проигрываем:
И побеждаем:
Нереализованное
В данном примере мы сделали классическую игру, однако беглый поиск по ютубу показал, что в игре еще много чего придумано. Сделать это на основе приведенной выше программы не составляет труда, однако я не стал этим заниматься, чтобы не загромождать основу.
Попробуем навскидку оценить, что и как из дополнительных возможностей можно реализовать:
- Варианты игры (или уровни) с двумя монстрами. Никаких проблем. В макрос «Ячейка» добавляется еще один логический признак, что в клетке находится «помидорка 2». В исходную позицию добавляется еще две координаты начальной позиции «помидорки 2». Блок обработки хода помидорки копируется и привязывается к этим выходам. Блок распределения координаты так же копируется и привязывается к добавленному входу в макросе «Ячейка». Все. Примерное время реализации — десять минут (чтобы аккуратно все расставить). Единственный вопрос: что делать если в ходе игры обе помидорки занимают одну и ту же клетку. И вообще могут ли они это делать. Но это уже вопрос касательно правил игры.
- Варианты игры (или уровни) с ловушками. Никаких проблем. После удачного хода игрока сбрасывается триггер «Ход игрока» и по этому сбросу запускается обработка хода помидорки. Всего то и нужно, что в макрос «Ячейка» добавить признак, что в данной клетке находится ловушка. При попадании в нее взводится триггер запрета хода и запускается счетчик импульсов на обработку хода помидорки. Пока триггер взведен, этот сигнал на блок обработки хода не передается (достаточно сложить его по «и» с инвертированным выходом триггера). Триггер запрета хода сбрасывается по достижению счетчиком пропущенных ходов заданного числа ходов. Примерное время реализации минут десять.
- Клетка-телепорт. В определенных версиях (или уровнях) игры есть две помеченные клетки, при попадании на которые персонаж игрока телепортируется во вторую клетку. Опять же данная функция реализуется без проблем. Нужен один логический признак на входе макроса «Ячейка», что в данной клетке стоит «телепорт». В блок обработки хода игрока добавляется еще один алгоритм «Выбор», который принудительно при попадании на эту клетку-телепорт подставляет на вход алгоритма «Сложение» координаты связанной с ней клетки. Время реализации — минут десять.
- Количество уровней. Новые уровни добавляются элементарно. Увеличивается модификатор на алгоритме «Селектор» и соответственно увеличивается модификатор алгоритма «Нач_позиция». Далее вбиваются начальные координаты стенок, домика, помидорки и игрока и все готово. Время на реализацию — зависит от количество уровней, которые нужно добавить. На добавление двух уровней у меня ушло всего лишь несколько минут.
Краткие итоги
Программа готова и отлично работает. Основа программы позволяет расширять ее без кардинальных изменений. При этом на само «программирование» затрачено совсем немного времени. Более того, программирование на языке FBD (графический язык программирования стандарта МЭК 61131-3) просто, понятно и не требует глубоких знаний. Все что нужно, это владеть элементарной логикой и математикой на школьном уровне и знать, как работают простейшие алгоритмы типа триггеров и счетчиков.
На этом все. Надеюсь было интересно.
Комментарии (6)
Evgeny42
02.03.2016 13:46-1Помогите санкционному сыру убежать от экскаватора.
OPCSenator
02.03.2016 15:21+2Давайте без политики. Вот если есть вопросы по статье (или предложения, идеи), излагайте не стесняясь. А вот подобные комментарии лучше делать на иных ресурсах.
Evgeny42
02.03.2016 16:54-1Вы первый про сыр начали :)
OPCSenator
02.03.2016 17:37+2Ну у меня никаких ассоциаций с нашумевшей историей не было вообще. Вместо "сырного" мог быть какой-нибудь "пряничный" человечек. Это просто забавная картинка, не несущая никаких скрытых смысловых нагрузок. Ну а остальное каждый додумывает сам для себя.
impwx
Как раз недавно вспоминал про эту замечательную игру, но ваша версия с радующимся сырком вне конкуренции.
OPCSenator
Поиск в гугле по запросу "game wappo" выдает ужасы с монстрами, пожирающими мозги. Нужно быть добрее. Поэтому в качестве картинок были выбраны смешные человечки.