基于 STM32F412RE 的 Flappy bird 游戏机实现

两年前博主还没有毕业,正值疫情期间,在家闲的慌,就利用这段时间做了一个游戏机。采用锂电池供电,屏幕是1.54的ST7789,单片机采用STM32F412RE,那时后芯片还没有开始涨价,在淘宝只要12元就可以买到一片,那叫一个爽啊。256K flash ,256k sram,直接通过DMA-SPI实现整屏刷新,速度非常快,刷屏可以到50帧以上。下面附上效果视频

Flappy bird 游戏机效果

这套设备带有wm8978,sd卡和一个耳机接口是支持音频输出的,可以移植NES模拟器到上面运行 256k的 sram ,足够动态加载大多数游戏了。

Flappy bird 按键检测实现

按键检测部分在rtthread的一个任务中进行扫描,没有加消抖(懒得加了,几乎没有影响),源码如下:

#include "drv_key.h"__IO uint8_t KEY_NUM;//键值rt_thread_t key_tid; //任务句柄
void key_thread_entry(void *parameter);
int bsp_key_init(void)
{rt_pin_mode(KEY_UP_PIN, PIN_MODE_INPUT_PULLUP); rt_pin_mode(KEY_DOWN_PIN, PIN_MODE_INPUT_PULLUP);
//  rt_pin_mode(KEY_LEFT_PIN, PIN_MODE_INPUT_PULLUP);
//  rt_pin_mode(KEY_RIGHT_PIN, PIN_MODE_INPUT_PULLUP);rt_pin_mode(KEY_A_PIN, PIN_MODE_INPUT_PULLUP);rt_pin_mode(KEY_B_PIN, PIN_MODE_INPUT_PULLUP);
//  rt_pin_mode(KEY_C_PIN, PIN_MODE_INPUT_PULLUP);
//  rt_pin_mode(KEY_D_PIN, PIN_MODE_INPUT_PULLUP);key_tid=rt_thread_create("key_thread",key_thread_entry, RT_NULL,256,20, 4);if (key_tid != RT_NULL)rt_thread_startup(key_tid);//启动return 0;
}
void key_thread_entry(void *parameter)
{uint8_t _key_num;while(1){_key_num=0;_key_num|=((!rt_pin_read(KEY_UP_PIN))<<0);_key_num|=((!rt_pin_read(KEY_DOWN_PIN))<<1);
//      _key_num|=((!rt_pin_read(KEY_LEFT_PIN))<<2);
//      _key_num|=((!rt_pin_read(KEY_RIGHT_PIN))<<3);_key_num|=((!rt_pin_read(KEY_A_PIN))<<4);_key_num|=((!rt_pin_read(KEY_B_PIN))<<5);
//      _key_num|=((!rt_pin_read(KEY_C_PIN))<<6);
//      _key_num|=((!rt_pin_read(KEY_D_PIN))<<7);KEY_NUM=_key_num;rt_thread_mdelay(10);}
}INIT_DEVICE_EXPORT(bsp_key_init);

Flappy bird 随机数实现

随机数的实现采用的是硬件随机数,通过ST的RNG外设实现,随机数用来设置柱子的高度。

#include "drv_rng.h"RNG_HandleTypeDef hrng;//初始化RNG
int bsp_rng_init(void)
{hrng.Instance=RNG;if (HAL_RNG_Init(&hrng) != HAL_OK){return 1;//随机数产生器工作不正常}else return 0;
}//生成[min,max]范围的随机数
int rng_get_random_range(int min,int max)
{ return HAL_RNG_GetRandomNumber(&hrng)%(max-min+1) +min;
}INIT_DEVICE_EXPORT(bsp_rng_init);

Flappy bird 屏幕驱动实现

屏幕刷新使用 SPI-DMA 半字单次传输,速度非常快,在sram里开辟一个显存,不断的更新显存上的内容,这里使用单次传输,为什么不使用连续传输呢,因为没有双BUFF,你在写缓存的时候,DMA正好在刷你这一块内存,显示会不正常。

#include "drv_st7789.h"SPI_HandleTypeDef hspi1;
DMA_HandleTypeDef hdma_spi1_tx;
//LCD显示缓存[y][x]
u16 lcd_buff[240][240]={0};
//外设初始化
int stm32_spi1_init(void)
{//初始化SPI1接口/8bithspi1.Instance = SPI1;hspi1.Init.Mode = SPI_MODE_MASTER;hspi1.Init.Direction = SPI_DIRECTION_1LINE;hspi1.Init.DataSize = SPI_DATASIZE_8BIT;hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;hspi1.Init.NSS = SPI_NSS_SOFT;hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;hspi1.Init.TIMode = SPI_TIMODE_DISABLE;hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;hspi1.Init.CRCPolynomial = 7;HAL_SPI_Init(&hspi1);//初始化SPI1_TX_DMA__HAL_RCC_DMA2_CLK_ENABLE();hdma_spi1_tx.Instance = DMA2_Stream3;hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;hdma_spi1_tx.Init.Mode = DMA_NORMAL;hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;HAL_DMA_Init(&hdma_spi1_tx);__HAL_LINKDMA(&hspi1,hdmatx,hdma_spi1_tx);HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);return 0;
}
void DMA2_Stream3_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma_spi1_tx);
}//传输完成回调函数
__IO uint8_t spi1_txcplt =0;//初始状态不允许传输
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{//HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)lcd_buff,  240 * 240);spi1_txcplt=1;
}//lcd_init
int bsp_st7789_init(void)
{stm32_spi1_init();rt_pin_mode(LCD_PIN_RST,PIN_MODE_OUTPUT);rt_pin_mode(LCD_PIN_DC,PIN_MODE_OUTPUT);rt_pin_mode(LCD_PIN_BL,PIN_MODE_OUTPUT);LCD_RST(0);rt_thread_mdelay(120);LCD_RST(1);rt_thread_mdelay(120);LCD_BL(1);LCD_Write_Cmd(0x11);rt_thread_mdelay(120);LCD_Write_Cmd(0x36);LCD_Write_Data(0x00);LCD_Write_Cmd(0x3A);LCD_Write_Data(0x05);LCD_Write_Cmd(0xB2);LCD_Write_Data(0x0C);LCD_Write_Data(0x0C);LCD_Write_Data(0x00);LCD_Write_Data(0x33);LCD_Write_Data(0x33);LCD_Write_Cmd(0xB7);LCD_Write_Data(0x35);LCD_Write_Cmd(0xBB);LCD_Write_Data(0x32);   //Vcom=1.625VLCD_Write_Cmd(0xC2);LCD_Write_Data(0x01);LCD_Write_Cmd(0xC3);LCD_Write_Data(0x15);LCD_Write_Cmd(0xC4);LCD_Write_Data(0x20);LCD_Write_Cmd(0xC6);LCD_Write_Data(0x0F);   //60MHZLCD_Write_Cmd(0xD0);LCD_Write_Data(0xA4);LCD_Write_Data(0xA1);LCD_Write_Cmd(0xE0);LCD_Write_Data(0xD0);LCD_Write_Data(0x08);LCD_Write_Data(0x0E);LCD_Write_Data(0x09);LCD_Write_Data(0x09);LCD_Write_Data(0x05);LCD_Write_Data(0x31);LCD_Write_Data(0x33);LCD_Write_Data(0x48);LCD_Write_Data(0x17);LCD_Write_Data(0x14);LCD_Write_Data(0x15);LCD_Write_Data(0x31);LCD_Write_Data(0x34);LCD_Write_Cmd(0xE1);LCD_Write_Data(0xD0);LCD_Write_Data(0x08);LCD_Write_Data(0x0E);LCD_Write_Data(0x09);LCD_Write_Data(0x09);LCD_Write_Data(0x15);LCD_Write_Data(0x31);LCD_Write_Data(0x33);LCD_Write_Data(0x48);LCD_Write_Data(0x17);LCD_Write_Data(0x14);LCD_Write_Data(0x15);LCD_Write_Data(0x31);LCD_Write_Data(0x34);LCD_Write_Cmd(0x21);LCD_Write_Cmd(0x29);LCD_Address_Set(0, 0, LCD_Width - 1, LCD_Height - 1);    //16bithspi1.Init.DataSize = SPI_DATASIZE_16BIT;HAL_SPI_Init(&hspi1);HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)lcd_buff,  240 * 240);return 0;
}
INIT_DEVICE_EXPORT(bsp_st7789_init);//写命令
static void LCD_Write_Cmd(u8 cmd)
{LCD_DC(0);HAL_SPI_Transmit(&hspi1,&cmd,1,1000);LCD_DC(1);
}
//写数据
static void LCD_Write_Data(u8 data)
{LCD_DC(1);HAL_SPI_Transmit(&hspi1,&data,1,1000);
}//设置窗口
void LCD_Address_Set(u16 x1, u16 y1, u16 x2, u16 y2)
{LCD_Write_Cmd(0x2a);LCD_Write_Data(x1 >> 8);LCD_Write_Data(x1);LCD_Write_Data(x2 >> 8);LCD_Write_Data(x2);LCD_Write_Cmd(0x2b);LCD_Write_Data(y1 >> 8);LCD_Write_Data(y1);LCD_Write_Data(y2 >> 8);LCD_Write_Data(y2);LCD_Write_Cmd(0x2C);
}//读点
u16 lcd_read_point(u16 x, u16 y)
{return lcd_buff[y][x];
}
//画点
void lcd_draw_point(u16 x, u16 y,u16 color)
{lcd_buff[y][x]=color;
}//更新屏幕
void lcd_update(void)
{if(spi1_txcplt==1)//可以传输{if(HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)lcd_buff,  240 * 240)==HAL_OK)spi1_txcplt=0;}
}
//等待更新屏幕完成
void lcd_wait_update(void)
{while(spi1_txcplt==0)rt_thread_mdelay(1);
}

