Давайте попробуем написать исходный код игры. Будет использоваться полностью процедурное программирование.
По структуре игра имеет:
- Область данных
- Проверка нажатых клавиш (keyListener)
- Рендерер
- Обработчик игровых событий
С точки зрения программирования, суть задач игры следующая:
- Резервируется область данных (у нас это 25 бит, но т.к. минимальный размер данных в C - это char (255), то это будет массив из 40 бит. В нём будет храниться игровое поле. 4 char'а для "жердочек", и 1 для "корзины".
- Каждый такт игры (раз в секунду) элементы массива побитно сдвигаются (<<) в сторону.
- Если было определено, что сдвинутый бит совпадает или не совпадает с определенной маской (которая задаётся корзиной), осуществляется ветвление: либо яйцо поймано, либо не поймано с дальнейшим переходом в соответствующие процедуры.
Давайте попробуем реализовать эти задачи.
1. Резервируем область памяти. Заполним произвольными значениями:
Напоминаю нумерацию массива: 0-3 для "жердочек", 4 - для "волка". Всё просто.
2. Определимся с таймингами и таймерами
В нашей игре (если не считать звука) должно быть два независимых таймера - один отсчитывает игровые такты, второй - для рендеринга. Поэтому сразу же инициализируем их:
3. Определимся с портами ввода/вывода
Сразу же расписываю в комментарии, чтобы не запутаться самому.
В данном случае мы нуждаемся в двух программных костылях:
- Так как порт B имеет лишь 8 бит, а нам нужно 9 для игры, то девятый бит перенесём на PD6
- Оказалось, что при сборке дисплея была допущена ошибка. А именно: светодиоды проводят ток в обе стороны. Изначально я не предполагал, что так будет, но выяснилось, что при включении одних светодиодов загораются другие. Придется задействовать троичную логику, чтобы разорвать цепь, для этого манипулируем не только PORT, но и DDR регистром.
Исходя из этого, напишем дальнейшие функции.
4. Напишем функцию рендеринга построчно
С учётом вышесказанных костылей, он будет выглядеть следующим образом:
От 1 до 4 - жёрдочки, выставляются аноды и катоды согласно содержимому char'а data (наш массив cArr 0-3).
Если 5 - тогда зажигаем другой порт.
Таким образом, мы можем развернуть картинку на матрицу 4x5.
Рендеринг осуществляется по словам, так как содержимое одной жердочки и есть слово. Мы можем присвоить его порту непосредственно, лишь сравнив с маской портов (аноды и катоды) и сдвинув на нужное количество единиц.
Если рендеринг осуществлять покоординатно (x, y, d), то метод будет выглядеть по-другому, я бы его объяснил, но это займёт очень много страниц. Возможно, в другой раз.
5. Напишем функцию рендеринга всего игрового поля
Очень просто: перебор массива от 1 до 5 для построчного отображения. Эта функция будет вызываться постоянно с максимально возможной скоростью.
6. Напишем функцию принятия внешних данных (обработка нажатий кнопок):
Функция элегантна и проста, подобно осеннему вальсу.
Если на инвертированном значении PIND, который сравнивается с битовой маской 0x0F что-то произошло, т.е. его мгновенное значение стало больше нуля, значит в элемент массива 4 заносится это самое значение.
Говоря по-простому, у нас все кнопки представляют собой как бы RS триггеры, поэтому даже фильтровать дребезг и помехи на них нет смысла - они попросту невозможны. Как только выяснилось, что напряжение начинает шевелиться на ножке, значит, нужно переключить регистр (переменную), заменив данные.
Таким образом, мы управляем корзиной.
7. Напишем игровое событие.
moveEgg - сдвиг яйца. Т.е. cArr[i] << 1 - тот самый сдвиг, который и приводит к изменению картинки на экране.
Дальнейшая часть функции будет переписана, но для демонстрации пока оставим её. Т.е. если в результате сдвига, сравненного с маской 0b00010000 что-то произошло, то есть яйцо укатилось за пределы "жердочки", то мы возвращаем его обратно в начальное положение.
В дальнейшем именно в этом месте будет сравнение с битовой маской cArr[4], то есть с введёнными нами с клавиатуры (кнопок) данными.
8. Подключим таймеры
Таймер 1, срабатывающий раз в секунду. Обрабатывает событие, то есть, в нашем случае, сдвигает яйца.
В главном цикле непрерывно осуществляется рендеринг и проверка того, была ли нажата кнопка.
Есть ещё быстрый 8-битный таймер 0, пока не инициализированный, на нём будет целесообразно в дальнейшем реализовать звук.
Выполнив все эти шаги, получим следующую картину:
Нам осталось сделать, в общем то, немного:
- Сравнение с введённой битовой маской
- Наглядно изобразить процедуры ветвления, то есть, что произойдёт, если сделать то или иное действие. Например, быстро мигнуть корзиной, если яйцо поймано. Показать исчезающую корзину, если яйцо утеряно. То есть, тут уже вступают художественные и гейм-дизайнерские элементы, а не простое программирование.
- Распределение яиц в "псевдослучайном" порядке, чтобы игра была интереснее
Опционально:
- Ускорение яиц со временем, выдача более 1 яйца подряд на корзину (усложнение игры).
Попробуем реализовать всё это в следующей (5) части поста.
А пока всем спасибо, с вами был Kekovsky, писал специально для pikabu.ru, до новых встреч.