基于stm32、0.96寸OLED实现的贪吃蛇小游戏(详细源码注释)

简介:本实验基于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实现的俄罗斯方块小游戏(详细源码注释)

 

<<天下难事,必做于易;天下大事,必做于细。——老子>>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章