基于 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接口/8bit
hspi1.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.625V
LCD_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); //60MHZ
LCD_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);
//16bit
hspi1.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); //画柱1
draw_pillar(&pillar2); //画柱2
draw_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(); //更新屏幕
}
}