#include<LCD5110_Graph.h> //提取液晶屏库函数
#include "stdio.h"
#include "Arduino.h"
extern byte X_MAX = 82; //右边界坐标
extern byte Y_MAX = 46; //左边界坐标
extern unsigned char TinyFont[]; //宏定义超小字体字符格式
extern unsigned char SmallFont[]; //宏定义小字体字符格式
byte DotX, DotY; //蛇身和食物显示单元横纵坐标
byte SnakeBody[100][2] = {{10, 16}, {10, 18}, {10.20}};
byte EatSelf = 0; //判断蛇头是否吃到自身,1:碰到,0:未碰到
byte NewFood = 0; //判断是否需要重新产生食物,1:吃到,0:未吃到
byte SnakeLength = 3; //蛇体长度,初始值为3
long score = 0; //分数(long型变量)
byte FoodX = 30, FoodY = 30; //食物坐标
byte Direct = 2; //按键方向(0上1右2下3左,初始化方向为下)
byte LastDirect = 2; //上次前进方向(蛇头前进方向不能与上次相反)
byte StepTime = 5; //前进一步间隔时间
#define A digitalRead(2) //预定义A键(向上)的判断值,0:按下 1:未按下
#define B digitalRead(3) //预定义B键(向右)的判断值,0:按下 1:未按下
#define C digitalRead(4) //预定义C键(向下)的判断值,0:按下 1:未按下
#define D digitalRead(5) //预定义D键(向左)的判断值,0:按下 1:未按下
#define BL 6 //将背光灯电源接在6号引脚
LCD5110 LCD(9, 10, 11, 13, 12); //添加一个5110液晶屏,命名为LCD
/********************程序初始化********************/
void setup()
{
analogWrite(BL, 0); //设置背光灯引脚,参数为0-1024,对应于关闭背光灯
LCD.InitLCD(); //初始化液晶屏
LCD.setFont(SmallFont); //设置字体格式为小字体字符
DisplayStart(); //显示开始界面
PrintFrame(); //显示边框
randomSeed(analogRead(7)); //设置随机数种子,用于产生食物随机位置
CreatFood(); //产生一个食物
}
/********************主函数********************/
void loop()
{
MoveSnake(); //移动蛇身
JudgeEatSelf(); //判断是否碰到自身或边界
}
/********************显示开始界面********************/
void DisplayStart()
{
LCD.clrScr(); //清屏
LCD.print("Snake", CENTER, 20); //在第二十行居中显示游戏名
LCD.update(); //显示以上图像
delay(2000); //延时两秒
LCD.clrScr(); //清屏
}
/********************显示结束界面********************/
void DisplayGameOver()
{
LCD.clrScr(); //清屏
for (byte y = 48; y > 20; y--) //从下至上移动"Game Over"字符串,到20行停止
{
LCD.print("Game Over", CENTER, y);
LCD.update();
delay(50);
}
delay(5000);
LCD.clrScr();
}
/********************显示边框********************/
void PrintFrame()
{
LCD.clrScr(); //清屏
LCD.drawRect(0, 6, 83, 47); //绘制从(0,6)到(83,47)的方框
LCD.drawRect(1, 7, 82, 46); //绘制从(1,7)到(82,46)的方框
LCD.setFont(TinyFont); //设置字体格式为超小字体
LCD.print("Score:",0, 0); //在(0,0)位置显示“Score”字符串
LCD.printNumI(0, 24, 0, 3, ' ');
LCD.update();
}
/**********产生蛇身或食物单元,为一个2x2方框,即一个稍大的点**********/
void CreatDot(byte x, byte y)
{
LCD.drawRect(x, y,x + 1, y + 1);
LCD.update();
}
/**********清除一个蛇身或食物单元**********/
void ClearDot(byte x, byte y)
{
LCD.clrRect(x, y, x + 1, y + 1);
LCD.update();
}
/******判断产生的食物是否合适(食物坐标不能与蛇身坐标重合)******/
byte IsFoodFit(byte X, byte Y)
{
byte WhetherFit = 1; //食物坐标是否适合判定值
for (int i = 0; i < SnakeLength; i++) //把食物坐标依次与蛇身单元坐标对比
{
if ((X == SnakeBody[i][0] && Y == SnakeBody[i][1]) || (X % 2 != 0) || (Y % 2 != 0))
{
WhetherFit = 0;
break;
}
}
return WhetherFit;
}
/**********产生食物的随机坐标**********/
void RandomFoodPlace()
{
FoodX = random(2, 81); //在(2.81)的范围内随机产生行坐标
FoodY = random(8, 45); //在(8,45)的范围内随机产生行坐标
}
/********************显示食物********************/
void CreatFood()
{
ClearDot(FoodX, FoodY); //清除上一次食物
while (!IsFoodFit(FoodX, FoodY))RandomFoodPlace();
CreatDot(FoodX, FoodY); //显示食物
}
/********************显示蛇身********************/
void DisplaySnake()
{
int i;
for (i = 0; i < SnakeLength; i++)
{
CreatDot(SnakeBody[i][0], SnakeBody[i][1]);
}
}
/********************清除蛇身********************/
void ClearSnake()
{
int i;
for (i = 0; i < SnakeLength; i++)
{
ClearDot(SnakeBody[i][0], SnakeBody[i][1]);
}
}
/********************移动蛇身********************/
void MoveSnake()
{
if (A == 0)Direct = 0; //判断按键按下并给方向参数赋值
else if (B == 0)Direct = 1;
else if (C == 0)Direct = 2;
else if (D == 0)Direct = 3;
if ((LastDirect + Direct) % 2 == 0)Direct = LastDirect;
//判断上一次与下一次方向是否相反,若相反,取上一次的移动方向
LastDirect = Direct; //存储本次方向作为上一次方向
if (SnakeBody[SnakeLength - 1][0] == FoodX && SnakeBody[SnakeLength - 1][1] == FoodY)
//判断蛇头坐标是否与食物坐标相同,如相同表示吃到食物
{
SnakeLength++; //蛇身长度增加一节
score++; //分数增加一分
NewFood = 1; //需要产生新食物
}
if (NewFood == 1)
{
CreatFood(); //先产生一个随机食物
NewFood = 0; //无需产生新食物
switch (Direct) //判断方向值
{
case 0: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0];
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1] - 2;
break;
//如果向上,增加一个蛇头单元,它的列坐标与之前相同,行坐标减2
case 1: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0] + 2;
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1];
break;
//如果向右,增加一个蛇头单元,它的行坐标与之前相同,列坐标加2
case 2: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0];
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1] + 2;
break;
//如果向下,增加一个蛇头单元,它的列坐标与之前相同,行坐标加2
case 3: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0] - 2;
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1];
break;
//如果向左,增加一个蛇头单元,它的行坐标与之前相同,列坐标减2
}
}
else //未吃到食物的坐标运算
{
switch (Direct) //判断方向
{
case 0:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 1][1] - 2;
}
break;
case 1:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 1][0] + 2;
}
break;
case 2:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 1][1] + 2;
}
break;
case 3:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 1][0] - 2;
}
break;
}
}
for (byte i = 0; i < StepTime; i++) //多次显示蛇身,保证蛇身出现时间比消失时间长以减少闪烁
DisplaySnake();
LCD.printNumI(score, 24, 0, 3, ' '); //显示分数
ClearSnake(); //清除蛇身
}
/**********判断蛇头是否碰到自身或边界**********/
void JudgeEatSelf()
{
for (int i; i < SnakeLength - 1; i++)
{
if (SnakeBody[SnakeLength - 1][0] == SnakeBody[i][0] && SnakeBody[SnakeLength - 1][1] == SnakeBody[i][1])
//若蛇头与蛇身某一单元行列坐标相同,则显示游戏结束
{
DisplayGameOver();
break;
}
}
if ((SnakeBody[SnakeLength - 1][0] <= 0 || (SnakeBody[SnakeLength - 1][0] >= X_MAX) || (SnakeBody[SnakeLength - 1][1] <= 6) || ((SnakeBody[SnakeLength - 1][1] >= Y_MAX))))
//若蛇头超出边界坐标,则显示游戏结束
{
DisplayGameOver();
}
}
#include "stdio.h"
#include "Arduino.h"
extern byte X_MAX = 82; //右边界坐标
extern byte Y_MAX = 46; //左边界坐标
extern unsigned char TinyFont[]; //宏定义超小字体字符格式
extern unsigned char SmallFont[]; //宏定义小字体字符格式
byte DotX, DotY; //蛇身和食物显示单元横纵坐标
byte SnakeBody[100][2] = {{10, 16}, {10, 18}, {10.20}};
byte EatSelf = 0; //判断蛇头是否吃到自身,1:碰到,0:未碰到
byte NewFood = 0; //判断是否需要重新产生食物,1:吃到,0:未吃到
byte SnakeLength = 3; //蛇体长度,初始值为3
long score = 0; //分数(long型变量)
byte FoodX = 30, FoodY = 30; //食物坐标
byte Direct = 2; //按键方向(0上1右2下3左,初始化方向为下)
byte LastDirect = 2; //上次前进方向(蛇头前进方向不能与上次相反)
byte StepTime = 5; //前进一步间隔时间
#define A digitalRead(2) //预定义A键(向上)的判断值,0:按下 1:未按下
#define B digitalRead(3) //预定义B键(向右)的判断值,0:按下 1:未按下
#define C digitalRead(4) //预定义C键(向下)的判断值,0:按下 1:未按下
#define D digitalRead(5) //预定义D键(向左)的判断值,0:按下 1:未按下
#define BL 6 //将背光灯电源接在6号引脚
LCD5110 LCD(9, 10, 11, 13, 12); //添加一个5110液晶屏,命名为LCD
/********************程序初始化********************/
void setup()
{
analogWrite(BL, 0); //设置背光灯引脚,参数为0-1024,对应于关闭背光灯
LCD.InitLCD(); //初始化液晶屏
LCD.setFont(SmallFont); //设置字体格式为小字体字符
DisplayStart(); //显示开始界面
PrintFrame(); //显示边框
randomSeed(analogRead(7)); //设置随机数种子,用于产生食物随机位置
CreatFood(); //产生一个食物
}
/********************主函数********************/
void loop()
{
MoveSnake(); //移动蛇身
JudgeEatSelf(); //判断是否碰到自身或边界
}
/********************显示开始界面********************/
void DisplayStart()
{
LCD.clrScr(); //清屏
LCD.print("Snake", CENTER, 20); //在第二十行居中显示游戏名
LCD.update(); //显示以上图像
delay(2000); //延时两秒
LCD.clrScr(); //清屏
}
/********************显示结束界面********************/
void DisplayGameOver()
{
LCD.clrScr(); //清屏
for (byte y = 48; y > 20; y--) //从下至上移动"Game Over"字符串,到20行停止
{
LCD.print("Game Over", CENTER, y);
LCD.update();
delay(50);
}
delay(5000);
LCD.clrScr();
}
/********************显示边框********************/
void PrintFrame()
{
LCD.clrScr(); //清屏
LCD.drawRect(0, 6, 83, 47); //绘制从(0,6)到(83,47)的方框
LCD.drawRect(1, 7, 82, 46); //绘制从(1,7)到(82,46)的方框
LCD.setFont(TinyFont); //设置字体格式为超小字体
LCD.print("Score:",0, 0); //在(0,0)位置显示“Score”字符串
LCD.printNumI(0, 24, 0, 3, ' ');
LCD.update();
}
/**********产生蛇身或食物单元,为一个2x2方框,即一个稍大的点**********/
void CreatDot(byte x, byte y)
{
LCD.drawRect(x, y,x + 1, y + 1);
LCD.update();
}
/**********清除一个蛇身或食物单元**********/
void ClearDot(byte x, byte y)
{
LCD.clrRect(x, y, x + 1, y + 1);
LCD.update();
}
/******判断产生的食物是否合适(食物坐标不能与蛇身坐标重合)******/
byte IsFoodFit(byte X, byte Y)
{
byte WhetherFit = 1; //食物坐标是否适合判定值
for (int i = 0; i < SnakeLength; i++) //把食物坐标依次与蛇身单元坐标对比
{
if ((X == SnakeBody[i][0] && Y == SnakeBody[i][1]) || (X % 2 != 0) || (Y % 2 != 0))
{
WhetherFit = 0;
break;
}
}
return WhetherFit;
}
/**********产生食物的随机坐标**********/
void RandomFoodPlace()
{
FoodX = random(2, 81); //在(2.81)的范围内随机产生行坐标
FoodY = random(8, 45); //在(8,45)的范围内随机产生行坐标
}
/********************显示食物********************/
void CreatFood()
{
ClearDot(FoodX, FoodY); //清除上一次食物
while (!IsFoodFit(FoodX, FoodY))RandomFoodPlace();
CreatDot(FoodX, FoodY); //显示食物
}
/********************显示蛇身********************/
void DisplaySnake()
{
int i;
for (i = 0; i < SnakeLength; i++)
{
CreatDot(SnakeBody[i][0], SnakeBody[i][1]);
}
}
/********************清除蛇身********************/
void ClearSnake()
{
int i;
for (i = 0; i < SnakeLength; i++)
{
ClearDot(SnakeBody[i][0], SnakeBody[i][1]);
}
}
/********************移动蛇身********************/
void MoveSnake()
{
if (A == 0)Direct = 0; //判断按键按下并给方向参数赋值
else if (B == 0)Direct = 1;
else if (C == 0)Direct = 2;
else if (D == 0)Direct = 3;
if ((LastDirect + Direct) % 2 == 0)Direct = LastDirect;
//判断上一次与下一次方向是否相反,若相反,取上一次的移动方向
LastDirect = Direct; //存储本次方向作为上一次方向
if (SnakeBody[SnakeLength - 1][0] == FoodX && SnakeBody[SnakeLength - 1][1] == FoodY)
//判断蛇头坐标是否与食物坐标相同,如相同表示吃到食物
{
SnakeLength++; //蛇身长度增加一节
score++; //分数增加一分
NewFood = 1; //需要产生新食物
}
if (NewFood == 1)
{
CreatFood(); //先产生一个随机食物
NewFood = 0; //无需产生新食物
switch (Direct) //判断方向值
{
case 0: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0];
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1] - 2;
break;
//如果向上,增加一个蛇头单元,它的列坐标与之前相同,行坐标减2
case 1: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0] + 2;
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1];
break;
//如果向右,增加一个蛇头单元,它的行坐标与之前相同,列坐标加2
case 2: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0];
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1] + 2;
break;
//如果向下,增加一个蛇头单元,它的列坐标与之前相同,行坐标加2
case 3: SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 2][0] - 2;
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 2][1];
break;
//如果向左,增加一个蛇头单元,它的行坐标与之前相同,列坐标减2
}
}
else //未吃到食物的坐标运算
{
switch (Direct) //判断方向
{
case 0:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 1][1] - 2;
}
break;
case 1:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 1][0] + 2;
}
break;
case 2:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][1] = SnakeBody[SnakeLength - 1][1] + 2;
}
break;
case 3:
{
for (int i = 0; i < SnakeLength - 1; i++)
{
SnakeBody[i][0] = SnakeBody[i + 1][0];
SnakeBody[i][1] = SnakeBody[i + 1][1];
}
SnakeBody[SnakeLength - 1][0] = SnakeBody[SnakeLength - 1][0] - 2;
}
break;
}
}
for (byte i = 0; i < StepTime; i++) //多次显示蛇身,保证蛇身出现时间比消失时间长以减少闪烁
DisplaySnake();
LCD.printNumI(score, 24, 0, 3, ' '); //显示分数
ClearSnake(); //清除蛇身
}
/**********判断蛇头是否碰到自身或边界**********/
void JudgeEatSelf()
{
for (int i; i < SnakeLength - 1; i++)
{
if (SnakeBody[SnakeLength - 1][0] == SnakeBody[i][0] && SnakeBody[SnakeLength - 1][1] == SnakeBody[i][1])
//若蛇头与蛇身某一单元行列坐标相同,则显示游戏结束
{
DisplayGameOver();
break;
}
}
if ((SnakeBody[SnakeLength - 1][0] <= 0 || (SnakeBody[SnakeLength - 1][0] >= X_MAX) || (SnakeBody[SnakeLength - 1][1] <= 6) || ((SnakeBody[SnakeLength - 1][1] >= Y_MAX))))
//若蛇头超出边界坐标,则显示游戏结束
{
DisplayGameOver();
}
}