0.96寸OLED贪吃蛇算法思路分享

您所在的位置:网站首页 c语言贪吃蛇思路 0.96寸OLED贪吃蛇算法思路分享

0.96寸OLED贪吃蛇算法思路分享

2024-07-09 19:45| 来源: 网络整理| 查看: 265

一个星期前,我把单片机带回了寝室,然后。。。就诞生了个小小的贪吃蛇小游戏程序

先提前说好,在这个函数中涉及到几个函数需要替换的清手动替换

OLED_Clear(); //oled清屏函数 OLED_Rect(); //oled画空心矩形函数 OLED_ShowString(); //oled显示字符串函数 OLED_DrawDot(); //oled描点函数 OLED_ShowNum(); //oled显示变量值函数 delay_ms(); //延时函数

那么,让我们先看一下这个.h文件

#ifndef __OLED_SNAKE_H #define __OLED_SNAKE_H #define Key_UP P00 #define Key_DOWN P01 #define Key_Right P02 #define Key_Left P03 #define Key_UP_number 0 #define Key_DOWN_number 1 #define Key_Right_number 2 #define Key_Left_number 3 //按键宏定义 void OLED_snake(); #endif

这个.h文件是把下面的.c文件里用到的按键和一些特殊定义的值给宏定义一下,然后整个直接在main函数里调用OLED_snake();这个函数就行了。

所以.h就完成了一些片面操作,主要还得看.c里面的算法

#include "oled.h" #include "oled_snake.h" #include "stdlib.h" //用随机数函数=rand(); char snake_x[100]; char snake_y[100]; char snake_food[2]; char fraction = 0; //分数 char fraction_temporary = 0; //分数临时值 void Game_Over(); void OLED_snake() { char x = 20,y = 31; //蛇初始位置 char i = 0; //蛇增量 char Key_number = Key_Right_number; //默认向右 char a = 1; OLED_Clear(); //清除上一局的残骸 snake_x[0] = x; snake_y[0] = y; snake_food[0] = 60; snake_food[1] = 31; OLED_Rect(0,0,95,63); //设置贪吃蛇内部范围 OLED_ShowString(96,0,"GET:",0); //显示分数文字 //这个设置大概是,蛇在(1,1)到(94,62)[不包括线]内有效----------------------------------------------- OLED_DrawDot(snake_food[0],snake_food[1],1); //先默认生成出来一个食物 while(1) { OLED_ShowNum(100,1,fraction,3,16); //显示分数文字 //把分数值显示出来 if(Key_number == Key_UP_number | Key_number == Key_DOWN_number) //检测当前的蛇是不是向上或者向下 { if(Key_Right == 1) { Key_number = Key_Right_number; //向右 }else if(Key_Left == 1) { Key_number = Key_Left_number; //向左 } } if(Key_number == Key_Right_number | Key_number == Key_Left_number) //检测当前的蛇是不是向左或者向右 { if(Key_UP == 1) { Key_number = Key_UP_number; }else if(Key_DOWN == 1) { Key_number = Key_DOWN_number; } } //这里是通过判断蛇当前走向进行转向的控制,毕竟没有哪个贪吃蛇是可以回头的吧-------------------------- OLED_DrawDot(snake_x[fraction],snake_y[fraction],0); //把尾巴消除掉 if(Key_UP != 1 | Key_DOWN != 1 | Key_Right != 1 | Key_Left != 1) { switch(Key_number) { case Key_Right_number: x = x + 1; y = y; break; case Key_Left_number: x = x - 1; y = y; break; case Key_UP_number: x = x; y = y + 1; break; case Key_DOWN_number: x = x; y = y - 1; break; } } fraction_temporary = fraction; for(fraction_temporary;fraction_temporary>0;fraction_temporary--) //通过分数决定循环次数 { snake_x[fraction_temporary] = snake_x[fraction_temporary - 1]; snake_y[fraction_temporary] = snake_y[fraction_temporary - 1]; } snake_x[0] = x; snake_y[0] = y; //计算好的坐标存入数组头 fraction_temporary = fraction; for(fraction_temporary;fraction_temporary>=0;fraction_temporary--) // { OLED_DrawDot(snake_x[fraction_temporary],snake_y[fraction_temporary],1); } //把蛇的身体完整的显示出来------------------------------------------------------------- if(snake_x[0] == snake_food[0] & snake_y[0] == snake_food[1]) { fraction++; while(a) { snake_food[0] = rand(); snake_food[1] = rand(); if(snake_food[0]>=92 | snake_food[1]>=61 | snake_food[1]= 50) { }else{ delay_ms(50 - fraction);//算是速度吧,分越高速度越快 } if(x>=94|x=62|y=0;fraction_temporary--) // { OLED_DrawDot(snake_x[fraction_temporary],snake_y[fraction_temporary],0); } OLED_DrawDot(snake_food[0],snake_food[1],0); fraction = 0; OLED_Clear(); OLED_snake(); } } }

