МАРСОХОД

Open Source Hardware Project

Проекты Altera Quartus II для платы Марсоход

Видеоигра Питон

После создания игры Теннис и Теннис для двоих я долго раздумывал, можно ли сделать на плате Марсоход еще что нибудь эдакое. Проблем здесь на самом деле хватает и самая главная - это ограниченные ресурсы нашей микросхемы ПЛИС. Для создания более или менее сложной игры хорошо бы иметь память. Тот же Тетрис не сделать без памяти. Если стакан для Тетриса будет шириной 10 кубиков, а высота 20 кубиков, то вот уже нужно 200 бит только для хранения текущего состояния игры.

Поразмыслив, я решил оставить Тетрис "на потом", а пока попробовать сделать что нибудь попроще, например, игру "Питон". Дело оказалось очень непростое. И вот, потратив несколько вечеров, я сделал вот что:

Далее расскажу, как я это делал.

Ну собственно аппаратная часть у меня осталась без изменений, такая, как и раньше.  Сигналы видео R, G и B выходят на разъем платы Марсоход с контактами F3, F4, F5 соответственно. Сигналы синхронизации выходят на контакты F1 (HSYNC) и F0 (VSYNC). Все эти сигналы (и еще "Земля") идут на разъем VGA. Ну еще хорошо бы подать сигналы через резисторы. Некоторым мониторам не нравится слишком высокий уровень сигнала.

Теперь о самой игре. Конечно нужно было придумать как хранить состояние змейки в ПЛИС. Поскольку змейка после нескольких поворотов выглядит как ломаная линия я решил, что самый правильный способ - хранить информацию о каждом сегменте в отдельности. Для представления каждого сегмента нужно знать X и Y координаты одного конца сегмента, длину сегмента, его ориентацию и направление движения. Если считать, что поле движения - это квадрат 16х16, то для представления координат нужно два 4-х разрядных регистра, для представления длины - еще один 4-х разрядный регистр, ну и для запоминания ориентации сегмента и направления движения еще по одному регистру. Итого: 4+4+4+1+1 = 14. Четырнадцать триггеров для хранения одного сегмента. В моей программе на языке Verilog это выглядит так:


reg [3:0]seg0_x;
reg [3:0]seg0_y;
reg [3:0]seg0_len;
reg seg0_vert; //0-horz, 1-vert
reg seg0_mov; //0-left/up, 1-right/down


Я решил, что для начала мне хватит и трех сегментов. Ну вот такое ограничение. Змейка не может делать больше 2-х поворотов.

Представление змейки в ввиде сегментов кажется довольно удобным, поскольку при очередном повороте второй сегмент становится третьим, первый становится вторым, а сам первый становится новым и очень коротким сегментом.

Дальше во время движения длина первого сегмента увеличивается, а длина последнего (у которого длина не нулевая) уменьшается.

Все вот так и кажется просто, но при реализации задуманного на самом деле я столкнулся с массой проблем. Smile

В проекте нужно описать поведение каждого регистра каждого сегмента в отдельности и для этого нужно четко представлять себе что же происходит с сегментом при том или ином событии.

Вот, например, рассмотрим движение короткой змейки гризонтально и потом ее поворот:

xcoord1 - поведение сегмента змейки в видеоигре

Змейка двигалась вправо и должна повернуть на право. У первого сегмента были координаты [1,2] и была длина 5 (фиолетовым цветом на рисунке слева), а стали [5,2] и длина 1 (желтым цветом на рисунке справа). Кроме того, появился второй сегмент, которого раньше не было. Его координаты [1,2] и длина 4.

При движении в другую сторону. Поведение первого сегмента будет иным:

xcoord2 - поведение сегмента змейки в видеоигре  

Теперь змейка движется влево и при повороте, координаты первого сегмента остаются предыдущими, но только меняется длина.

Поведение только координаты X для первого сегмента змейки я определил на языке Verilog вот так:


//define behave of snake segment0 x-coord
always @(posedge clk_slow or posedge game_reset)
begin
 if
(game_reset)
  seg0_x <= 1; //on game reset snake starts from fixed place
 else
 begin
  //X coord can change only if horizontal segment
  if(!seg0_vert)
  begin
   if
(turn)
    seg0_x <= seg0_x + ((seg0_len - 1) & {4{seg0_mov}});
   else
    //seg0_x may be increased or decreased by 1 or stay unchanged
    seg0_x <= seg0_x +
{!seg0_mov,!seg0_mov,!seg0_mov,( (seg0_mov ^ tail0) & (~eat) ) | (!seg0_mov)};
  end
 end
end


Довольно мудрено, правда? Ведь поведение регистра как оказалось зависит от ориентации сегмента, направления движения и факта съедания кролика. Теперь представьте, что мне нужно описать одновременное поведение всех остальных регистров всех сегментов на все случаи жизни... В общем оказалось не очень просто - ведь они меняют состояние одновременно по событию шаг или событию поворот.  

Следующая проблема - останов игры, когда змейка врезается в стенку. Первое, что приходит на ум - сравнение координат головы змейки и координат бордюра. На самом деле этот метод для меня оказался не очень хорош, так как в этом случае пришлось бы устанавливать в проект  несколько сравнивателей, а это довольно много занимает места. Не подходит.

Решение пришло следующее. Я рисую бордюр зеленым цветом, а змейка у меня фиолетовая (красный на синем фоне). Если змейка наползет на бордюр, то в этом месте экрана появится желтый цвет - это может служить признаком останова игры. Вот так и было сделано:


//when snake head comes over border then color is mixed on screen and we should stop
reg stop;
always @(posedge char_clock or posedge game_reset)
 if(game_reset)
  stop = 0;
 else
  if
(video_r & video_g)
   stop = 1;


Примерно такой же механизм был использован и для определения факта съедания кролика. Кролик у меня один, он на экране черным цветом. Когда змейка наползает на него, то в этом месте экрана получается красный цвет (видимо кровища). Когда одновременно луч видеосигнала на экране рисует и первый сегмент (seg0_visible==1) и рисует кролика (rabbit_visible), то запоминаем факт съедания:


//calculate when rabbit was eaten
//if during screen refresh we meet simultaneous snake segment visible and rabbit over
reg eat;
always @(posedge char_clock or posedge game_reset)
 if(game_reset)
  eat <= 0;
 else
 if
(seg0_visible & rabbit_visible)
  eat <= 1;
 else
 if
(clk_slow)
  eat <= 0;


Конечно в  программе у меня много чего там. Я только обозначаю основные ключевые моменты.

Вот то же проблема. Сначала я хотел сделать появление кролика в случайных местах. Ну там можно было бы сделать какой-то псевдо-случайный выбор места нового кролика. К сожалению места в микросхеме не хватило на все это. В результате кролик имеет у меня всего два положения и описывается одним битом. Smile Ну не страшно. Мне главное идею продемонстрировать. В результате и так у меня проект в чипе ПЛИС занял 239 логических элемента из 240!  

Как обычно, проект целиком можно взять на нашем сайте: icon Игра ПИТОН (50.59 Кбайт)

Пробуйте!

PS: Возможно вы обнаружите несколько глюков. Так, например, после съедания кролика не нужно тут же поворачивать, а то змейку стошнит и ее длина не увеличится. Это из за того, что сигнал turn имеет больший приоритет. 

Еще проблема - если держать кнопку поворота чуть дольше, чем нужно змейка начинает ползти поверх себя и игра ломается. Может вы придумаете что-нибудь..

Комментарии  

+1 #9 Владимир Хлуденьков 24.03.2014 05:11
Цитирую Вадим:
По моему выбранна неоптимальная схема хранения змейки.
3 * 14 = 42 бит.
можно хранить так.
4+4 координаты головы.
а дальше кодируем переходы от головы до следующей клетке где лежит следующий квадрат тела. у нас 4 направления - то есть на каждый переход надо 2 бита + длина змейки (количество переходов)
предположим у наз змейка из трех квадратов то надо 8+2+2 = 12 бит змейка + длина 2 перехода. пусть переходов будет от 0 до 15 (длина 1-16) получится в итоге 8 + 16 * 2 + 4 = 44 бита и нет ограничений на повороты.
кроме того при поворотах нам достаточно сдвигать регистр переходов и записывать в его начало новое направление от головы. а когда нужно увеличить длину - то же самое + инкремент длины.


Я думаю лучше всего хранить координаты, начиная от хвоста an[x, y]. А координаты близлежащих к голове секторов определять просто как указано выше. Но пересчёт координат при движении будет легче.
0 #8 Вадим 16.12.2011 07:11
По моему выбранна неоптимальная схема хранения змейки.
3 * 14 = 42 бит.
можно хранить так.
4+4 координаты головы.
а дальше кодируем переходы от головы до следующей клетке где лежит следующий квадрат тела. у нас 4 направления - то есть на каждый переход надо 2 бита + длина змейки (количество переходов)
предположим у наз змейка из трех квадратов то надо 8+2+2 = 12 бит змейка + длина 2 перехода. пусть переходов будет от 0 до 15 (длина 1-16) получится в итоге 8 + 16 * 2 + 4 = 44 бита и нет ограничений на повороты.
кроме того при поворотах нам достаточно сдвигать регистр переходов и записывать в его начало новое направление от головы. а когда нужно увеличить длину - то же самое + инкремент длины.
+2 #7 Петр 12.04.2011 23:18
Спасибо за ответ!
Потихоньку разбираюсь!
А как насчет вывода текста?
+2 #6 nckm_ 12.04.2011 04:45
Цитирую Петр:
Здравствуйте!
Подскажите начинающему - Вы с помощью плис, работающей на 5 МГц, выводите изображение на монитор?
Я пробовал на at91sam7x256 со 100 МГц, но результаты плачевны.
Большое спасибо за сайт - читаю с удовольствием!

ну да. В видео режиме 800x600 60Гц согласно спецификации VESA PixelClock = 40Mhz, если у меня булет 5Мгц, то период как раз занимает 8 точек на экране. И игра теннис и игра питон у меня сделаны кубиками 16Х16
+2 #5 Петр 11.04.2011 23:40
Здравствуйте!
Подскажите начинающему - Вы с помощью плис, работающей на 5 МГц, выводите изображение на монитор?
Я пробовал на at91sam7x256 со 100 МГц, но результаты плачевны.
Большое спасибо за сайт - читаю с удовольствием!
-1 #4 Евгений 11.04.2011 14:46
Циклон 3 я думаю будет более востребован чем мелкая CPLD
+2 #3 viewfim 11.04.2011 03:33
Большое спасибо !!! Очень хочу что бы вы продолжали так плодотворно писать :)
+2 #2 Александр 08.04.2011 10:35
Прикольно!
А Вы не думали об изготовлении платы с более "крутым камнем"? Думаю проекты перешли бы на другой уровень. Хотя конечно, сделать что-то работающее на микросхеме с минимумом возможностей тоже интересно (я бы сказал появляется спортивный интерес).
+2 #1 Алексей 05.04.2011 05:53
Отлично! Николай спасибо.

Добавить комментарий


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Проекты Проект Марсоход Видеоигра Питон