AdaZhao 发表于 2016-1-4 11:53:51

捣蛋贪吃蛇

【背景】新拿到一堆各式各样的灯带,测试了很多效果,结果就是亮瞎眼…面对一堆的像素块就想到了贪吃蛇,又觉得太一般,于是加入了player2,负责捣蛋。利用Arduino, Joy Stick以及procesing/蓝牙做成了这个双人捣蛋贪吃蛇游戏。
【视频】https://v.qq.com/x/page/v0179rh9tkj.html
【介绍】蓝色代表蛇,绿色代表食物玩家1: 按下按钮游戏开始上下左右四个方向操控蛇的方向(每次转向之后要将摇杆位置归0以便下次检测)如果没能在5秒内吃到食物 食物位置将随机改变出界或碰到自己的尾巴游戏结束 红灯亮起再次按下按钮重置游戏再按下按钮新一轮游戏开始玩家2:每隔1秒有一次移动食物一格的机会 负责捣蛋!我尝试了两种玩家2的操控方法,一种是利用BLE link蓝牙模块,用现有的走你!(GoBLE)APP来控制食物的移动,但是每次移动时灯阵里的(0,0)号灯一直会变成蓝色灯,值为1,我会在下文附上所有代码,个人认为Arduino代码并没有问题,怀疑是GoBLE的库有问题,还希望大家能一起讨论一下。另一种方法是用键盘,借助processing控制食物。


【硬件清单】必要硬件:1 x JoyStick摇杆(SKU:DFR0061)1 x NeoPixel (未上架)1 x IO传感器扩展板 V7.1 (SKU: DFR0265) 做完这个项目深深的爱上了这个扩展版可选硬件:1 x BLE-LINK 蓝牙4.0通讯模块 (SKU: TEL0073)
【接线图】键盘控制版本

蓝牙控制版本

JoyStick-X PinA0JoyStick-Y PinA1
JoyStick-Z Pin13NeoPixels Shield Pin6
【代码】键盘控制版本Arduino/*
*This code is loosely base off the project found here https://www.kosbie.net/cmu/fall-10/15-110/handouts/snake/snake.html
*Created by Ada Zhao <adazhao1211@gmail.com>
*12/23/2015
*/

#include <Adafruit_NeoPixel.h>//the library can be found at https://github.com/adafruit/Adafruit_NeoPixel
#include <Metro.h>

#define PIN 6//data pin for NeoPixel

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB   Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB   Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(40, PIN, NEO_GRB + NEO_KHZ800);

#define X 5//this is the depth of the field
#define Y 8//this is the length of the field

//global vars
int hrow=0,hcol=0;//sets the row and col of the snake head
bool game = true;//game is good
bool start = false;//start the game with true
bool ignoreNextTimer=false;//tells the game timer wether or not it should run
//When true the game timer will not update due to the update by a keypress
int sx=4,sy=3;//set the initial snake location
long previousMillis = 0;//used for the game timer
long interval = 350; //how long between each update of the game
unsigned long currentMillis = 0;//used for the game timer

//used for update food location every five seconds
unsigned long currentfoodtime=0;
long prevfoodtime=0;
long foodinterval=10000;
int rx,ry;//food location

int sDr=-1,sDc=0;//used to keep last direction, start off going up

int gameBoard = //game field, 0 is empty, -1 is food, >0 is snake
{
      {0,0,0,0,0,0,0,0},
      {0,0,0,0,0,0,0,0},
      {0,0,0,0,0,0,0,0},
      {0,0,0,0,0,0,0,0},
      {0,0,0,0,0,0,0,0}      
};

//joystick
int pinX = A0;
int pinY = A1;
int pinZ = 13;
bool doMove = true;
bool startGame = true;
bool detect = true;

//move food
int buttonState;
long prevTime = 0;
long foodTime = 1000;

void setup(){
pinMode(pinZ, INPUT);
      Serial.begin(9600);
//snake head
      hrow=sx;
      hcol=sy;
      strip.begin();//start the NeoPixel Sheild
      strip.show(); // Initialize all pixels to 'off'
      resetGame();//clear and set the game
}