嚯嚯,看起来可真长,实则不然,都是一个一个区域的功能叠加出来的功能罢了;看到昂,这个.c里一共有两个函数名,分别是Game_Over();OLED_Snake();,不用看,前面的gameover是给后面OledSnake服务的,也就是显示"GameOver"的功能,

那,让我们把OLED_Snake();这个大函数拆开来看看吧

一、基础参数定义部分

char snake_x[100]; char snake_y[100]; char snake_food[2]; char fraction = 0; //分数 char fraction_temporary = 0; //分数临时值

在整个大函数开始前,我是定义了三个数组和两个cahr变量,三个数组的前两个snake_x[100];snake_y[100];是保存整个蛇的身体的数组,这个数组内部的赋值是根据下面的fraction分数值来决定的,就好比分数是0,那么蛇的身体只有1,那么保存蛇身体位置坐标信息只保存一次即可;在如果我分数是10,那么蛇的身体就是11,那这个数组就会保存11组蛇的身体的坐标。fraction_temporary也是储存了分数值,只是后面经常要对fraction分数值进行操作,所以就衍生出来一个"分身"来代替原来的分数进行操作。

讲解完定义部分之后,我们就进入void OLED_snake()内部看看(其实吧整个函数看一遍之后会发现,我已经大概写好了一部分到才一部分的范围都是从哪儿到哪儿了)

二、对蛇以及食物各方面的初始化

char x = 20,y = 31; //蛇初始位置 char i = 0; //蛇增量 char Key_number = Key_Right_number; //默认向右 char a = 1; OLED_Clear(); //清除上一局的残骸 snake_x[0] = x; snake_y[0] = y; snake_food[0] = 60; snake_food[1] = 31; OLED_Rect(0,0,95,63); //设置贪吃蛇内部范围 OLED_ShowString(96,0,"GET:",0); //显示分数文字 //这个设置大概是,蛇在(1,1)到(94,62)[不包括线]内有效----------------------------------------------- OLED_DrawDot(snake_food[0],snake_food[1],1); //先默认生成出来一个食物

这一段开头先是定义了蛇头的坐标x,y,并赋值初始值是在(20,31)的位置;

紧接着定义了一个键值Key_number来保存现在蛇应该往哪个方向去,还记得之前.h文件里那一堆#define宏定义吗,在这里就派上用场了,把数字宏定义成文字,是大大增加了程序的可读性;

至于这个a,是后面生成蛇食物的时候用到的一个参数,到那个地方的时候在说;

在之后就把刚才初始化的蛇头位置赋值给了蛇身体数组的第"0"个位置里;

之后就把蛇的食物的初始位置(60,31)也赋值给了食物数组里,因为食物就一个,一个也就一个坐标也就是两个数固定了,所以用1个数组就够了;

OLED_Rect(0,0,95,63);这个函数就是画了个矩形,让玩家能看到边界;

OLED_ShowString(96,0,"GET:",0); 在矩形范围的右边显示分数信息;

OLED_DrawDot(snake_food[0],snake_food[1],1);把刚刚初始的食物显示出来

至此,初始化的内容就完成了,接下来就是进入整个函数的核心while循环里了,整个程序运行也就是在这个while循环里实现的

三、按键检测控制蛇头方向

if(Key_number == Key_UP_number | Key_number == Key_DOWN_number) //检测当前的蛇是不是向上或者向下 { if(Key_Right == 1) { Key_number = Key_Right_number; //向右 }else if(Key_Left == 1) { Key_number = Key_Left_number; //向左 } } if(Key_number == Key_Right_number | Key_number == Key_Left_number) //检测当前的蛇是不是向左或者向右 { if(Key_UP == 1) { Key_number = Key_UP_number; }else if(Key_DOWN == 1) { Key_number = Key_DOWN_number; } } //这里是通过判断蛇当前走向进行转向的控制,毕竟没有哪个贪吃蛇是可以回头的吧--------------------------

这里通过两个if来控制蛇头变化的方向,我们常见的蛇的转向就是上转右,右转下之类的,不管你们玩没玩过,反正我是没玩过直接掉头的贪吃蛇;这个区域就是通过判断蛇头方向来控制检测的按键:假如蛇头现在方向向上,那么只有向左向右的按键可以使用。

下面就是蛇移动和显示的关键部分之一了!

四、蛇头,蛇身体坐标位置计算和储存

