基于 STM32F412RE 的 Flappy bird 游戏机实现

分享一款基于STM32F412RE单片机和ST7789屏幕的游戏机实现,包含锂电池供电、按键检测、硬件随机数生成、屏幕刷新和FlappyBird游戏核心。作者详细介绍了关键组件如按键扫描、随机数生成器和屏幕驱动的代码实现。
摘要由CSDN通过智能技术生成

基于 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();			 //更新屏幕
	}
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值