void loop(){
      currentMillis = millis();//get the current time
      //game clock
      if(currentMillis - previousMillis >= interval) {
                previousMillis = currentMillis;
                if (game&&start&&ignoreNextTimer==false){                        
                        drawBoard();
                        updateGame();                        
                }
                ignoreNextTimer=false;//resets the ignore bool
      }
//check joystick
      checkJoyStick();
//check keyboard
if (Serial.available()){
    int val = Serial.read();
    changeFood(val);
}

}


void checkJoyStick(){
//check joyStick
float valX = analogRead(pinX);
float valY = analogRead(pinY);
int valZ = digitalRead(pinZ);
   
    //four directions
                if (valX <= 100 && valY <= 616 && valY >= 416 && doMove){//move left
                        if (game&&start){
                              moveSnake(0,-1);
                              ignoreNextTimer=true;
                        }
      doMove = false;               
                }
                else if (valX >= 923 && valY >= 416 && valY <= 616 && doMove){//move right
                        if (game&&start){
                              moveSnake(0,1);
                              ignoreNextTimer=true;
                        }
      doMove = false;

                }
                else if (valY >= 923 && valX >= 411 && valX <= 611 && doMove){//move up
                        if (game&&start){
                              moveSnake(-1,0);
                              ignoreNextTimer=true;
                        }
      doMove = false;

                }
                else if (valY <= 100 && valX >= 411 && valX <= 611 && doMove){//move down
                        if (game&&start){
                              moveSnake(1,0);
                              ignoreNextTimer=true;
                        }
      doMove = false;

                }
                else if (valX <= 530 && valX >= 500 && valY <= 540 && valY >= 500 && doMove == false){
    //reset joyStick
                  doMove = true;
                }

    //start or reset game by pressing the Z button
    if (startGame && valZ == 0 && detect){
      start = true;
      drawBoard();
      detect = false;
      startGame = false;
    }
    if (valZ == 1 && detect == false){
      detect = true;
    }
    if (valZ == 0 && startGame == false && detect){
      resetGame();
      detect = false;
      startGame = true;
    }
   
}


void changeFood(int foodDir){
      if(currentMillis-prevTime>=foodTime){
      //player 2 can move the food every three seconds
      gameBoard = 0;
      switch (foodDir){
          case 1:
            rx --;
          break;
          case 4:
            ry ++;         
          break;
          case 2:
            rx ++;
          break;
          case 3:
            ry --;
          break;
          }
          if (gameBoard != 0 || rx < 0 || rx > X-1 || ry < 0 || ry > Y-1){
            switch (foodDir){
            case 1:
                rx ++;
            break;
            case 4:
                ry --;         
            break;
            case 2:
                rx --;
            break;
            case 3:
                ry ++;
            break;
            }
          }else{
         Serial.println("reset");
         gameBoard = -1;
         val = 0;
         drawBoard();
         prevTime = millis();      
          }
      }
}

void updateGame(){
if (game && start){
      moveSnake(sDr,sDc);
      //If the snake hasn't get the food within an interval, then replace the food to another place.
      currentfoodtime=millis();
      if(currentfoodtime-prevfoodtime>=foodinterval){
      if(gameBoard==-1){
          gameBoard=0;
      }
      placeFood();
      drawBoard();
      }
}
if (game && start){
    drawBoard();//update the screen
}
}

void resetGame(){
      resetBoard();
      sDr=-1;
      sDc=0;
      loadSnake();
      placeFood();
      findSnakeHead();//find where the snake is starting from
      game=true;
      start=false;
      ignoreNextTimer=false;
      drawBoard();
}


void placeFood(){

rx = random(0,X-1);
ry = random(0,Y-1);

    while(gameBoard>0){
      rx = random(0,X-1);
      ry = random(0,Y-1);
    }
    gameBoard=-1;
    prevfoodtime=millis();
}

void loadSnake(){
      gameBoard=1;
}

void resetBoard(){
      for(int x=0;x<X;x++){
                for(int y =0;y< Y;y++){
                        gameBoard=0;
                }
               
      }
      loadSnake();
}

void gameOver(){
      game = false;
      start = false;

      for(int light=0;light<40;light += 4){
                for(int i =0;i< strip.numPixels();i++){
                        strip.setPixelColor(i,strip.Color(light,0,0));
                }
                strip.show();
                delay(25);
      }
for(int light=39;light >= 0;light --){
    for(int i =0;i< strip.numPixels();i++){
      strip.setPixelColor(i,strip.Color(light,0,0));
    }
    strip.show();
    delay(25);
}
}