OLED_DrawDot(snake_x[fraction],snake_y[fraction],0); //把尾巴消除掉 if(Key_UP != 1 | Key_DOWN != 1 | Key_Right != 1 | Key_Left != 1) { switch(Key_number) { case Key_Right_number: x = x + 1; y = y; break; case Key_Left_number: x = x - 1; y = y; break; case Key_UP_number: x = x; y = y + 1; break; case Key_DOWN_number: x = x; y = y - 1; break; } } fraction_temporary = fraction; for(fraction_temporary;fraction_temporary>0;fraction_temporary--) //通过分数决定循环次数 { snake_x[fraction_temporary] = snake_x[fraction_temporary - 1]; snake_y[fraction_temporary] = snake_y[fraction_temporary - 1]; } snake_x[0] = x; snake_y[0] = y; //计算好的坐标存入数组头 fraction_temporary = fraction; for(fraction_temporary;fraction_temporary>=0;fraction_temporary--) // { OLED_DrawDot(snake_x[fraction_temporary],snake_y[fraction_temporary],1); } //把蛇的身体完整的显示出来-------------------------------------------------------------

    从开头的一个擦除就能看出来,整个蛇的身体应该是先擦在写的显示方法

那么开头为什么说是先擦除蛇尾呢?OLED_DrawDot(snake_x[fraction],snake_y[fraction],0);这个函数可以看到,它是读取蛇身体坐标数组里第"fraction"个数来进行擦除行为,这时候进行模拟一下,此时分数是0,那么之前固定写进第0位的数在这里会因为分数"fraction"为0而擦除,当然,如果分数为1,那擦的就是第1个数。

    这个地方解释起来很迷,是因为这个地方和下面的地方连接的太紧密了,容我接着解释

紧跟着尾巴清除的就是一个通过识别Key_number的值得一个switch来控制蛇头坐标的变化的地方,四个方向对应着四种坐标变化方式,在下次有效按键按下之前,坐标都会以固定方法变化。

    Ok到目前为止还能解释的清楚,下面的两个for说实话我认为还挺复杂的,我们在拆开来说

接着,fraction_temporary = fraction;分数的分身继承了分数的值,来进行下面for的操作

for(fraction_temporary;fraction_temporary>0;fraction_temporary--) //通过分数决定循环次数 { snake_x[fraction_temporary] = snake_x[fraction_temporary - 1]; snake_y[fraction_temporary] = snake_y[fraction_temporary - 1]; }

这个for,把坐标数组里的值整体向后移动了一位,这样就能把之前"蛇头"的坐标转变成"蛇身";加入分数为0,那么这个for就不会运行。如果分数为10,那么这个for就会运行10次,数组里面的数也会向后移动10位,加上蛇头,一共就是11个数。

在这个for下面snake_x[0] = x; snake_y[0] = y;是把刚刚蛇头计算好的坐标重新赋值给数组0位里,成为了新的"蛇头"

    哎嘿,光把这些坐标信息保存起来没用啊,得让它显示出来啊,于是就有了下面一个for

同样的,fraction_temporary = fraction;分数的分身继承了分数的值,来进行下面for的操作

for(fraction_temporary;fraction_temporary>=0;fraction_temporary--) // { OLED_DrawDot(snake_x[fraction_temporary],snake_y[fraction_temporary],1); }

这个for和上面的for有一个不同就是,这个for的判断变成了>=,这样就算分数值是0也会运行一次用来显示蛇。

至此,整条蛇的显示是可以根据分数来显示了,单贪吃蛇贪吃蛇,不吃怎么行呢

五、蛇吃食物的判定和新食物的生成

if(snake_x[0] == snake_food[0] & snake_y[0] == snake_food[1]) { fraction++; while(a) { snake_food[0] = rand(); snake_food[1] = rand(); if(snake_food[0]>=92 | snake_food[1]>=61 | snake_food[1]是为了防止出现蛇头坐标和蛇头坐标对比的情况,那这样游戏还没开始就会结束了。

七、最后的收尾(刷新速度和出界判定)

i++; if(fraction >= 50) { }else{ delay_ms(50 - fraction);//算是速度吧,分越高速度越快 } if(x>=94|x=62|y=0;fraction_temporary--) // { OLED_DrawDot(snake_x[fraction_temporary],snake_y[fraction_temporary],0); } OLED_DrawDot(snake_food[0],snake_food[1],0); fraction = 0; OLED_Clear(); OLED_snake(); } } }

只要一调用这个函数,就会在矩形中间显示"GAME OVER"的字样,此时重制所有的值并检测按键,通过调用核心函数OLED_snake();来开始下一局游戏。

我在上周六的时候写完了这个小游戏,从想法诞生到完成实践也就用了将近三个小时的时间,还是挺简单的

最后经典运行一下看看结果,因为没法发视频,就发个截图算了

我定义蛇身体数组的时候只定义100,所以理论蛇的身体最大也就100长度,而char变量最大可以到255,嘛,反正我最大就这样了。

这个算法好处就是,只要对应屏幕有描点,画矩形函数,这套就能移植到任何屏幕上去(没试过android),算法并没有进行优化,有什么不好的地方还请指出

源码公开,仅供学习



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3