[项目]贪吃蛇

12519浏览
查看: 12519|回复: 12

[项目] 贪吃蛇

[复制链接]
注:以下是学生做的贪吃蛇,所有都是学生自己研究的,什么都不要问我

功能:
菜单里有三个选项,开始游戏,亮度和最高分。
游戏规则不多说,就是需要提醒一下是可以从墙的一边穿到另一边的。
亮度可调0-15,默认是3,重启不会保存。
最高分同样重启不会保存。
材料:
Arduino Romeo,XY摇杆,MAX7219 LED矩阵模块
接线:
MAX7219:
DIN – 6
CS – 5
CLK – 4
XY摇杆:
X – A0
Y – A1
Z – A2
  1. // 矩阵引脚(数字)
  2. #define CLK  4
  3. #define CS   5
  4. #define DIN  6
  5. // XY摇杆引脚(模拟)
  6. #define X    0
  7. #define Y    1
  8. #define SW   2
  9. #define LEFT  0
  10. #define RIGHT 1
  11. #define UP    2
  12. #define DOWN  3
  13. #define BTN   4
  14. #define PRESS   0
  15. #define RELEASE 1
  16. #define MAXLEN 64
  17. #define MAIN    0
  18. #define GAME    1
  19. #define SCORE   2
  20. #define SETTING 3
  21. #define TOP    4
  22. #define START     0
  23. #define INTENSITY 1
  24. #define HIGHSCORE 2
  25. typedef struct{
  26.   byte x, y;
  27. }position;
  28. // (类似)循环队列操作
  29. void in(position p); // 插入新位置
  30. position out(); // 弹出最末尾的位置
  31. position get(); // 获取第一个位置
  32. position get2(); // 获取第二个位置
  33. byte length(); // 获取当前长度
  34. // 数字,字母位图
  35. const short numbers[10][5] = {
  36.   {7, 5, 5, 5, 7},
  37.   {2, 2, 2, 2, 2},
  38.   {7, 4, 7, 1, 7},   
  39.   {7, 4, 7, 4, 7},
  40.   {5, 5, 7, 4, 4},
  41.   {7, 1, 7, 4, 7},
  42.   {7, 1, 7, 5, 7},
  43.   {7, 4, 4, 4, 4},
  44.   {7, 5, 7, 5, 7},
  45.   {7, 5, 7, 4, 7}
  46. }, chars[26][8] = {
  47.   {4, 6, 9, 9, 9, 15, 9, 9},
  48.   {4, 7, 9, 9, 7, 9, 9, 7},
  49.   {4, 6, 9, 1, 1, 1, 9, 6},
  50.   {4, 7, 9, 9, 9, 9, 9, 7},
  51.   {4, 15, 1, 1, 7, 1, 1, 15},
  52.   {4, 15, 1, 1, 7, 1, 1, 1},
  53.   {4, 6, 9, 1, 13, 9, 9, 6},
  54.   {4, 9, 9, 9, 15, 9, 9, 9},
  55.   {3, 7, 2, 2, 2, 2, 2, 7},
  56.   {4, 8, 8, 8, 8, 9, 9, 6},
  57.   {4, 9, 9, 5, 3, 5, 9, 9},
  58.   {4, 1, 1, 1, 1, 1, 1, 15},
  59.   {4, 9, 15, 15, 9, 9, 9, 9},
  60.   {4, 9, 11, 11, 13, 13, 9, 9},
  61.   {4, 6, 9, 9, 9, 9, 9, 6},
  62.   {4, 7, 9, 9, 7, 1, 1, 1},
  63.   {4, 6, 9, 9, 9, 11, 13, 14},
  64.   {4, 7, 9, 9, 7, 3, 5, 9},
  65.   {4, 6, 9, 1, 6, 8, 9, 6},
  66.   {5, 31, 4, 4, 4, 4, 4, 4},
  67.   {4, 9, 9, 9, 9, 9, 9, 6},
  68.   {4, 9, 9, 9, 9, 6, 6, 6},
  69.   {4, 9, 9, 9, 9, 15, 15, 9},
  70.   {4, 9, 9, 6, 6, 6, 9, 9},
  71.   {5, 17, 17, 10, 4, 4, 4, 4},
  72.   {4, 15, 8, 4, 6, 2, 1, 15}
  73. };
  74. byte disp[8][8], tail, head, direction, buttons = 0, changing, timing[3], rsw = 0, flowleng, part = MAIN, status = START, intensity, highscore = 0;
  75. short valx, valy, flowx;
  76. unsigned long delays[3], times[3], rswt;
  77. position snake[MAXLEN], food;
  78. char* flows;
  79. void in(position p){
  80.   head = (head + 1) % MAXLEN;
  81.   snake[head] = p;
  82. }
  83. position out(){
  84.   position rtn = snake[tail];
  85.   tail = (tail + 1) % MAXLEN;
  86.   return rtn;
  87. }
  88. position get(){
  89.   return snake[head];
  90. }
  91. position get2(){
  92.   if (head) return snake[head - 1];
  93.   return snake[MAXLEN];
  94. }
  95. byte length(){
  96.   if (head >= tail) return head - tail + 1;
  97.   return MAXLEN + head - tail + 1;
  98. }
  99. // 输出数字
  100. void printNum(byte num, short x, short y){
  101.   for(int i = 0; i < 5; i++){
  102.     for(int j = 0; j < 3; j++){
  103.       if (numbers[num][i] & (1 << j)) disp[i + y][j + x] = !0;
  104.       else disp[i + y][j + x] = 0;
  105.     }
  106.   }
  107. }
  108. // 获取字母对应序号
  109. byte getIndex(char c){
  110.   if ((c >= 'a') && (c <= 'z')) return c - 97; // 'a' = 97
  111.   if ((c >= 'A') && (c <= 'Z')) return c - 65; // 'A' = 65
  112.   if (c >= 26) return -1;
  113.   return c;
  114. }
  115. // 输出字母
  116. byte printChar(char c, short x, short y){
  117.   if ((c >= '0') && (c <= '9')){
  118.     printNum(c - 48, x, y); // '0' = 48
  119.     return 3;
  120.   }
  121.   c = getIndex(c);
  122.   if (c == -1) return 0;
  123.   for(int i = 0; i < 7; i++){
  124.     if (i + y > 7) break;
  125.     if (i + y < 0) continue;
  126.     for(int j = 0; j < chars[c][0]; j++){
  127.       if (j + x > 7) break;
  128.       if (j + x < 0) continue;
  129.       if (chars[c][i + 1] & (1 << j)) disp[i + y][j + x] = !0;
  130.       else disp[i + y][j + x] = 0;
  131.     }
  132.   }
  133.   return chars[c][0];
  134. }
  135. // 打印字符串
  136. void printString(char* s, short x, short y){
  137.   for(int i = 0; i < strlen(s); i++){
  138.     x += printChar(s[i], x, y + ((s[i] >= '0') && (s[i] <= '9'))) + 1;
  139.     if (x > 7) return;
  140.   }
  141. }
  142. // 流动显示字符串
  143. void flowString(char* s){
  144.   flowleng = strlen(s);
  145.   for(int i = 0; i < strlen(s); i++){
  146.     char c = getIndex(s[i]);
  147.     if (c != -1) flowleng += chars[c][0];
  148.   }
  149.   flowx = 0;
  150.   flows = s;
  151.   timerContinue(2);
  152. }
  153. // 停止显示流动字符串
  154. void stopFlowing(){
  155.   timerPause(2);
  156. }
  157. // 生成食物
  158. void newFood(){
  159.   byte num = random(64 - length()); // 能生成的位置只有 64 - length() 个
  160.   if ((food.x != get().x) || (food.y != get().y)){ // 初始化时可能排除上一次食物的位置
  161.     disp[food.y][food.x] = 0;
  162.     update(food.y);
  163.   }
  164.   for(int i = 0; i < 8; i++)
  165.     for(int j = 0; j < 8; j++)
  166.       if (!disp[j][i]){ // 身体不能生成食物
  167.         if (num) num--;
  168.         else{
  169.           food.x = i;
  170.           food.y = j;
  171.           disp[food.y][food.x] = 1;
  172.           update(food.y);
  173.           return;
  174.         }
  175.       }
  176. }
  177. // 向矩阵模块输出一个字节的数据
  178. void writeByte(byte data){
  179.   for(int i = 0; i < 8; i++){
  180.     digitalWrite(DIN, (data << i) & 0x80);
  181.     digitalWrite(CLK, LOW);
  182.     digitalWrite(CLK, HIGH);
  183.   }
  184. }
  185. // 修改矩阵模块指定寄存器的值
  186. void writeReg(byte addr, byte data){
  187.   digitalWrite(CS, LOW);
  188.   writeByte(addr);
  189.   writeByte(data);
  190.   digitalWrite(CS, HIGH);
  191. }
  192. // 刷新指定行
  193. void update(byte row){
  194.   byte t = 0;
  195.   for(int i = 0; i < 8; i++) if (disp[row][i]) t |= 128 >> i;
  196.   writeReg(row + 1, t);
  197. }
  198. // 刷新全屏幕
  199. void update(){
  200.   for(int i = 0; i < 8; i++){
  201.     update(i);
  202.   }
  203. }
  204. // 清屏
  205. void clrscr(){
  206.   for(int i = 0; i < 8; i++) for(int j = 0; j < 8; j++) disp[i][j] = 0;
  207. }
  208. // 重绘
  209. void repaint(){
  210.   clrscr();
  211.   for(int i = tail; i != head; i = (i + 1) % MAXLEN) disp[snake[i].x][snake[i].y] = 1;
  212. }
  213. // 初始化地图
  214. void initMap(){
  215.   clrscr();
  216.   for(int i = 0; i < 4; i++){
  217.     disp[3][i + 2] = 1;
  218.     snake[i].x = i + 2;
  219.     snake[i].y = 3;
  220.   }
  221.   tail = 0;
  222.   head = 3;
  223.   direction = 1 << RIGHT;
  224.   newFood();
  225.   update();
  226.   timerContinue(0);
  227.   timerContinue(1);
  228. }
  229. // 获取前进方向
  230. byte getDirection(){
  231.   byte d1 = 255, d2 = 255;
  232.   for(int i = 0; i < 4; i++) if (direction & (1 << i)){
  233.     if (d1 == 255) d1 = i;
  234.     else {
  235.       d2 = i;
  236.       break;
  237.     }
  238.   }
  239.   if (d2 == 255) return d1;
  240.   changing = !changing; // 如果摇杆掰到角落上就两个轮流来(ノへ ̄、)
  241.   if (changing) return d2;
  242.   return d1;
  243. }
  244. // 注册计时器
  245. void timerRegister(byte id, unsigned long time){
  246.   delays[id] = time;
  247.   times[id] = millis();
  248.   timing[id] = 0;
  249. }
  250. // 计时器暂停
  251. void timerPause(byte id){
  252.   timing[id] = 0;
  253. }
  254. // 计时器继续
  255. void timerContinue(byte id){
  256.   times[id] = millis();
  257.   timing[id] = !0;
  258. }
  259. // 返回计时器状态
  260. bool timerTiming(byte id){
  261.   return timing[id];
  262. }
  263. // 计时器事件
  264. void timerAction(byte id){
  265.   if (id == 0){ // 蛇该动了
  266.     byte drtn = getDirection();
  267.     position np = get();
  268.     // 穿墙判定不解释
  269.     if (drtn == LEFT){
  270.       if (np.x) np.x--;
  271.       else np.x = 7;
  272.     } else if (drtn == RIGHT){
  273.       if (np.x == 7) np.x = 0;
  274.       else np.x++;
  275.     } else if (drtn == UP){
  276.       if (np.y) np.y--;
  277.       else np.y = 7;
  278.     } else if (drtn == DOWN){
  279.       if (np.y == 7) np.y = 0;
  280.       else np.y++;
  281.     }
  282.     if ((np.x == food.x) && (np.y == food.y)){ // 吃到东西了
  283.       disp[np.y][np.x] = 1;
  284.       update(np.y);
  285.       in(np);
  286.       newFood();
  287.     } else {
  288.       position last = out();
  289.       disp[last.y][last.x] = 0;
  290.       if (disp[np.y][np.x]){ // 咬到自己了
  291.         byte score = length() - 3;
  292.         timerPause(0);
  293.         timerPause(1);
  294.         part = SCORE;
  295.         if (score > highscore) highscore = score;
  296.         clrscr();
  297.         printNum(score / 10, 0, 1);
  298.         printNum(score % 10, 4, 1);
  299.         update();
  300.       }else{ // 没咬到就挪一下
  301.         in(np);
  302.         disp[np.y][np.x] = !0;
  303.         update(np.y);
  304.         update(last.y);
  305.       }
  306.     }
  307.   } else if (id == 1){ // 食物闪一下
  308.     disp[food.y][food.x] = !disp[food.y][food.x];
  309.     update(food.y);
  310.   } else if (id == 2){ // 字符串挪一下
  311.     clrscr();
  312.     printString(flows, -flowx, 0);
  313.     update();
  314.     flowx++;
  315.     if (flowx == flowleng) flowx = -8;
  316.   }
  317. }
  318. // 按钮按下事件
  319. void buttonPressed(byte id){
  320.   if (part == GAME){
  321.     if (id == BTN){
  322.     } else { // 游戏中变方向,同时还要防止头转180°这种奇葩情况。。
  323.       position s1 = get(), s2 = get2();
  324.       if ((id == LEFT) && ((get().x - get2().x == 1) || (get2().x - get().x == 7))) return;
  325.       if ((id == RIGHT) && ((get2().x - get().x == 1) || (get().x - get2().x == 7))) return;
  326.       if ((id == UP) && ((get().y - get2().y == 1) || (get2().y - get().y == 7))) return;
  327.       if ((id == DOWN) && ((get2().y - get().y == 1) || (get().y - get2().y == 7))) return;
  328.       direction = buttons;
  329.       if (buttons & !(1 << id)) changing = 0;
  330.     }
  331.   }
  332. }
  333. // 按钮释放事件
  334. void buttonReleased(byte id){
  335.   if (part == MAIN){
  336.     if (id == BTN){ // 主菜单选择了什么东西
  337.       stopFlowing();
  338.       if (status == START){
  339.         part = GAME;
  340.         initMap();
  341.       } else if (status == INTENSITY){
  342.         part = SETTING;
  343.         clrscr();
  344.         printNum(intensity / 10, 0, 1);
  345.         printNum(intensity % 10, 4, 1);
  346.         update();
  347.       } else if (status == HIGHSCORE){
  348.         part = TOP;
  349.         clrscr();
  350.         printNum(highscore / 10, 0, 1);
  351.         printNum(highscore % 10, 4, 1);
  352.         update();
  353.       }
  354.     } else if ((id == LEFT) || (id == UP)){ // 换选项,下同  其实这最好用数组,但是只有三个就没写。。
  355.       if (status == START){
  356.         status = HIGHSCORE;
  357.         flowString("highscore");
  358.       } else if (status == INTENSITY){
  359.         status = START;
  360.         flowString("start");
  361.       } else if (status == HIGHSCORE){
  362.         status = INTENSITY;
  363.         flowString("intensity");
  364.       }
  365.     } else {
  366.       if (status == START){
  367.         status = INTENSITY;
  368.         flowString("intensity");
  369.       } else if (status == INTENSITY){
  370.         status = HIGHSCORE;
  371.         flowString("highscore");
  372.       } else if (status == HIGHSCORE){
  373.         status = START;
  374.         flowString("start");
  375.       }
  376.     }
  377.   } else if (part == GAME){
  378.     if (id == BTN){ // 暂停
  379.       if (timerTiming(0)) timerPause(0);
  380.       else timerContinue(0);
  381.     } else {
  382.       if (direction & !(1 << id)) direction &= !(1 << id);
  383.     }
  384.   } else if (part == SCORE){
  385.     if (id == BTN){ // 回主菜单
  386.       part = MAIN;
  387.       flowString("start");
  388.     }
  389.   } else if (part == SETTING){
  390.     if (id == BTN){ // 还是回主菜单
  391.       part = MAIN;
  392.       flowString("intensity");
  393.     } else{ // 改亮度
  394.       if ((id == LEFT) || (id == UP)){
  395.         if (intensity != 0) intensity--;
  396.       } else {
  397.         if (intensity != 15) intensity++;
  398.       }
  399.       writeReg(0xA, intensity);
  400.       printNum(intensity / 10, 0, 1);
  401.       printNum(intensity % 10, 4, 1);
  402.       update();
  403.     }
  404.   } else if (part == TOP){
  405.     if (id == BTN){ // 仍然是回主菜单
  406.       part = MAIN;
  407.       flowString("highscore");
  408.     }
  409.   }
  410. }
  411. void setup() {
  412.   pinMode(CLK, OUTPUT);
  413.   pinMode(CS, OUTPUT);
  414.   pinMode(DIN, OUTPUT);
  415.   intensity = 3;
  416.   writeReg(0x9, 0); // Decode Mode
  417.   writeReg(0xA, intensity); // Intensity
  418.   writeReg(0xB, 7); // Scan Limit
  419.   writeReg(0xC, 1); // Shutdown
  420.   writeReg(0xF, 0); // Display Test
  421.   update();
  422.   timerRegister(0, 1000); // move timer
  423.   timerRegister(1, 200);  // food twinkle
  424.   timerRegister(2, 100);  // flow string
  425.   flowString("start");
  426. }
  427. void loop() {
  428.   // 循环检测计时器
  429.   for(int i = 0; i < 3; i++) if ((timing[i]) && (millis() - times[i] >= delays[i])){
  430.     timerAction(i);
  431.     times[i] += delays[i];
  432.   }
  433.   // 循环检测摇杆
  434.   if (analogRead(X) == 1023){
  435.     if (!(buttons & (1 << RIGHT))){
  436.       buttons |= 1 << RIGHT;
  437.       buttonPressed(RIGHT);
  438.     }
  439.   } else if (analogRead(X) == 0){
  440.     if (!(buttons & (1 << LEFT))){
  441.       buttons |= 1 << LEFT;
  442.       buttonPressed(LEFT);
  443.     }
  444.   } else if (buttons & (1 << RIGHT)){
  445.     if (analogRead(X) < 923){
  446.       buttons &= !(1 << RIGHT);
  447.       buttonReleased(RIGHT);
  448.     }
  449.   } else if (buttons & (1 << LEFT)){
  450.     if (analogRead(X) >= 100){
  451.       buttons &= !(1 << LEFT);
  452.       buttonReleased(LEFT);
  453.     }
  454.   }
  455.   if (analogRead(Y) == 1023){
  456.     if (!(buttons & (1 << UP))){
  457.       buttons |= 1 << UP;
  458.       buttonPressed(UP);
  459.     }
  460.   } else if (analogRead(Y) == 0){
  461.     if (!(buttons & (1 << DOWN))){
  462.       buttons |= 1 << DOWN;
  463.       buttonPressed(DOWN);
  464.     }
  465.   } else if (buttons & (1 << UP)){
  466.     if (analogRead(Y) < 923){
  467.       buttons &= !(1 << UP);
  468.       buttonReleased(UP);
  469.     }
  470.   } else if (buttons & (1 << DOWN)){
  471.     if (analogRead(Y) >= 100){
  472.       buttons &= !(1 << DOWN);
  473.       buttonReleased(DOWN);
  474.     }
  475.   }
  476.   // 循环检测按钮
  477.   if (buttons & (1 << BTN)){
  478.     if (analogRead(SW)){
  479.       if (rsw){
  480.         if (micros() - rswt > 1000){ // 防止按钮按下时出现有时认为没有按下的时候,认为用户连续按下了多次
  481.           buttons &= !(1 << BTN);
  482.           rsw = 0;
  483.           buttonReleased(BTN);
  484.         }
  485.       } else {
  486.         rsw = 1;
  487.         rswt = micros();
  488.       }
  489.     } else rsw = 0;
  490.   } else if (!analogRead(SW)){
  491.     buttons |= 1 << BTN;
  492.     buttonPressed(BTN);
  493.   }
  494. }