void moveSnake(int row, int col){//row and col
      
      sDr = row;
      sDc = col;

      int new_r=0,new_c=0;
      new_r=hrow+row;
      new_c=hcol+col;
      if (new_r>=X||new_r<0||new_c>=Y||new_c<0){
                gameOver();
      }
      else if(gameBoard>0){
                gameOver();
      }
      else if (gameBoard==-1){
                gameBoard = 1+gameBoard;
                hrow=new_r;
                hcol=new_c;
                placeFood();
                drawBoard();
      }
      else{
                gameBoard = 1+gameBoard;
                hrow=new_r;
                hcol=new_c;
                removeTail();
                drawBoard();
      }
      
}

void removeTail(){
      for (int x=0;x<X;x++){
                for (int y=0;y<Y;y++){
                        if(gameBoard>0){
                              gameBoard--;
                        }
                }
      }
}

void drawBoard(){
      clear_dsp();

      for (int x=0;x<X;x++){
                for (int y=0;y<Y;y++){
                        if(gameBoard==-1){ //food
                              strip.setPixelColor(SetElement(x,y),strip.Color(0,20,0));
                        }
                        
                        else if(gameBoard==0){
                              strip.setPixelColor(SetElement(x,y),strip.Color(0,0,0));
                        }
                        else{
                              strip.setPixelColor(SetElement(x,y),strip.Color(0,0,10));
                        }
                        
                }
      }
      strip.show();
}

void findSnakeHead(){
      hrow=0;//clearing out old location
      hcol=0;//clearing out old location
      
      for (int x=0;x<X;x++){
                for (int y=0;y<Y;y++){
                        if (gameBoard>gameBoard){
                              hrow=x;
                              hcol=y;
                        }
                }
      }
}

void clear_dsp(){
      for(int i =0;i< strip.numPixels();i++){
                strip.setPixelColor(i,strip.Color(0,0,0));
      }
      strip.show();
}

uint16_t SetElement(uint16_t row, uint16_t col){
      //array = value;
      return Y * row+col;
}
Processingimport processing.serial.*;

Serial myPort;// Create object from Serial class

void setup()
{
size(200,200); //make our canvas 200 x 200 pixels big
String portName = Serial.list(); //change the 0 to a 1 or 2 etc. to match your port
print(portName);
myPort = new Serial(this, portName, 9600);
}

void draw() {
}

void keyPressed() {
if (key == CODED) {
    if (keyCode == UP) {
      myPort.write(1);
    } else if (keyCode == DOWN) {
      myPort.write(2);
    } else if (keyCode == LEFT) {
      myPort.write(3);
    } else {
      myPort.write(4);
    }
}
}
蓝牙控制版本在源代码基础上添加/修改以下部分#include <Adafruit_NeoPixel.h>//the library can be found at https://github.com/adafruit/Adafruit_NeoPixel
#include <Metro.h>
#include "GoBLE.h"

//ble
int buttonState;
long prevTime = 0;
long foodTime = 3000;

void setup()
{
Goble.begin();
pinMode(pinZ, INPUT);
Serial.begin(115200);
}

void loop()
{
//check GoBle
checkGoBle();
}

void checkGoBle ()
{
    if(Goble.available()){
    buttonState   = Goble.readSwitchUp();
    buttonState   = Goble.readSwitchDown();
    buttonState   = Goble.readSwitchLeft();
    buttonState= Goble.readSwitchRight();
   
    for (int i = 1; i < 5; i++) {
      if (buttonState == PRESSED)   
      {
      int foodDir = i;
      Serial.println(foodDir);
      changeFood(foodDir);
      }
    }
}
}

void changeFood(int foodDir){
      if(currentMillis-prevTime>=foodTime)
      {
      Serial.println("moving food!!!");
      gameBoard = 0;
      switch (foodDir){
          case 1:
            rx --;
          break;
          case 2:
            ry ++;         
          break;
          case 3:
            rx ++;
          break;
          case 4:
            ry --;
          break;
          }
          if (gameBoard != 0){
            switch (foodDir){
            case 1:
                rx ++;
            break;
            case 2:
                ry --;         
            break;
            case 3:
                rx --;
            break;
            case 4:
                ry ++;
            break;
            }
          }else{
         Serial.println("reset");
         gameBoard = -1;
         drawBoard();
         prevTime = millis();      
          }
      }
}


