捣蛋贪吃蛇
【背景】新拿到一堆各式各样的灯带,测试了很多效果,结果就是亮瞎眼…面对一堆的像素块就想到了贪吃蛇,又觉得太一般,于是加入了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)灯亮起,向左正常工作。
Ash 发表于 2016-1-4 15:44
楼主想看有动态效果哒~
有视频的 我在文中标注一下 苦海 发表于 2016-1-4 23:38
求加一个程序框图
嗯 好的 等我忙完手头的东西 Geemi 发表于 2016-1-11 10:13
好蛇源自贪吃蛇
为什么听起来像个蛇肉馆 牛逼 楼主是软件硬件都皆会啊~坐等更多作品 楼主想看有动态效果哒~ {:5_159:} 我想看更牛掰的效果 40个点有些少啊 楼主在LED矩阵从哪里获得? 又见贪食蛇 求加一个程序框图 牛哄哄 hnyzcj 发表于 2016-1-4 20:16
楼主在LED矩阵从哪里获得?
某宝 :D 强悍!!等视频 啊。。。 孙毅 发表于 2016-1-5 12:55
强悍!!等视频 啊。。。
已经加上了 LZ好厉害 赞一个! 挺好玩的{:5_132:},如{果有个外形就更好了 这个好玩耶!!!nice!!楼主 不错喔 好蛇源自贪吃蛇
页:
[1]
2