- PVSM.RU - https://www.pvsm.ru -
В этой части соберем все вместе и сделаем простую скроллерную стрелялку на космическую тему: корабль летит и лазерами отстреливает врагов
<<< предыдущая [1] следующая >>>
[2]
Источник [3]
Нам нужно реализовать такие режимы работы игры:
Код нужно организовать примерно так:
И еще надо нарисовать всю нужную графику и написать музыку.
Начнем с экрана заставки, потом сделаем игровой экран. Для показа жизней и очков используем Спрайт 0. Прокрутка будет вертикальная. Вот макет:
Кое-что из текстов тоже будет реализовано спрайтами, например "Пауза" и "Конец игры". Это упростит разработку.
Первый логически связный кусок игры — заставка, игровой экран и экран паузы. Переход между ними будет по кнопке Старт.
Заставку проще всего нарисовать в Фотошопе. Arial Black хорошо смотрится в названии игры, особенно после добавления небольшой перспективы и пары фильтров. Сжимаем до 128 пикселей в ширину, 4 цвета, и переносим в YY-CHR.
Корабль и звезды для фона можно делать сразу в YY-CHR. Звезды набросаем рандомно, а NES Screen Tool отлично упакует их в RLE .h файл. Текст сохраняем в таблицы имен.
Сразу же можно сделать и экран победы, временно повесив его на Селект. Музыку тоже импортирую на этом этапе, эффекты можно переключать стрелками. Потом все это удалю. Вообще, более правильно добавлять музыку в самом конце — один и тот же трек во время отладки очень быстро начинает раздражать.
Теперь можно реализовать корабль и логику его движения стрелками. Он продолжает двигаться с небольшой инерцией несколько кадров после отпускания кнопки — легкий намек на законы движения в космосе. В принципе, это можно будет отключить по итогам тестов.
Дальше идет счетчик очков, обновляемый каждый кадр. Он сделан в другой таблице имен, их подмена в середине кадра реализована через нулевой спрайт. Эта техника подробно описана в одной из статей [4].
Vert_scroll2 = ((Vert_scroll & 0xF8) << 2);
Sprite_Zero(); // ждем коллизии нулевого спрайта
PPU_ADDRESS = 0;
SCROLL = Vert_scroll;
SCROLL = 0;
PPU_ADDRESS = Vert_scroll2;
Тут возникли затруднения: через несколько секунд все разъезжалось. В отладчике FCEUX можно поставить брейкпоинты на запись в регистры, поставил их на регистры управления прокруткой — $2000, $2005, $2006. Значения записывались правильные, но установка верхней координаты экрана должна быть в V-blank, а в реальности получалась на 40-й строке. Это получилось из-за музыки, процедуры которой не поместились в V-blank. Переставил их в самый конец очереди, все стало работать нормально.
Теперь можно заняться разными режимами игры.
void main (void){
while (1) { // бесконечный цикл
while (GameMode == TITLE_MODE){
// Заставка
}
while (GameMode == RUN_GAME_MODE){
// Игра
}
while (GameMode == PAUSE_MODE){
// Пауза
}
while (GameMode == GAME_OVER_MODE){
// Конец игры
}
while (GameMode == VICTORY_MODE){
// Победа
}
}
}
Спрайтовые объекты лучше реализовать структурами:
struct ENEMY {
unsigned char anime; // номер спрайта
unsigned char dir; // направление - если 0, то отзеркаливаем влево
unsigned char Y; // верх
unsigned char X; // левый край
unsigned char delay; // задержка начала движения
unsigned char type; // тип объекта
unsigned char move; // куда его двигать
unsigned char count; // насколько уже переместился
};
Враги будут набегать волнами, так что его тип будет устанавливаться перед ее началом.
Дропбокс [5]
Гитхаб [6]
Отдельная структура для снарядов:
struct BULLET {
unsigned char Y; // y = 0 - объект за экраном, и не отображается
unsigned char Y_sub;
unsigned char tile;
unsigned char attrib;
unsigned char X;
unsigned char X_sub;
unsigned char Y_speed; // в старшем полубайте скорость, в младшем - ускорение
unsigned char X_speed;
};
Спрайты надо реализовать чуть по другому — динамически отрисовывать их каждый кадр. Спрайты лежат в буфере OAM, адреса в памяти $200-$2FF, и сначала находятся за экраном — вертикальная координата больше 0xF0. Затем отрисовываю нулевой спрайт, а после этого уже каждый активный спрайт помещаю в буфер. Порядок размещения спрайтов в буфере меняется каждый кадр, так что спрайты мерцают.
Метаспрайты бОльшего размера надо подготовить в NES Screen Tool. Код из примера Shiru мне не понравился, пришлось переписать. В частности, когда метаспрайт выходит за границу экрана, он не появляется с противоположной стороны, а просто теряется из виду. Это не вяжется с логикой игры, хоть и работает быстрее. Кроме того, возникли затруднения с отражениями спрайтов. Пришлось писать скрипт на Пайтоне, который конвертирует готовые метаспрайты в формат, удобный для импорта в код. Это слегка ускорило процесс.
На этом этапе кнопка Вниз добавляет снаряды, Селект — добавляет врагов. Для некоторых из них работает отзеркаливание. Обработка коллизий переписана на Ассемблере и позволяет увеличить количество объектов.
Дропбокс [7]
Гитхаб [6]
А теперь надо переписать все еще раз. Обработка нулевого спрайта и управление прокруткой происходят в обработчике NMI. Каждый кадр происходят примерно такие действия:
Нужно придумать способ, как красиво разместить вражеские корабли в начале каждой волны. Появляются они тоже не одновременно. Каждый враг активен и двигается, пока по нему не попадут, или пока он не уйдет за экран. Когда все враги из волны исчезнут тем или иным способом, активируется таймер Master_Delay, отсчитывающий кадры до следующей волны. Когда волны закончатся, начинается режим Босса.
У Босса есть список возможных ходов и хитпоинты, снимающиеся при попаданиях. Когда они обнулятся, включаются нехитрые звуковые эффекты и тряска экрана. После этого игра переходит в режим Победы. Из него можно начать заново, с сохранением жизней и очков.
Остальное предлагаю смотреть в коде игры. Используйте его как есть, или как основу для своего проекта. Спасибо за внимание!
Дропбокс [8]
Гитхаб [6]
Хочу поблагодарить всех, кто помогал мне изучить программирование для NES, особенно участников форума forum.nesdev.com [9].
Очень много я почерпнул из примеров кода для cc65, которые написал Shiru. Кое-что из этих примеров использовано в этом туториале. Он же автор Famitone2 и NES Screen Tool. Его сайт с играми и примерами:
https://shiru.untergrund.net/software.shtml [10]
http://shiru.untergrund.net/articles/programming_nes_games_in_c.htm [11]
Две его игры продаются на GreetingCarts (Retroscribe):
http://www.greetingcarts.com/ [12] (сайт мертв — прим. перев.)
Хочу поблагодарить THEFOX за его помощь, когда я только начинал осваивать cc65. И за примеры, которые раньше были на его сайте:
https://www.fauxgame.com/ [13]
Но поиграть в его игру, Streemerz, все равно можно.
Rainwarrior сделал демо Coltrane, и дал хороший пример работы со звуком:
http://www.rainwarrior.ca/music/coltrane_src.zip [14]
А еще у него есть игра Lizard Game:
http://lizardnes.com/ [15]
Всем спасибо!
А мне теперь надо сделать то же самое, но для приставки SNES...
Автор: Вадим Марков
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/274949
Ссылки в тексте:
[1] <<< предыдущая: https://habrahabr.ru/post/349742/
[2] Image: https://habrahabr.ru/post/350426/
[3] Источник: http://www.dailymail.co.uk/sciencetech/article-4060104/The-birth-gaming-legend-Nintendo-releases-original-drawings-used-create-Zelda.html
[4] одной из статей: https://habrahabr.ru/post/349376/
[5] Дропбокс: http://dl.dropboxusercontent.com/s/vcnifnoooflgilq/spacy.zip
[6] Гитхаб: https://github.com/BubaVV/nesdoug
[7] Дропбокс: http://dl.dropboxusercontent.com/s/fczfdpahrdgb7rl/spacy2.zip
[8] Дропбокс: http://dl.dropboxusercontent.com/s/70f89x9viu4r8mw/Spacy4.zip
[9] forum.nesdev.com: http://forum.nesdev.com
[10] https://shiru.untergrund.net/software.shtml: https://shiru.untergrund.net/software.shtml
[11] http://shiru.untergrund.net/articles/programming_nes_games_in_c.htm: http://shiru.untergrund.net/articles/programming_nes_games_in_c.htm
[12] http://www.greetingcarts.com/: http://www.greetingcarts.com/
[13] https://www.fauxgame.com/: https://www.fauxgame.com/
[14] http://www.rainwarrior.ca/music/coltrane_src.zip: http://www.rainwarrior.ca/music/coltrane_src.zip
[15] http://lizardnes.com/: http://lizardnes.com/
[16] Источник: https://habrahabr.ru/post/350426/?utm_source=habrahabr&utm_medium=rss&utm_campaign=350426
Нажмите здесь для печати.