【语句分析】总括:Adafruit_NeoPixel strip = Adafruit_NeoPixel(40, PIN, NEO_GRB +NEO_KHZ800);初始化led阵,建立一个类型为Ada_NeoPixel,名为strip(可更改)的对象,三个参数分别为LED的总数,与Arduino连接的pin以及像素类型。这种led可以通过一个数字控制脚控制每一个单独的led,非常方便。int gameBoard建立一个与灯阵一致的矩阵,不同数值代表不同的物体,0为背景,-1为食物,大0的值为蛇currentMillis = millis();读取当前时间,与上次更新游戏的时间做减法,决定蛇是静止还是向前移动
贪吃蛇部分:基本逻辑是将贪食蛇看做一个数列,每一节有一个值,尾巴尖为1,依次递增,蛇头的值最大。每次蛇移动时执行moveSnake(),检测移动后蛇头的位置——食物/背景/蛇身/出界,后两种情况执行gameover(),如果是食物,给检测的位置附上蛇头+1的值,形成新的数列;如果是背景,执行removetTail(),在现有的基础上让数列里的每个数都-1,蛇的长度保持不变。如果吃到食物或距离上次进食超过5秒,执行placeFood(),随机放置食物。
玩家1部分:checkJoySitck()实时检测摇杆位置,当摇杆处于中间位置时重置摇杆,开始下一次检测。
玩家2部分:键盘版本需要打开并运行processing文件。通过Serial,processing可以将读到的键盘输入的信息实时传送给Arduino。蓝牙版本需要包含GoBLE的库,实时检测用户是否在通过蓝牙下指令,如果下了指令并且时间间隔多余1秒,执行moveFood(),移动食物

【存在问题】用蓝牙控制时(0,0)位置的灯会亮蓝色,值为1,上下右三个方向都会使(0,0)灯亮起,向左正常工作。



AdaZhao 发表于 2016-1-5 12:27:51

Ash 发表于 2016-1-4 15:44
楼主想看有动态效果哒~

有视频的 我在文中标注一下

AdaZhao 发表于 2016-1-5 12:29:08

苦海 发表于 2016-1-4 23:38
求加一个程序框图

嗯 好的 等我忙完手头的东西

AdaZhao 发表于 2016-1-11 22:27:16

Geemi 发表于 2016-1-11 10:13
好蛇源自贪吃蛇

为什么听起来像个蛇肉馆

dsweiliang 发表于 2016-1-4 13:37:56

牛逼

luna 发表于 2016-1-4 14:10:39

楼主是软件硬件都皆会啊~坐等更多作品

Ash 发表于 2016-1-4 15:44:53

楼主想看有动态效果哒~ {:5_159:}

大连林海 发表于 2016-1-4 15:50:19

我想看更牛掰的效果

丄帝De咗臂 发表于 2016-1-4 19:47:58

40个点有些少啊

hnyzcj 发表于 2016-1-4 20:16:33

楼主在LED矩阵从哪里获得?

hnyzcj 发表于 2016-1-4 20:16:45

又见贪食蛇

苦海 发表于 2016-1-4 23:38:58

求加一个程序框图

qing1987 发表于 2016-1-5 09:46:07

牛哄哄

AdaZhao 发表于 2016-1-5 12:28:23

hnyzcj 发表于 2016-1-4 20:16
楼主在LED矩阵从哪里获得?

某宝 :D

孙毅 发表于 2016-1-5 12:55:11

强悍!!等视频 啊。。。

AdaZhao 发表于 2016-1-5 13:12:15

孙毅 发表于 2016-1-5 12:55
强悍!!等视频 啊。。。

已经加上了

visionsl 发表于 2016-1-5 19:13:00

LZ好厉害

Jason_G 发表于 2016-1-6 00:04:46

赞一个!

free 发表于 2016-1-6 10:05:43

挺好玩的{:5_132:},如{果有个外形就更好了

Geemi 发表于 2016-1-6 16:46:41

这个好玩耶!!!nice!!楼主

小色叮铛 发表于 2016-1-8 21:28:10

不错喔

Geemi 发表于 2016-1-11 10:13:27

好蛇源自贪吃蛇
页: [1] 2
查看完整版本: 捣蛋贪吃蛇