Flappy bird 游戏核心实现

首先要想显示出图像,需要图像数据,如果直接使用16位色BMP图片,那对内存消耗是很大的,不适合在单片机上使用,使用这里图片使用一种讨巧的方式实现,这样的处理方式极大减低了系统资源的使用。逻辑部分有碰撞检测,模式切换,位置计数,背景循环等,不做赘述,代码如下:

#include "game_engine.h"//0透明1(0X0000)2(0XFFFF)3(0XFE00)4(0XF800)
//翅膀在中间
const uint8_t bird_ico1[12*17]=
{0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,2,2,1,0,0,0,0,0,0,0,1,3,3,3,3,1,2,2,2,2,1,0,0,0,0,0,1,3,3,3,3,3,1,2,2,2,1,2,1,0,0,0,1,3,3,3,3,3,3,1,2,2,2,1,2,1,0,0,0,1,1,1,1,1,3,3,3,1,2,2,2,2,1,0,0,1,2,2,2,2,2,1,3,3,3,1,1,1,1,1,1,0,1,2,2,2,2,2,1,3,3,1,4,4,4,4,4,4,1,0,1,1,1,1,1,3,3,1,4,1,1,1,1,1,1,0,0,0,1,3,3,3,3,3,3,1,4,4,4,4,4,1,0,0,0,0,1,1,3,3,3,3,3,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,
};
//翅膀在下面
static const uint16_t bird_ico2[12*17]=
{0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,2,2,1,0,0,0,0,0,0,0,1,3,3,3,3,1,2,2,2,2,1,0,0,0,0,0,1,3,3,3,3,3,1,2,2,2,1,2,1,0,0,0,1,3,3,3,3,3,3,1,2,2,2,1,2,1,0,0,0,1,1,1,1,1,3,3,3,1,2,2,2,2,1,0,0,1,1,2,2,2,2,1,3,3,3,1,1,1,1,1,1,0,1,2,2,2,2,2,1,3,3,1,4,4,4,4,4,4,1,1,2,2,2,2,1,3,3,1,4,1,1,1,1,1,1,0,1,2,2,2,2,3,3,3,3,1,4,4,4,4,4,1,0,1,2,2,2,1,3,3,3,3,3,1,1,1,1,1,0,0,0,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,
};
//翅膀在上面
static const uint16_t bird_ico3[12*17]=
{0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,3,3,3,1,2,2,1,0,0,0,0,0,1,1,1,3,3,3,3,1,2,2,2,2,1,0,0,0,1,2,2,2,1,3,3,3,1,2,2,2,1,2,1,0,0,1,2,2,2,2,3,3,3,1,2,2,2,1,2,1,0,0,1,2,2,2,2,1,3,3,3,1,2,2,2,2,1,0,0,1,2,2,2,2,2,1,3,3,3,1,1,1,1,1,1,0,1,1,2,2,2,2,1,3,3,1,4,4,4,4,4,4,1,0,1,1,1,1,1,3,3,1,4,1,1,1,1,1,1,0,0,0,1,3,3,3,3,3,3,1,4,4,4,4,4,1,0,0,0,0,1,1,3,3,3,3,3,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,
};static struct game_bird_conf //鸟信息控制块
{u8 mode; //0:开始界面1:开始游戏2:游戏结束u16 fps_t0,fps_t1;u16 fps_num;
} _game_bird_conf;
static struct bird_info //鸟信息控制块
{int x0,y0,y1;u8 h,w;u32 t0,t1;u32 t2,t3; //翅膀扇动时间u32 f; //翅膀扇状态0中间1下面2中间3上面float v0,a;u32 num; //通过柱数量u8 num_lock1,num_lock2;
} bird;static struct pillar_info //柱信息控制块
{int x0,x1;u8 h,w,j;u32 t0,t1; //运动时间float v0;
} pillar1,pillar2;//鸟初始化
static void bird_init(void)
{bird.y0=200;  //初始位置bird.y1=200;  //初始位置bird.x0=30;    bird.w=34;    //宽bird.h=24;     //高bird.t0=game_get_tick(); //初始时间bird.t2=game_get_tick(); //初始时间bird.f=0;         //初始翅膀状态bird.v0=0;       //初始速度bird.a=0.0005;//初始加速度bird.num=0;bird.num_lock1=0;bird.num_lock2=0;
}//柱初始化
static void pillar_init(void)
{pillar1.x0=200;  //初始位置pillar1.x1=200;  //初始位置pillar1.h=game_get_rng(30,80);//相对地高pillar1.w=40;         //宽pillar1.j=100;   //间隙pillar1.t0=game_get_tick(); //初始时间pillar1.v0=0.05;     //初始速度pillar2.x0=340;  //初始位置pillar2.x1=340;  //初始位置pillar2.h=game_get_rng(30,80);;pillar2.w=40;pillar2.j=100;pillar2.t0=game_get_tick(); //初始时间pillar2.v0=0.05;      //初始速度
}//计算鸟移动位置
static void bird_move(void)
{bird.t1=game_get_tick()-bird.t0;bird.t3=game_get_tick()-bird.t2;//翅膀bird.y1=(int)(bird.y0+(bird.v0*bird.t1)-(bird.a*bird.t1*bird.t1));//限位if(bird.y1<29+bird.h){bird.y1=29+bird.h;}else if(bird.y1>239){bird.y1=239;}//翅膀时间if(bird.t3>50){if(bird.f==3)bird.f=0;else bird.f++;bird.t2=game_get_tick(); //更新初时间}
}
//计算柱移动位置
static void pillar_move(void)
{pillar1.t1=game_get_tick()-pillar1.t0;pillar1.x1=(int)(pillar1.x0-(pillar1.v0*pillar1.t1));if(pillar1.x1<=-pillar1.w){pillar1.x0=240;pillar1.x1=240;pillar1.h=game_get_rng(30,80);pillar1.t0=game_get_tick();bird.num_lock1=0; //解锁}pillar2.t1=game_get_tick()-pillar2.t0;pillar2.x1=(int)(pillar2.x0-(pillar2.v0*pillar2.t1));if(pillar2.x1<=-pillar2.w){pillar2.x0=240;pillar2.x1=240;pillar2.h=game_get_rng(30,80);pillar2.t0=game_get_tick();bird.num_lock2=0; //解锁}//鸟通过次数if(pillar1.x1+pillar1.w<bird.x0&&bird.num_lock1==0){bird.num++;bird.num_lock1=1; //上锁}if(pillar2.x1+pillar2.w<bird.x0&&bird.num_lock2==0){bird.num++;bird.num_lock2=1; //上锁}
}//判断碰撞
static void bird_crash(void)
{if((bird.y1-bird.h)<=29||bird.y1>=239)//鸟碰地{_game_bird_conf.mode=3;//游戏结束}if((pillar1.x1-bird.x0)>=-pillar1.w+5&&(pillar1.x1-bird.x0)<=bird.w-5)//鸟碰柱1{if((bird.y1-pillar1.h-30)<=bird.h||(bird.y1-pillar1.h-30)>=pillar1.j){_game_bird_conf.mode=3;//游戏结束}}if((pillar2.x1-bird.x0)>=-pillar2.w+5&&(pillar2.x1-bird.x0)<=bird.w-5)//鸟碰柱2{if((bird.y1-pillar2.h-30)<=bird.h||(bird.y1-pillar2.h-30)>=pillar2.j){_game_bird_conf.mode=3;//游戏结束}}
}//画鸟
//0透明1(0X0000)2(0XFFFF)3(0XFE00)4(0XF800)
static void draw_bird(void)//34*24
{for(char i=0;i<12;i++){for(char j=0;j<17;j++){switch(bird.f){case 0: switch(bird_ico1[i*17+j]){case 0: break;case 1: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0X0000);break;case 2: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFFFF);break;case 3: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFE00);break;case 4: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XF800);break;}    break;case 1: switch(bird_ico2[i*17+j]){case 0: break;case 1: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0X0000);break;case 2: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFFFF);break;case 3: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFE00);break;case 4: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XF800);break;}break;case 2: switch(bird_ico1[i*17+j]){case 0: break;case 1: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0X0000);break;case 2: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFFFF);break;case 3: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFE00);break;case 4: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XF800);break;}    break;case 3: switch(bird_ico3[i*17+j]){case 0: break;case 1: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0X0000);break;case 2: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFFFF);break;case 3: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XFE00);break;case 4: game_fill_rect2(bird.x0+j*2,(240-bird.y1)+i*2,2,2,0XF800);break;}break;}}}
}
//画柱
static void draw_pillar(struct pillar_info * pillar)
{uint16_t pillar_ico1[18]={0X0000,0XFF91,0XFF91,0XAE91,0XAE91,0X354C,0XAE91,0X354C,0X354C,0X354C,0X354C,0X354C,0X354C,0X354C,0X2D22,0X354C,0X0C85,0X0000};uint16_t pillar_ico2[14]={0X0000,0XFF91,0XFF91,0XAE91,0XAE91,0X354C,0XAE91,0X354C,0X354C,0X354C,0X2D22,0X354C,0X0C85,0X0000};game_fill_rect1(pillar->x1,240-pillar->h-30,36,1,0X0000);for(char i=0;i<8;i++){for(char j=0;j<18;j++){game_fill_rect1(pillar->x1+j*2,240-pillar->h-30+1+i,2,1,pillar_ico1[j]);}}game_fill_rect1(pillar->x1,240-pillar->h-30+9,36,1,0X0000);for(char i=0;i<pillar->h-10;i++){for(char j=0;j<14;j++){game_fill_rect1(pillar->x1+j*2+4,240-pillar->h-30+10+i,2,1,pillar_ico2[j]);}}game_fill_rect1(pillar->x1,240-pillar->h-30-pillar->j-1,36,1,0X0000);for(char i=0;i<8;i++){for(char j=0;j<18;j++){game_fill_rect1(pillar->x1+j*2,240-pillar->h-30-pillar->j-i-2,2,1,pillar_ico1[j]);}}game_fill_rect1(pillar->x1,240-pillar->h-30-pillar->j-10,36,1,0X0000);for(char i=0;i<240-pillar->h-30-pillar->j-10;i++){for(char j=0;j<14;j++){game_fill_rect1(pillar->x1+j*2+4,240-pillar->h-30-pillar->j-11-i,2,1,pillar_ico2[j]);}}
}
//画地面
static void draw_brick(void)
{u8 buff[20]={0};game_fill_rect2(0,240-30,240,1,0X0000);for(char i=0;i<14;i++){game_fill_rect1(pillar1.x1-240+i*40,240-30+1,19,8,0XAE91);game_fill_rect1(pillar1.x1-240+19+i*40,240-30+1,20,8,0x8dc3);game_fill_rect1(pillar1.x1-240+39+i*40,240-30+1,1,8,0X0000);}game_fill_rect2(0,240-30+9,240,1,0X0000);game_fill_rect2(0,220,240,20,0xf64f);sprintf((char*)buff,"SCONRE:%d",bird.num);game_show_string(10,222,100,60,16,buff,0,0,1);sprintf((char*)buff,"FPS:%d",_game_bird_conf.fps_num);game_show_string(180,222,100,60,16,buff,0,0,1);
}//画背景
static void draw_back(void)
{//天空game_fill_rect2(0,0,240,210,0x8638);//房子game_fill_rect2(0,169,5,40,0xcfda);game_fill_rect2(5,169,15,40,0x9673);game_fill_rect2(20,149,5,60,0xcfda);game_fill_rect2(25,149,40,60,0xaed6);game_fill_rect2(65,179,20,30,0x9673);game_fill_rect2(85,169,5,40,0xcfda);game_fill_rect2(90,169,15,40,0x9673);game_fill_rect2(105,149,5,60,0xcfda);game_fill_rect2(110,149,40,60,0xaed6);game_fill_rect2(150,179,20,30,0x9673);game_fill_rect2(170,169,5,40,0xcfda);game_fill_rect2(175,169,15,40,0x9673);game_fill_rect2(190,149,5,60,0xcfda);game_fill_rect2(195,149,40,60,0xaed6);game_fill_rect2(235,179,5,30,0x9673);
}//画游戏结束对话框
static void draw_game_over(void)
{game_fill_rect2(40,50,160,100,0x0000);game_fill_rect2(42,52,156,96,0xf64f);game_show_string(60,60,100,32,32,"GameOver",0,0,1);game_show_string(55,105,110,24,24,"A:Gontinue",0,0,1);
}
//画游戏开始对话框
static void draw_game_start(void)
{game_fill_rect2(50,85,140,50,0x0000);game_fill_rect2(52,87,136,46,0xf64f);game_show_string(75,95,110,24,24,"A:Start",0,0,1);
}//flappy bird初始化
void game_flappy_bird_init(void)
{bird_init(); pillar_init();
}
//flappy bird游戏运行时按键处理
void game_flappy_bird_run_key(void)
{static u8 key=0;if((GAME_KEY_NUM&GAME_KEY_A)==0)key=0; //表示松开if((GAME_KEY_NUM&GAME_KEY_A)!=0&&key==0)//按下只执行一次{bird.y0=bird.y1; //更新位置bird.t0=game_get_tick(); //更新初时间bird.v0=0.3; //速度key=1; //表示按下}
}//flappy bird游戏开始时按键处理
void game_flappy_bird_start_key(void)
{static u8 key=0;if((GAME_KEY_NUM&GAME_KEY_A)==0)key=0; //表示松开if((GAME_KEY_NUM&GAME_KEY_A)!=0&&key==0)//按下只执行一次{_game_bird_conf.mode=2;//运行游戏game_flappy_bird_init();   //初始化while((GAME_KEY_NUM&GAME_KEY_A)!=0)rt_thread_mdelay(1);//等待松开key=1; //表示按下}
}
//flappy bird游戏结束时按键处理
void game_flappy_bird_over_key(void)
{static u8 key=0;if((GAME_KEY_NUM&GAME_KEY_A)==0)key=0; //表示松开if((GAME_KEY_NUM&GAME_KEY_A)!=0&&key==0)//按下只执行一次{_game_bird_conf.mode=2;//运行游戏game_flappy_bird_init();   //初始化while((GAME_KEY_NUM&GAME_KEY_A)!=0)rt_thread_mdelay(1);//等待松开key=1; //表示按下}
}
void game_flappy_bird_break_key(void)
{static u8 key=0;if((GAME_KEY_NUM&GAME_KEY_B)==0)key=0; //表示松开if((GAME_KEY_NUM&GAME_KEY_B)!=0&&key==0)//按下只执行一次{_game_bird_conf.mode=0;while((GAME_KEY_NUM&GAME_KEY_B)!=0)rt_thread_mdelay(1);//等待松开key=1; //表示按下}
}
//flappy bird逻辑计算
void game_flappy_bird_logic(void)
{bird_move(); //移动计算pillar_move(); //移动计算bird_crash();  //判断碰撞
}//flappy bird画图
void game_flappy_bird_draw(void)
{draw_back();                       //画背景draw_brick();                      //画地面draw_pillar(&pillar1);    //画柱1draw_pillar(&pillar2);    //画柱2draw_bird();                         //画鸟switch(_game_bird_conf.mode){case 1:draw_game_start(); //画游戏开始对话框break;case 3:draw_game_over(); //画游戏结束对话框break;}}//flappy bird主循环
void game_flappy_bird_main(void)
{u32 _fps_num=0;_game_bird_conf.fps_t0=game_get_tick();_game_bird_conf.fps_num=0;game_flappy_bird_init();   //初始化_game_bird_conf.mode=1;//开始态while(_game_bird_conf.mode){//FPS_fps_num++;_game_bird_conf.fps_t1=game_get_tick()-_game_bird_conf.fps_t0;if(_game_bird_conf.fps_t1>=1000){_game_bird_conf.fps_t0=game_get_tick();_game_bird_conf.fps_num=_fps_num;_fps_num=0;}//游戏switch(_game_bird_conf.mode){case 1:game_flappy_bird_start_key();break;case 2:game_flappy_bird_run_key();  //按键处理game_flappy_bird_logic();//逻辑计算break;case 3:game_flappy_bird_over_key();break;}game_flappy_bird_break_key();game_lcd_wait_update();  //等待屏幕就绪game_flappy_bird_draw(); //往缓冲区画图game_lcd_update();             //更新屏幕}
}

基于 STM32F412RE 的 Flappy bird 游戏机实现相关推荐

  1. flappy bird游戏源代码揭秘和下载后续---移植到android真机上

    前言:         上一篇博客 flappy bird游戏源代码揭秘和下载,源码是运行在window或者mac系统上的,现在我们需要把代码移植到android真机上,让小鸟在手机里飞起来! ps: ...

  2. flappy bird游戏源代码揭秘和下载后续---移植到html5网页浏览器

    前言:      我们分析了flappy bird的代码思路(flappy bird游戏源代码揭秘和下载),也移植到了android平台(flappy bird游戏源代码揭秘和下载后续---移植到an ...

  3. flappy bird游戏源代码揭秘和下载

    背景: 最近火爆全球的游戏flappy bird让笔者叹为观止,于是花了一天的时间山寨了一个一模一样的游戏,现在把游戏的思路和源码分享出来,代码是基于javascript语言,cocos2d-x游戏引 ...

  4. Python详细了解强化学习算法并基于强化学习Q_learning让电脑玩flappy bird游戏

    完整代码:https://github.com/Connor666/flappy_bird-RL 首先,如果你是为了追求一个非常高的强化学习效果,也就是flappy bird的分数,那么建议出门右拐选 ...

  5. 一步步分析AI如何玩Flappy Bird

    一.Flappy Bird 游戏展示 在介绍模型.算法前先来直接看下效果,上图是刚开始训练的时候,画面中的小鸟就像无头苍蝇一样乱飞,下图展示的是在本机(后面会给出配置)训练超过10小时后(训练步数超过 ...

  6. Flappy bird需求规格说明书

    1.引言     1.1.编写目的 该需求分析为说明书通过对<flappy bird>游戏软件的客户需求分析,明确了所要开发的游戏软件的功能以及界面等的处理,从而使小组成员更清楚的了解用户 ...

  7. cmd小游戏_使用pygame制作Flappy bird小游戏

    原文链接: [Python]使用Pygame做一个Flappy bird小游戏(一)​mp.weixin.qq.com 最近看到很多大佬用强化学习玩Flappy bird.所以打算也上手玩一玩,但是苦 ...

  8. 第3章 flappy bird作业、SVN、GIT、码云

    第3章别碰白块讲解后(童晶:3 别碰白块(<C和C++游戏趣味编程>配套教学视频)),请同学们做了一个flappy bird的练习: 在别碰白块代码基础上,实现flappy bird游戏效 ...

  9. 强化学习七日打卡营终极复现之flappy bird

    7天的实战很快就过去了,在调参调到怀疑人生时,"标准答案"却出奇的简单,另外每次训练时间都非常长,要是有加快训练的方法就好了.最后有一个终极复现可以自由发挥,这就来实现曾经想玩的f ...

  10. 使用神经网络和遗传算法玩转 Flappy Bird

    阅读原文请点击 摘要: 本文展示了针对Flappy Bird游戏设计的机器学习算法.本实验的目标是使用神经网络和遗传算法编写一个人工智能游戏控制器,打出游戏最高分,不服的来挑战! 我们创建一个人工智能 ...

最新文章

  1. AI 复活已故漫画家手冢治虫,出版新作续写传奇
  2. Electron 实战桌面计算器应用
  3. Linux 上不可修改的文件和目录
  4. 黄河科技学院计算机补考好过吗,学生吐槽:重修费每学分240元不该收 黄河科技学院回应:收费合理...
  5. Cobub无码埋点关键技术的实现
  6. springboot全局常量_Spring-Boot配置属性和环境变量的加载顺序
  7. mysql 数据库文件导入和导出、远程上传和下载数据库
  8. datatable怎么根据两列分组_公司要IT转型,我该怎么办?用Python进行数据处理
  9. php更新数据步骤,Thinkphp5模型更新数据方法
  10. C# string转Intptr Intptr转string
  11. MATLAB函数freqz的使用
  12. mysql二级软件_全国计算机等级考试二级MySQL练习软件
  13. iphone换android手机铃声,为什么大多数苹果手机用户只使用默认铃声,从不更换?原因很现实...
  14. linux gtk主题安装教程,如何获取、安装和制作 GTK 主题
  15. Mysql第一天笔记02——安装Navicat
  16. iDB-数据库自动化运维平台
  17. 视频摘要和视频浓缩的区别
  18. Eclipse插件集合
  19. Atitit.团队文化建设------影响组织的的一些原理 法则 定理 效应 p826.v4
  20. docker 容器 日志文件过大

热门文章

  1. pandas 中 rank 的用法
  2. c语言大数乘方算法,用c语言实现大数乘方
  3. VScode透明主题
  4. STIPC-003_编程挑战系列赛第三场(柯柯的期末祝福) _F.小柯来放水
  5. opp原则_完美事业OPP与NDO纲要
  6. 百度地图实现定位图标随手机方向变化而变化,即运用方向传感器
  7. 计算机语言处理器,计算机语言处理器
  8. Win8.1和Win10各自的优势
  9. Adversarial Machine Learning 经典算法解读(FGSM, DeepFool)
  10. C语言是否能用memcmp函数比较结构体