简介:本实验基于stm32最小系统、0.96寸OLED(68*128)和摇杆实现一个经典的贪吃蛇小游戏。项目源码地址:点击下载。
普通摇杆,0.96寸OLED 单色屏幕(SPI协议通讯),stc32f103c8t6最小系统开发板,如下所示。
器件齐全后,用杜邦线将摇杆和oled正确地连接到stm32开发板上,注意各个器件的电源需要接到同一电压,下图是接线说明:
接好线之后,将源码编译下载,进入调试,正常运行如下图所示,如果屏幕出现花屏,那么将res引脚接到GND进行复位,再重新进行调试或单片机复位:
接着按下摇杆的中键就可以进入游戏:
屏幕的上方显示得分,通过摇杆控制蛇的运动方向。
游戏失败后可按下摇杆中键重新开始游戏。
源代码下载解压后,利用keil4工具打开工程可看到:
上面已表明各个文件的作用, 可重点看Snake.c游戏设计文件。如果代码里面的注释是乱码,点击Edit->Configuration->Editor->Encoding->选择Chinese GB2312。下面面只列举部分代码说明。
main函数
int main(void)
{
RCC_PCLK1Config(RCC_HCLK_Div4);
USART1_Configuration(); //初始化串口
TIME_Configuration(); //初始化定时器
ADC_Configuration(); //初始化摇杆的ad采集
OLED_Init(); //oled初始化默认参数
GameReady(); //进入游戏准备界面
Tick_Updata(&sysms);
while (1){
event=JoyState(); //获取摇杆的状态
ret=Game_InputHandle(event);//蛇移动
if(ret){ //ret=1 表示屏幕需刷新
GameMapToLcdCache(); //将像素从游戏图像gamemap位图经过放大后写进LcdCache显存
OLED_Updata();//将LcdCache[8][128]显存的数据发送到屏幕进行显示
sprintf(str,"Score=%d",GameScore);
OLED_DispString(0,0,str); //屏幕上方显示分数
}
}
}
main函数并不复杂,while循环里面不断获取摇杆状态,将状态传进Game_InputHandle函数进行游戏操作。
实现原理:
运用链表的特性,蛇的每一个节点就是链表的节点,节点的内容是该点的x,y坐标,如上图所示。遍历链表逐个读取蛇身的每个节点里面的x,y坐标,根据该坐标在屏幕上描点,即可描绘出蛇的形状来。
蛇的运动的原理是从蛇尾节点开始,其x,y坐标等于前一个节点的坐标,直至到头节点,头节点的新x,y坐标跟蛇运动的方向有关。每当蛇迟到食物时,尾部追加节点。
void GAME_NewSnake(SNAKELIST* SnakeList) //初始化链表,初始化蛇长度,4+1节
{
int x=4,y=15,i;//蛇的初始位置
SnakeList->x=x;
SnakeList->y=y++;
SnakeList->prev=SnakeList;
SnakeList->next=SnakeList;
for(i=0;i<4;i++){
GAME_SnakeListAddNode(SnakeList,x,y+i);
}
GAME_SnakeFillInGameMap(gamemap,SnakeList);
}
GAME_NewSnake游戏开始前,给蛇初始化5个节点,头节点在x=4,y=15处,其余4个节点在其下方。通过GAME_SnakeFillInGameMap函数将蛇在gamemap上描画出来。gamemap再放大映射到LCDcache显存里面去,再通过spi把整个屏幕的像素发送到屏幕进行显示。
void GAME_NewFood(unsigned char (*gamemap)[WIDTH]) //在地图上随机产生新的食物
{
unsigned int seed1,seed2;
int x,y;
while(1){
Tick_Updata(&seed1);
Tick_Updata(&seed2);
x=seed1%WIDTH;
y=seed2%HEIGHT;
if(gamemap[y][x]==0){
gamemap[y][x]=2;
break;
}
}
}
GAME_NewFood在地图上随机生成一个食物,当然这里的随机数,利用不断变化的时钟进行求余,得到的随机坐标后,先判断该左边是否可用,若是已经存在东西(蛇身或者边框)则继续获取随机数,如此反复直到得到一个空的的随机坐标。所以,当游戏玩到最后,蛇身很长的时候(界面没有多少空白点),产生食物的时间会长很多。
unsigned char Game_InputHandle(unsigned char event)//对输入按键事件的处理
{
unsigned char ret=0;
if(GameStatus==GAMEPAUSE&&event!=RESTART_EV){ //游戏状态未运行时,除非按下restart,否则不进入
return 0;
}
if(event==5-Snokedirection){//按下蛇前进的相反方向时,忽略
event=NON;
}
switch(event){
case NON:
speed_max=200;
if(If_TimeOut(&speed_move,SPPED_MOVE)){ //自动前进
Tick_Updata(&speed_move);
event=Snokedirection;
}else{
break;
}
case TURN_LEFT_EV: //蛇向左移动
case TURN_RIGHT_EV://蛇向右移动
case TURN_DOWN_EV: //蛇向下移动
case TURN_UP_EV: //蛇向上移动
if(If_TimeOut(&speed_turn,speed_max)){
Tick_Updata(&speed_turn);
speed_max=150;
Snokedirection=event;
ret=GAME_SnakeMove(gamemap,&SnokeHeadNode,event,&GameScore);
printf("event=%d\r\n",event);
}
break;
case RESTART_EV://游戏复位
if(If_TimeOut(&speed_restart,SPPED_RESTART_MAX)){
Tick_Updata(&speed_restart);
GameStatus=GAMERUNING;
GameScore=0;
Snokedirection=TURN_UP_EV;
GAME_BackgroundInit(gamemap);
GAME_NewSnake(&SnokeHeadNode);
GAME_NewFood(gamemap);
ret=1;
printf("event=%d\r\n",event);
}
break;
default:
//error
break;
}
return ret;
}
Game_InputHandle函数在main函数里被调用,SPPED_MOVE这个宏是控制蛇自动移动的速度的,所设置的值越低则运动的越快。speed_max的值是控制手动移动的速度,当摇杆状态被持续维持为一个方向时,就会按照该速度进行前进。
源码添加了许多注释,欢迎下载。若有疑问欢迎提出。
相关推荐:
基于stm32、0.96寸OLED实现的俄罗斯方块小游戏(详细源码注释)
<<天下难事,必做于易;天下大事,必做于细。——老子>>