复制代码

效果图:
贪吃蛇图1

贪吃蛇图2

大连林海  初级技神

发表于 2016-2-29 20:54:30

超牛逼的 学生
回复

使用道具 举报

dsweiliang  初级技神

发表于 2016-3-1 08:22:26

Romeo BLE多合一控制器好用么,我也想入一块
回复

使用道具 举报

丄帝De咗臂  高级技匠
 楼主|

发表于 2016-3-1 09:07:38

dsweiliang 发表于 2016-3-1 08:22
Romeo BLE多合一控制器好用么,我也想入一块

果断好用
回复

使用道具 举报

源代码  中级技匠

发表于 2016-3-1 10:48:19

你的学生好腻害
回复

使用道具 举报

孙毅  初级技匠

发表于 2016-3-1 16:10:10

张禄老师是大学老师?!
好腻害的学生!
回复

使用道具 举报

丄帝De咗臂  高级技匠
 楼主|

发表于 2016-3-1 17:20:17

孙毅 发表于 2016-3-1 16:10
张禄老师是大学老师?!
好腻害的学生!

高中老师,学生比我厉害
回复

使用道具 举报

iooops  中级技匠 来自手机

发表于 2016-3-1 17:22:58

啊!
回复

使用道具 举报

iooops  中级技匠 来自手机

发表于 2016-3-1 17:23:08

棒!
回复

使用道具 举报

孙毅  初级技匠

发表于 2016-3-1 21:48:51

棒!
回复

使用道具 举报

mickey  NPC

发表于 2016-3-2 17:12:14

用Arduino UNO + 输入扩展板V2.0 + LCD12864 显示屏扩展板做的话,效果会更好。
回复

使用道具 举报

丄帝De咗臂  高级技匠
 楼主|

发表于 2016-3-2 17:13:56

mickey 发表于 2016-3-2 17:12
用Arduino UNO + 输入扩展板V2.0 + LCD12864 显示屏扩展板做的话,效果会更好。

谢谢指导
回复

使用道具 举报

20060606  高级技匠

发表于 2020-8-20 05:15:06

好创意,赞一个
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail