Привет. в прошлый раз мы начали программировать змейку. Мы используем светодиодную матрицу и джойстик для управления. Сегодня допишем код, чтобы у нас получилась настоящая игра змейка.
Содержание
В предыдущей статье мы уже начали писать программу для игры. Посмотрите тот пост, если пропустили или уже забыли. Сегодня мы допишем программу, чтобы получить настоящую игру.
Для того, чтобы выполнить этот проект нам понадобиться
- Ардуино UNO
- Перемычки
- Макетная плата
- Светодиодная матрица
- Резистор 220 Ом
- Джойстик
- Кабель USB
Игра змейка
Мы уже можем управлять змейкой с помощью джойстика и переходить через границы матрицы. Но, чтобы это была настоящая игра, нужно добавить еще несколько моментов.
Во-первых змейка должна состоять из нескольких светодиодов. И ее размер должен увеличиваться, когда змейка ест.
Во-вторых мы должны создать еду, проверять, не съели ли ее. И создавать новую.
И в-третьих, нужно создать условия проигрыша. Иначе какая это будет игра.
Несколько секций змейки
Сложность здесь в том, что при изменении направления движения головы змейки, ее тело должно повторять точный путь. Для этого нам придется записывать координаты каждой секции змейки. И выводить их последовательно при изменении направления движения.
Для этого заведем специальный массив. В нем будут храниться координаты каждой секции змейки. Максимальный размер змеи будет равен размеру матрицы, поэтому сразу зарезервируем 256 элементов. Также создадим переменную для реального размера змейки в текущий момент. И текущую скорость.
int snake[256]; // array of snakes elements
int snakeSize = 2; // real snake size
int snakeSpeed = 500; // snake speed
В начале игры, все элементы змеи, кроме ее реального размера, должны быть пустыми. А несколько элементов должны быть рядом со стартовой позицией. Инициализируем это следующим образом.
for( i=0; i<=255; i++ ){
snake[i] = 0;
}
for( i=0; i<=snakeSize; i++ ){
snake[i] = lastDirection+i;
}
Теперь в функции перемещения змейки мы должны нарисовать все элементы, но убрать лишние при движении вперед. Для этого 0 элементу присвоим новое значение из функции Snakedirection, а последующим элементам присвоим значения предыдущих элементов.
FastLED.clear();
for(i=snakeSize; i>=1; i--){
snake[i] = snake[i-1];
}
snake[0] = snakeDirection;
for( i=0; i<=255; i++ ){
if( snake[i] ){
leds[snake[i]].setRGB(red, green, blue);
}
}
FastLED.show();
Таким образом, в нашей змейке появилось три секции. И при движении они перемещаются по матрице. А при смене направления движения джойстиком, весь маршрут сохраняется и прорисовывается на матрице.
Еда
Теперь создадим что-нибудь съедобное. Чтобы змея могла съесть и вырасти. В переменную f запишем координаты точки с едой. Она будет генерироваться случайно.
f = random(0, 255);
И напишем небольшую функцию. Она будет проверять, съедена ли еда. И создавать новую.
void food( int eaten ){
if( eaten == f ){
snakeSize++;
f = random(0, 255);
red = fred;
green = fgreen;
blue = fblue;
fred = random(0, 255);
fgreen = random(0, 255);
fblue = random(0, 255);
snakeSpeed = snakeSpeed / 1.1;
} else {
leds[f].setRGB(fred, fgreen, fblue);
FastLED.show();
}
}
Если точка съедена, вы увеличим размер змейки, увеличим скорость и создадим новую точку со случайными координатами. Также перекрасим змейку в цвета съеденной ранее еды.
Конец игры
Для определения конца игры напишем еще одну функцию. Она будет восстанавливать размер змейки, скорость, а также перекрашивать ее в красный цвет.
void death(){
snakeSize = 2;
snakeSpeed = 500;
red = 255;
green = 0;
blue = 0;
}
Таким образом мы будем понимать, что старая змейка умерла и нужно все начинать сначала. Конечно, вы можете изменить код и добавить текст или закрасить всю матрицу красным.
Теперь добавим условие, при котором считать игру законченной. Поскольку мы сделали границы матрицы прозрачными. То, запретим змейке пересекаться со своим собственным телом. Для этого в функцию прорисовки текущего шага добавим проверку.
for( i=0; i<=255; i++ ){
if( snake[i] == snakeDirection ){
death();
}
}
Если координаты текущего шага уже есть в составе змейки, вызываем функцию death()
Мы не стали сбрасывать все цифры из массива snake, а значит после смерти несколько элементов останутся лежать на месте. Наезжать на них так же будет нельзя, ведь эти координаты находятся в составе змейки.
Полный текст программы
#include <FastLED.h>
//maatrix settings
#define NUM_LEDS 256
#define DATA_PIN 3
#define BRIGHTNESS 8
//joystick settings
#define pinX A2 // ось X джойстика
#define pinY A1 // ось Y джойстика
#define swPin 2 // кнопка джойстика
int snake[256]; // array of snake elements
int snakeSize = 2; // real snake size
int snakeSpeed = 500;
int row; // row number
int col; // column number
int lastDirection = 135; // start direction
int i, newDirection, OlddX = 1, OlddY, f;
int red, green, blue, fred, fgreen, fblue; //colors
CRGB leds[NUM_LEDS];
void setup() {
red = random(0, 255);
green = random(0, 255);
blue = random(0, 255);
fred = random(127, 255);
fgreen = random(127, 255);
fblue = random(127, 255);
Serial.begin(9600);
pinMode(pinX, INPUT);
pinMode(pinY, INPUT);
pinMode(swPin, INPUT);
digitalWrite(swPin, HIGH);
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
for( i=0; i<=255; i++ ){
snake[i] = 0;
}
for( i=0; i<=snakeSize; i++ ){
snake[i] = lastDirection+i;
}
f = random(0, 255);
FastLED.show();
}
int Snakedirection(int last, int dX, int dY ){
dX = map(dX, 0, 1000, -1, 1);
dY = map(dY, 0, 1000, -1, 1);
if(dX == 0 && dY == 0 && OlddX != dX){
dX = OlddX;
}
if(dY == 0 && dX == 0 && OlddY != dY){
dY = OlddY;
}
int newDirection = last;
if( dX != 0 ){ // moving in X direction
if ( row&1 ){
if( col == 0 && dX == 1){ newDirection = last -15; }
else if( col == 15 && dX == -1){ newDirection = last +15; }
else newDirection = last + dX; // четная
} else {
if( col == 0 && dX == 1){ newDirection = last +15; }
else if( col == 15 && dX == -1 ){ newDirection = last -15; }
else newDirection = last - dX; // не четная
}
}
if( dY < 0){ // moving in Y DOWN direction
if(row == 15 && dY == -1){newDirection = col;}
else if ( row&1 ){
newDirection = last + (col*2)+1; // четная
} else {
newDirection = last + (16-col-1)+(16-col); // не четная
}
}
if( dY > 0){ // moving in Y UP direction
if( row == 0 && dY == 1){ newDirection = 255 - col;}
else if ( row&1 ){
newDirection = last - (last - 16*row) - (16 - col); // четная
} else {
newDirection = last - (col*2)-1; // не четная
}
}
OlddX = dX;
OlddY = dY;
return newDirection;
}
int snakeMove(int snakeDirection){
for( i=0; i<=255; i++ ){
if( snake[i] == snakeDirection ){
death();
}
}
FastLED.clear();
for(i=snakeSize; i>=1; i--){
snake[i] = snake[i-1];
}
snake[0] = snakeDirection;
for( i=0; i<=255; i++ ){
if( snake[i] ){
leds[snake[i]].setRGB(red, green, blue);
}
}
FastLED.show();
row = (int)(snakeDirection/16); // row number
if ( row&1 ){
col = (row+1) * 16 - snakeDirection - 1;
} else {
col = snakeDirection - row * 16;
}
return snakeDirection;
}
void food( int eaten ){
if( eaten == f ){
snakeSize++;
f = random(0, 255);
red = fred;
green = fgreen;
blue = fblue;
fred = random(0, 255);
fgreen = random(0, 255);
fblue = random(0, 255);
snakeSpeed = snakeSpeed / 1.1;
} else {
leds[f].setRGB(fred, fgreen, fblue);
FastLED.show();
}
}
void death(){
snakeSize = 2;
snakeSpeed = 500;
red = 255;
green = 0;
blue = 0;
}
void color(boolean sw){
if(!sw){
red = random(0,255);
green = random(0,255);
blue = random(0,255);
}
}
void loop() {
color( digitalRead(swPin) );
newDirection = Snakedirection(lastDirection, analogRead(pinX), analogRead(pinY));
lastDirection = snakeMove(newDirection);
food(newDirection);
delay(snakeSpeed);
}
Заключение
Мы написали нашу первую игру для Ардуино. Конечно, в коде еще много возможностей для улучшения игры. И у вас в запасе еще 78% памяти Ардуино. Но это уже готовая классическая аркадная игра.
Hi, id just saw your tutorial from Arduino Project Hub…
My request would be to make this Snake Game 2 player or more player as it will be awesome to beat your friends!!
🙂
Hello, friend! Thanks for this comment! I would love to make that project. Unfortunately, don’t have time for that for now. But, i thought about it already. Someday!
owh i found someone else already did the 2 Player… maybe just need to change the code to yours ?
https://github.com/reddtoric/2-player-snake
Of course, there are plenty of that projects. But it is great to make it by yourself, isn’t it? 🙂
well, im not a programmer………. coffee or beer ? 🙂
thanks for reply.. i can buy you some coffee if you do it 🙂
Hey! Thanks! I like coffee, of course. But, if you wanna support my project, you can subscribe to my youtube channel first. It would be super great.
And if you really want to support me, you can become my first patron on patreon! I would be happy.
All links are here — https://arcadepub.ru/%d0%bf%d0%be%d0%b4%d0%b4%d0%b5%d1%80%d0%b6%d0%ba%d0%b0/
Hi, do you know what I would have to do to change this for an 8×8 matrix? I believe this one is with Individually addressable LEDs, so as long as the 8×8 I use is also Individually addressable, I should be able to use this code, right?
Thanks in advance!
Hey! It’s been a while since i wrote it, so i’m not sure. I suppose you would have to change a direction calculation. And LED’s order might differ. But, i hope you will figure it out!
Hi, thanks for answering, and on another note; I don’t really understand this line
int lastDirection = 135; // start direction
Why do you start it at 135? What does that number represent?
I would really appreciate it if you could clear that up.
Thanks in advance.
Hi, thanks for asnwering. I just have one issue with the code that stops me from turning it into 8×8.
I don’t quite get this line:
int lastDirection = 135; // start direction
What does 135 mean here? Why is it 135 and not another number? And would I have to change it if I change the size of the matrix?
Thanks in advance.
Hey, are my comments getting through? It says «Your comment is pending review.» On them, and I’m not sure what that means
Hey! Ted, everything is ok. Just didnt have time for answer. I’m still not sure, what is the answer for you question, i need to build this scheme again and try it, before i’m ready to give a solution. So, stay tuned, it will take a few days.
Oh, sorry! I was just getting worried that my comments weren’t being uploaded :P. Well, make sure to tell me when you’ve finished it!
By the way, I asked this on the post where you first explained how to use the matrix, but I guess I’ll ask it on here too, while I’m at it.
The question is:
How is your matrix wired? As in, do all rows go from left to right? or does it alternate each row?
For example, my Matrix goes from left to right on all rows:
First row, LED 0 to LED 7
Second row, LED 8 to LED 15
Third row, LED 16 to LED 23
And so on…
However, there are some Matrix that go like this:
First row, LED 0 to LED 7
Second row, LED 15 to LED 8
Third row, LED 16 to LED 23
Fourth row, LED 31 to LED 24
And so on …
So, which one is yours?
Thanks in advance.