智能手表:小恐龙游戏
小恐龙
初始菜单下 Key3 按下进入游戏选择界面呈现 “谷歌小恐龙”,Key2 可下移光标定位该选项,再按 Key3 进入游戏,游戏中 Key3 控制小恐龙起跳、OLED 以 0.1s 为单位计时,小恐龙碰障碍物则触发 “Game Over” 结束游戏 。
先写一个计分页面,通过计时器0.1s cnt++ 在定时器中调用
每累计到 100(对应 0.1 秒计时单位)就将分数Score
加 1
int Score=0;
//显示分数
void Show_Score(void)
{OLED_ShowNum(91,0,Score,5,OLED_6X8);
}
void Dino_Tick(void)
{static uint8_t Score_Count=0,Ground_cnt,Cloud_Count;Score_Count++;Ground_cnt++;Cloud_Count++;if(Score_Count>=100)//计分 0.1s{Score_Count=0;Score++;}
}
写地面向左移动效果
地面的宽为256个像素点 高为8
让其不断向左移动
将256分为2组,当一组刚好移动到显示平外时,另一组刚好在屏幕内
每20ms Ground_Pos ++ 标志位加一
OLED_DisplayBuf[7][i]显存数组在 OLED 屏幕的特定行(第 7 行)绘制地面(Ground)图案,并通过偏移 Ground_Pos变量实现地面的滚动效果
Ground[];是取模生成的地面
显存函数就128个 但是Ground的256个
当Ground_Pos<128时,正常显示
当Ground_Pos>128时,比如 Ground_Pos=200, 255-200=55此时地面才有55长度,不够屏幕的宽度,怎么办呢,所以就得从开头的位置截取73的长度,也就是,让Ground_Pos从0计数到73,来补全空余的
//显示地面
uint16_t Ground_Pos;
void Show_Ground(void)
{if(Ground_Pos<128){for(uint8_t i=0;i<128;i++){OLED_DisplayBuf[7][i]=Ground[i+Ground_Pos];}}else {for(uint8_t i=0;i<255-Ground_Pos;i++){OLED_DisplayBuf[7][i]=Ground[i+Ground_Pos];}for(uint8_t i=255-Ground_Pos;i<128;i++){OLED_DisplayBuf[7][i]=Ground[i-(255-Ground_Pos)];}}}
void Dino_Tick(void)
{static uint8_t Score_Count=0,Ground_cnt,Cloud_Count;Score_Count++;Ground_cnt++;Cloud_Count++;if(Score_Count>=100)//计分 0.1s{Score_Count=0;Score++;}if(Ground_cnt>=20){Ground_cnt=0;//20ms地面 和 仙人掌一起移动Ground_Pos++;Barrier_Pos++;if(Ground_Pos>=256)Ground_Pos=0;if(Barrier_Pos>=144) Barrier_Pos=0;}
}
写障碍物显示的函数
当障碍物移动过去时,才显示取一次随机数
barrier_flag
用于选择障碍物类型(随机取 0-2),Barrier_Pos
记录障碍物偏移位置;当偏移量≥143 时重新随机切换障碍物类型,通过OLED_ShowImage
在 (127-Barrier_Pos,44) 位置显示 16x18 大小的障碍物图像。
//障碍物
uint8_t barrier_flag;
uint8_t Barrier_Pos;
struct Object_Position barrier;
void Show_Barrier(void)
{if(Barrier_Pos>=143){barrier_flag=rand()%3;}OLED_ShowImage(127-Barrier_Pos,44,16,18,Barrier[barrier_flag]);barrier.minX=127-Barrier_Pos;barrier.maxX=127-Barrier_Pos+16;barrier.minY=44;barrier.maxY=44+18;}
void Dino_Tick(void)
{static uint8_t Score_Count=0,Ground_cnt,Cloud_Count;Score_Count++;Ground_cnt++;Cloud_Count++;if(Score_Count>=100)//计分 0.1s{Score_Count=0;Score++;}if(Ground_cnt>=20){Ground_cnt=0;//20ms地面 和 仙人掌一起移动Ground_Pos++;Barrier_Pos++;if(Ground_Pos>=256)Ground_Pos=0;if(Barrier_Pos>=144) Barrier_Pos=0;}
}
显示云朵 50ms移动一次 16x8 大小的云朵图像
uint8_t Cloud_Pos;
void Show_Cloud(void)
{OLED_ShowImage(127-Cloud_Pos,9,16,8,Cloud);
}void Dino_Tick(void)
{//接上面程序if(Cloud_Count>=50){ Cloud_Count=0;Cloud_Pos++;if(Cloud_Pos>=200)Cloud_Pos=0;}
}
画小恐龙,后脚着地,前脚着地,双脚着地的图片,取模软件取模
两种状态 奔跑 跳跃
奔跑:前后脚着地,每50ms切换一次
跳跃:就是Y坐标从小到大再到小
也就是sin函数 w=2*pi/T=2*pi/2*1s=pi
Y=28*sin(pi*t/1000)把s转化为ms
通过按键 1 触发跳跃(dino_jump_flag=1
),利用正弦函数计算跳跃位移(Jump_Pos=28*sin(pi*jump_t/1000)
)模拟抛物线运动;奔跑状态下根据Cloud_Pos
奇偶性切换两张恐龙图像实现跑步动画,跳跃时显示跳跃姿态图像;
//恐龙
extern uint8_t KeyNum;
uint16_t jump_t; //跳跃计数
uint8_t dino_jump_flag=0;//0 奔跑 1 跳跃
uint8_t Jump_Pos; //跳跃位移
extern double pi;struct Object_Position dino;void Show_Dino(void)
{KeyNum=Key_GetNum();if(KeyNum==1) dino_jump_flag=1;Jump_Pos=28*sin((float)(pi*jump_t/1000));if(dino_jump_flag==0){if(Cloud_Pos%2==0) OLED_ShowImage(0,44,16,18,Dino[0]);else OLED_ShowImage(0,44,16,18,Dino[1]);}else{OLED_ShowImage(0,44-Jump_Pos,16,18,Dino[2]);}dino.minX=0;dino.maxX=16;dino.minY=44-Jump_Pos;dino.maxY=62-Jump_Pos;}
写个检测是否碰到障碍
定义碰撞边界
为小恐龙和障碍物分别设定矩形碰撞框(通过struct Object_Position
存储):- 小恐龙:
dino.minX
(左边界)、dino.maxX
(右边界)、dino.minY
(上边界)、dino.maxY
(下边界) - 障碍物:
barrier.minX
、barrier.maxX
、barrier.minY
、barrier.maxY
- 小恐龙:
判断坐标重叠
当两个物体的矩形区域同时满足以下条件时,判定为碰撞:- 小恐龙的右边界 > 障碍物的左边界(
dino.maxX > barrier.minX
) - 小恐龙的左边界 < 障碍物的右边界(
dino.minX < barrier.maxX
) - 小恐龙的下边界 > 障碍物的上边界(
dino.maxY > barrier.minY
) - 小恐龙的上边界 < 障碍物的下边界(
dino.minY < barrier.maxY
)
- 小恐龙的右边界 > 障碍物的左边界(
void Show_Dino(void)
{//接上面的代码dino.minX=0;dino.maxX=16;dino.minY=44-Jump_Pos;dino.maxY=62-Jump_Pos;}
void Show_Barrier(void)
{//接上面的代码barrier.minX=127-Barrier_Pos;barrier.maxX=127-Barrier_Pos+16;barrier.minY=44;barrier.maxY=44+18;}
int isColliding(struct Object_Position *a,struct Object_Position *b)
{if((a->maxX>b->minX) && (a->minX<b->maxX) &&(a->maxY>b->minY) &&(a->minY<b->maxY)){OLED_Clear();OLED_ShowString(28,24,"Game Over",OLED_8X16);OLED_Update();Delay_s(1);OLED_Clear();OLED_Update();return 1;}return 0;}