完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
这是一个几个星期之前的小项目,参考修改了一下网上和正点原子的例程。
功能:贪吃蛇小游戏 单片机:stm32f103c8t6 用到的硬件资源: 1.LED指示灯 2.矩阵键盘 3.OLED模块 4.通用定时器 软件设计: 1.矩阵键盘(中断方式)前面文章有介绍 2.定时器中断:用于更新游戏界面 3.贪吃蛇设计: OLED的像素点是128*64的,为了可以显示清晰,在这里把游戏的点坐标映射为32x12(map【32】【12】),每个坐标占用16个像素点,前16行像素点用于显示分数。 #define MAXLENGTH 100 //蛇的最大长度 int map[32][12]={0};//地图大小 x,y(一个坐标为4*4个像素点) 实际按4倍尺寸放大后地图大小为128*48个像素点 int score; //分数 bool eated=false; //蛇吃到食物的标记 extern u8 KeyValue; //获取按键值 struct { int snake_Grid[MAXLENGTH][2]; //二维数组,行坐标表示蛇节点,列表示当前节点的x,y坐标 int length; //蛇的长度 int direction;//蛇的方向 }snake; //定义结构体变量snake 清除界面函数 void GUI_Clear(int map[32][12])//界面清除 { int i,j; for(i=0;i<32;i++) { for(j=0;j<12;j++) { map[j]=0; } } } 创建地图函数:填充游戏界面的边界坐标,并将边界坐标的值存入map数组 void Creat_map(int map[32][12])//创建地图 { int i,j; for(i=0;i<12;i++) { for(j=0;j<32;j++) { if(i==0||i==11) { map[j]=-2; } if(j==0||j==31) { map[j]=-2; } } } } 绘制地图函数:用画点函数将坐标绘出来,y坐标+16是因为前16行用来显示分数,16行以后为游戏界面 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_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); } } } } 刷新界面函数:根据map数组里面的值一次性刷新游戏界面内的点坐标 void GUI_Refresh(int map[32][12])//界面刷新 { int i,j,temp; for(i=0;i<32;i++) { for(j=0;j<12;j++) { temp=map[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; } } } OLED_Refresh_Gram(); } 初始化蛇和地图函数:初定蛇长度为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坐标,蛇头坐标 for(i=1;i snake.snake_Grid[0]=snake.snake_Grid[0][0]-i; snake.snake_Grid[1]=snake.snake_Grid[0][1]; //给刚开始的蛇身几个初始坐标 } Creat_map(map); } 画蛇函数:将二维数组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 x=snake.snake_Grid[0]; y=snake.snake_Grid[1]; map[x][y]=2; } } 按键处理函数:自定义矩形键盘值控制蛇的方向 void Get_Command()//获取键盘值 { u8 key; key=KeyValue; switch(key) { case 5:if(snake.direction!=RIGHT) //左 snake.direction=LEFT; break; case 7:if(snake.direction!=LEFT) //右 snake.direction=RIGHT; break; case 2:if(snake.direction!=DOWN) //上 snake.direction=UP; break; case 6:if(snake.direction!=UP) //下 snake.direction=DOWN; break; } } 蛇的移动函数:蛇身移动就让其坐标等于前一个坐标的值,蛇头则根据蛇的方向就行移动 void Move()//移动 { int i; map[snake.snake_Grid[snake.length-1][0]][snake.snake_Grid[snake.length-1][1]]=0;//清除尾巴 if(eated) //如果吃到了食物 { snake.length++; eated=false; //设置为false,不然无限变长 } for(i=snake.length-1;i>0;i--) //从尾巴开始,蛇身每一个点的位置等于它前面一个点的位置 { snake.snake_Grid[0]=snake.snake_Grid[i-1][0]; snake.snake_Grid[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; } } 生成食物函数:在游戏界面内空位生成食物点坐标,并将食物坐标存入map数组 int Chek(int i,int j)//检查地图空位 { if(map[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[j]=-1;//画出食物 } 判断蛇吃食物:蛇吃到食物后通过eated标记状态(传递给move函数使蛇身+1)、分数+1、重新生成食物 bool GameOver()//游戏结束 { bool isGameOver=false; int sx=snake.snake_Grid[0][0],sy=snake.snake_Grid[0][1],i;//蛇头坐标 for(i=1; i if(snake.snake_Grid[0]==sx&&snake.snake_Grid[1]==sy) isGameOver=true; } if(snake.snake_Grid[0][0]==31||snake.snake_Grid[0][0]==0|| snake.snake_Grid[0][1]==11||snake.snake_Grid[0][1]==0) //判断有没有撞墙 isGameOver=true; return isGameOver; } 判断游戏是否结束: void Show_Score()//显示分数 { OLED_ShowString(30,0,"Score:",16); OLED_ShowNum(80,0,score,2,16); } 显示分数: void TIM4_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能 //定时器TIM4初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 TIM_Cmd(TIM4, ENABLE); //使能TIMx } //定时器4中断服务程序 void TIM4_IRQHandler(void) //TIM4中断 { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否 { TIM_ClearITPendingBit(TIM4, TIM_IT_Update ); //清除TIMx更新中断标志 Move(); Eat_Food(); drawSnake(); } } |
|
|
|
只有小组成员才能发言,加入小组>>
3316 浏览 9 评论
2995 浏览 16 评论
3494 浏览 1 评论
9060 浏览 16 评论
4088 浏览 18 评论
1180浏览 3评论
605浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
599浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2335浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1896浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 01:55 , Processed in 0.728734 second(s), Total 75, Slave 56 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号