本篇实现了一个使用c8t6驱动0.96寸OLED的贪吃蛇游戏。也可以使用rct6,就是改改启动文件,芯片类型,宏定义等等。需要完整代码的可以私信我。
我是用的是四线IIC接口的OLED
B5 -> OLED上的SDA
B6 -> OLED上的SCL
B8 -> 左按键
3.3v -> 左按键
B9 -> 右按键
3.3v -> 右按键
B10 -> 下按键
3.3v -> 下按键
B11 -> 上按键
3.3v -> 上按键
B12 -> 结束按键
3.3v -> 结束按键
R -> 复位按键
GUD -> 复位按键
该代码主要实现在指定的x,y上打印一个点,将它存放在自定义的OLED_GRAM中,最终通过OLED_Refresh他们一并刷新到OLED上。
void OLED_DrawPoint(u8 x,u8 y)
{
u8 pos,bx,temp=0;
if(x>127||y>63)return;//超出范围了.
pos=7-y/8;
bx=y%8;
temp=1<<(7-bx);
OLED_GRAM[x][pos]|=temp;
}
void OLED_DrawNoPoint(u8 x,u8 y)
{
u8 pos,bx,temp=0;
if(x>127||y>63)return;//超出范围了.
pos=7-y/8;
bx=y%8;
temp=1<<(7-bx);
OLED_GRAM[x][pos]&=~temp;
}
因为0.96寸OLED屏幕是128*64,将每2*2的四个OLED上的节点看做蛇的一个节点,所以定义地图大小为32*12
#define MAXLENGTH 100 //蛇的最大长度
int map[32][12]={0};//地图大小 x,y(一个坐标为4*4个像素点) 实际按4倍尺寸放大后地图大小为128*48个像素点
int score; //分数
bool eated=false; //蛇吃到食物的标记
u8 KeyValue=0; //获取按键值
struct {
int snake_Grid[MAXLENGTH][2]; //二维数组,行坐标表示蛇节点,列表示当前节点的x,y坐标
int length; //蛇的长度
int direction;//蛇的方向
}snake; //定义结构体变量snake
int tailX, tailY; //标记尾部位置
int FoodX, FoodY; //标记食物位置
//创建地图函数
void Creat_map()//创建地图
{
int i,j;
for(i=0;i<12;i++)
{
for(j=0;j<32;j++)
{
if(i==0||i==11)
{
map[j][i]=-2;
}
if(j==0||j==31)
{
map[j][i]=-2;
}
}
}
}
//绘制地图函数
void Paint_Map(int x,int y)//绘制地图点坐标
{
int i,j;
for(i=4*y;i<4*y+4;i++)
{
for(j=4*x;j<4*x+4;j++)
{
OLED_DrawPoint(j,i+16);
}
}
}
//绘制蛇头函数
void Paint_Head(int x,int y )//绘制蛇头点坐标
{
int i,j;
for(i=4*y;i<4*y+4;i++)
{
for(j=4*x;j<4*x+4;j++)
{
if(i==4*y||i==4*y+3)
{
OLED_DrawPoint(j,i+16);
}
if(j==4*x||j==4*x+3)
{
OLED_DrawPoint(j,i+16);
}
}
}
}
//绘制蛇身函数
void Paint_Body(int x,int y )//绘制蛇身函数
{
int i,j;
for(i=4*y;i<4*y+4;i++)
{
for(j=4*x;j<4*x+4;j++)
{
if(i==4*y||i==4*y+3)
{
OLED_DrawPoint(j,i+16);
}
if(j==4*x||j==4*x+3)
{
OLED_DrawPoint(j,i+16);
}
}
}
}
//绘制食物坐标函数
void Paint_Food(int x,int y )//绘制食物点坐标
{
int i,j;
for(i=4*y;i<4*y+4;i++)
{
for(j=4*x;j<4*x+4;j++)
{
if(i==4*y+1||i==4*y+2)
{
OLED_DrawPoint(j,i+16);
}
if(j==4*x+1||j==4*x+2)
{
OLED_DrawPoint(j,i+16);
}
}
}
// 更新食物的坐标
FoodX = x;
FoodY = y;
}
void Paint_Clean(int x,int y)//取消地图点坐标
{
int i,j;
for(i=4*y;i<4*y+4;i++)
{
for(j=4*x;j<4*x+4;j++)
{
OLED_DrawNoPoint(j,i+16);
}
}
}
//清除界面函数
void GUI_Clear()//界面清除
{
int i,j;
for(i=0;i<32;i++)
{
for(j=0;j<12;j++)
{
map[i][j]=0;
}
}
}
//画蛇函数:将二维数组snake.snake_Grid里面的各个蛇坐标的值存入map数组
void drawSnake() //画蛇
{
int i,x,y;
//蛇头
x=snake.snake_Grid[0][0];
y=snake.snake_Grid[0][1];
map[x][y]=1;
//蛇身
for(i=1;i
通过这个函数来输出这个游戏的所有显示数据。
//刷新界面函数:根据map数组里面的值一次性刷新游戏界面内的点坐标
void GUI_Refresh()//界面刷新
{
int i,j,temp;
for(i=0;i<32;i++)
{
for(j=0;j<12;j++)
{
temp=map[i][j];
switch (temp)
{
case 2:
Paint_Body(i,j);
break;
case 1:
Paint_Head(i,j);
break;
case -2:
Paint_Map(i,j);
break;
case-1:
Paint_Food(i,j);
break;
case 0:
Paint_Clean(i,j);
break;
}
}
}
}
初始化蛇长度为5、方向向右、蛇头坐标位于(7,5)。
//初始化蛇和地图函数:初定蛇长度为5、方向向右、蛇头坐标位于(7,5)
void Snake_Init()//蛇及地图初始化
{
int i;
snake.length=5;
snake.direction=RIGHT;
score=0;
snake.snake_Grid[0][0]=7;//x坐标,蛇头坐标
snake.snake_Grid[0][1]=5;//y坐标,蛇头坐标
tailX = snake.snake_Grid[snake.length - 1][0]; // Initialize tail coordinates
tailY = snake.snake_Grid[snake.length - 1][1];
for(i=1;i
初始在snake.h中宏定义了方向
#define RIGHT 1
#define LEFT 2
#define DOWN 3
#define UP 4
使用中断的方法读取按键对应引脚的上升沿,将值赋给全局变量KeyValue;
//按键处理函数:自定义矩形键盘值控制蛇的方向
void Get_Command()//获取键盘值
{
u8 key=1;
key=KeyValue;
switch(key)
{
case 1:if(snake.direction!=RIGHT) //左
snake.direction=LEFT;
break;
case 2:if(snake.direction!=LEFT) //右
snake.direction=RIGHT;
break;
case 3:if(snake.direction!=DOWN) //上
snake.direction=UP;
break;
case 4:if(snake.direction!=UP) //下
snake.direction=DOWN;
break;
}
}
void EXTI9_5_IRQHandler(void)
{
if(KEY8==1) //按键0
{
KeyValue=1;
EXTI->PR=1<<8;
}else if(KEY9==1){
KeyValue=2;
EXTI->PR=1<<9;
}
//清除LINE5上的中断标志位
}
//外部中断15~10服务程序
void EXTI15_10_IRQHandler(void)
{
if(KEY10==1) //按键1
{
KeyValue=3;
EXTI->PR=1<<10;
}else if(KEY11==1) //按键1
{
KeyValue=4;
EXTI->PR=1<<11;
}else if(KEY12==1) //按键1
{
Show_Endface();
KeyValue=5;
EXTI->PR=1<<12;
}
//清除LINE15上的中断标志位
}
//外部中断初始化程序
//初始化PA0,PC5,PA15为中断输入.
void EXTI_Init(void)
{
KEY_Init();
Ex_NVIC_Config(GPIO_B,8,RTIR); //上升沿触发
Ex_NVIC_Config(GPIO_B,9,RTIR); //上升沿触发
Ex_NVIC_Config(GPIO_B,10,RTIR); //上升沿触发
Ex_NVIC_Config(GPIO_B,11,RTIR); //上升沿触发
Ex_NVIC_Config(GPIO_B,12,RTIR); //上升沿触发
MY_NVIC_Init(2,0,EXTI9_5_IRQn,2); //抢占2,子优先级2,组2
MY_NVIC_Init(2,1,EXTI15_10_IRQn,2); //抢占2,子优先级2,组2
}
主要思想就是先判断蛇头结点是否与食物结点重合,如果没重合就删掉尾节点,头结点根据方向移动;如果重合,就不删掉尾节点,头结点根据方向移动。然后前结点的坐标等于后结点的坐标。
//蛇的移动函数:蛇身移动就让其坐标等于前一个坐标的值,蛇头则根据蛇的方向就行移动
void Move() {
int i;
int headX = snake.snake_Grid[0][0];
int headY = snake.snake_Grid[0][1];
tailX = snake.snake_Grid[snake.length - 1][0]; // 存储当前尾巴的坐标
tailY = snake.snake_Grid[snake.length - 1][1];
// 判断蛇头是否与食物坐标重合
if (headX == FoodX && headY == FoodY) {
snake.length++;
eated = true; // 设置为true,表示吃到了食物
score += 1;
Food(); // 生成新的食物
} else {
map[tailX][tailY] = 0; // 清除尾巴
}
for (i = snake.length - 1; i > 0; i--) {
snake.snake_Grid[i][0] = snake.snake_Grid[i - 1][0];
snake.snake_Grid[i][1] = snake.snake_Grid[i - 1][1];
}
switch (snake.direction) {
case UP:
snake.snake_Grid[0][1]--;
break;
case DOWN:
snake.snake_Grid[0][1]++;
break;
case LEFT:
snake.snake_Grid[0][0]--;
break;
case RIGHT:
snake.snake_Grid[0][0]++;
break;
}
drawSnake();
}
//生成食物函数:在游戏界面内空位生成食物点坐标,并将食物坐标存入map数组
int Chek(int i,int j)//检查地图空位
{
if(map[i][j]!=0)
{
return 0;
}
return 1; //是空位就返回1
}
void Food()//生成食物
{
int i,j;
do
{
i=(rand()%30)+1; //生成1~30之间的一个数
j=(rand()%10)+1; //生成1~10之间的一个数
}
while(Chek(i,j)==0); //检查该点是否为空位
map[i][j]=-1;//画出食物
}
bool GameOver()//游戏结束
{
bool isGameOver=false;
int sx=snake.snake_Grid[0][0],sy=snake.snake_Grid[0][1],i;//蛇头坐标
for(i=1; i
//显示分数
void Show_Score()//显示分数
{
OLED_ShowString(30,6,"Score:",16);
OLED_ShowNum(80,6,score,2,16);
}