本教程将使用赤菟开发板上的sw1开关实现小鸟的跳跃并用LCD屏幕来显示图片。
简单来说,就如同原版游戏一样,每次点击sw1按钮小鸟就会向上弹跳,一旦碰到柱子或者是墙就会结束游戏并告诉你游戏里你通过了多少个柱子。
算法实现flappybird
1.鸟的设计
要完成这个实验,首先要分步设计游戏的各个部分。那么我们最开始就要先设计鸟。我首先想到要把鸟在一个区域内随机出现。首先我们要让鸟不能出现在柱子的区域里防止游戏直接结束。在查询了资料后,我借鉴了贪吃蛇里生成食物的代码来写,使其出现在一个合适的位置。bird.x=((RNG_GetRandomNumber()%25));表明鸟在x轴坐标为0-25的区域,y同理。
void creatbird()
{
int flag=0;
while(!flag)
{
flag=1;
bird.x=((RNG_GetRandomNumber()%25));
bird.y=((RNG_GetRandomNumber()%23)*10);
}
lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, RED);
}
鸟的移动
在我创建了鸟之后我开始设计鸟像原版游戏一样无时无刻往下落,所以为了让鸟能够每时每刻下落,我使用了y-10=。又为了让鸟能够表现出下落的样子,我使用了lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, WHITE);来擦除原来的鸟,并把新的鸟打印在lcd屏幕上。之后写出用按键让鸟在按键按下后每次上升20个单位的代码,这样就能完成鸟的移动部分的代码。
void movebird()
{
uint8_t getkey =0;
lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, WHITE);
getkey=Basic_Key_Handle();
bird.y+=10;
switch(getkey)
{
case sw1:
bird.y-=30;
break;
default:
break;
}
lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, RED);
Delay_Ms(250);
}
柱子的创建
之后就要开始创建柱子了。创建柱子我设计的柱子大小是60,每隔60个单位产生一个新的柱子,高度是在一定范围内随机的,为了防止生成重叠的上下柱子,因此设定了柱子的上下高度。因为显示屏的宽度是239,所以在屏幕上最多可以产生两个柱子。
void creatpillar1()
{
pillar1.l=180;
pillar1.h1=((RNG_GetRandomNumber()%80));
pillar1.h2=90+((RNG_GetRandomNumber()%100));
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
}
void creatpillar2()
{
pillar2.l=180;
pillar2.h1=((RNG_GetRandomNumber()%90));
pillar2.h2=90+((RNG_GetRandomNumber()%110));
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
柱子的移动
在考虑柱子的移动时,我们要分情况讨论。首先是第一个柱子第一次x轴坐标大于60的时候,第二个柱子没有刷出来。然后是第二个柱子刷出来了之后。这个要分种情况。首先是柱子1在x轴坐标为0到60的情况,正常移动就行。在柱子1x轴坐标为0到-60的情况下,柱子1的左边的刷新位置从0开始就可以了。柱子2也是同理。
void movepillar()
{
if(pillar1.l>60&&bird.h==0)
{
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
pillar1.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
}
else if(pillar1.l==60)
{
lcd_fill(0,0,239,239,WHITE);
pillar1.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
creatpillar2();
bird.s++;
bird.h++;
}
else if(pillar1.l>0&&pillar1.l<60)
{
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
else if(pillar1.l<=0&&pillar1.l+60>0)
{
lcd_fill(0,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(0,pillar1.h2,pillar1.l+60,239,WHITE);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(0,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(0,pillar1.h2,pillar1.l+60,239,BLACK);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
else if(pillar1.l==-60)
{
lcd_fill(0,0,239,239,WHITE);
pillar2.l-=10;
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
creatpillar1();
bird.s++;
}
else if(pillar2.l>0&&pillar2.l<=60)
{
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
else if(pillar2.l<=0&&pillar2.l+60>0)
{
lcd_fill(0,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(0,pillar2.h2,pillar2.l+60,239,WHITE);
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(0,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(0,pillar2.h2,pillar2.l+60,239,BLACK);
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
}
}
判断条件
在这个部分要判断鸟是否撞上了柱子或者墙,为了简化代码,我使用了结构体来判断。判断是否撞到墙了就是y轴坐标小于0或者y轴坐标加8大于239,而判断撞到柱子上则是鸟的x轴坐标在柱子的x坐标范围内并且鸟的y轴坐标在柱子的y轴坐标内。
int judge(struct Pillar pillar)
{
if(bird.y<0||bird.y>239)
{
return 0;
}
if(bird.y>=0&&bird.y<=pillar.h1)
{
if(pillar.l-bird.x-8<=0&&pillar.l+60-bird.x>=0)
{
return 0;
}
}
if(bird.y+8>=pillar.h2&&bird.y+8<=239)
{
if(pillar.l-bird.x-8<=0&&pillar.l+60-bird.x>=0)
{
return 0;
}
}
return 1;
}
按键设置
接着就是设置按键。通过查询芯片手册我设置按键如下。
void GPIO_INIT()
{
GPIO_InitTypeDef GPIO_InitTypdefStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitTypdefStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_InitTypdefStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitTypdefStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitTypdefStruct);
GPIO_InitTypdefStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitTypdefStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitTypdefStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitTypdefStruct);
}
/*********************************************************************
设置按键
*/
uint8_t Basic_Key_Handle( void )
{
uint8_t keyval = 0;
if( ! GPIO_ReadInputDataBit( GPIOE, GPIO_Pin_4 ) )
{
Delay_Ms(10);
if( ! GPIO_ReadInputDataBit( GPIOE, GPIO_Pin_4 ) )
{
keyval = sw1;
}
}
return keyval;
}
完整代码
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*
*@Note
Random number generator routine:
Generate a 32-bit random number and print it through the serial port (PA9).
*/
#include "debug.h"
#include "lcd.h"
#include "ch32v30x_rng.h"
#define sw1 3
struct Bird
{
int x;
int y;
int s;
int h;
}bird;
struct Pillar
{
int l;
int h1,h2;
}pillar1,pillar2;
/*******************************************************************************
* Function Name : Basic_Key_Handle
* Description : Basic Key Handle
* Input : None
* Return : 0 = no key press
* key = key press down value
*******************************************************************************/
void GPIO_INIT()
{
GPIO_InitTypeDef GPIO_InitTypdefStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitTypdefStruct.GPIO_Pin = GPIO_Pin_4;
GPIO_InitTypdefStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitTypdefStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitTypdefStruct);
GPIO_InitTypdefStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitTypdefStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitTypdefStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitTypdefStruct);
}
/*********************************************************************
设置按键
*/
uint8_t Basic_Key_Handle( void )
{
uint8_t keyval = 0;
if( ! GPIO_ReadInputDataBit( GPIOE, GPIO_Pin_4 ) )
{
Delay_Ms(10);
if( ! GPIO_ReadInputDataBit( GPIOE, GPIO_Pin_4 ) )
{
keyval = sw1;
}
}
return keyval;
}
/*********************************************************************
创建鸟并使其在一个随机位置出现
*/
void creatbird()
{
int flag=0;
while(!flag)
{
flag=1;
bird.x=((RNG_GetRandomNumber()%25));
bird.y=((RNG_GetRandomNumber()%23)*10);
}
lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, RED);
}
/*********************************************************************
实现鸟的移动
*/
void movebird()
{
uint8_t getkey =0;
lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, WHITE);
getkey=Basic_Key_Handle();
bird.y+=10;
switch(getkey)
{
case sw1:
bird.y-=30;
break;
default:
break;
}
lcd_fill(bird.x, bird.y, bird.x+8, bird.y+8, RED);
Delay_Ms(250);
}
/*********************************************************************
创建柱子
*/
void creatpillar1()
{
pillar1.l=180;
pillar1.h1=((RNG_GetRandomNumber()%80));
pillar1.h2=90+((RNG_GetRandomNumber()%100));
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
}
void creatpillar2()
{
pillar2.l=180;
pillar2.h1=((RNG_GetRandomNumber()%90));
pillar2.h2=90+((RNG_GetRandomNumber()%110));
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
/*********************************************************************
* 移动柱子
*/
void movepillar()
{
if(pillar1.l>60&&bird.h==0)
{
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
pillar1.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
}
else if(pillar1.l==60)
{
lcd_fill(0,0,239,239,WHITE);
pillar1.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
creatpillar2();
bird.s++;
bird.h++;
}
else if(pillar1.l>0&&pillar1.l<60)
{
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
else if(pillar1.l<=0&&pillar1.l+60>0)
{
lcd_fill(0,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(0,pillar1.h2,pillar1.l+60,239,WHITE);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(0,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(0,pillar1.h2,pillar1.l+60,239,BLACK);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
else if(pillar1.l==-60)
{
lcd_fill(0,0,239,239,WHITE);
pillar2.l-=10;
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
creatpillar1();
bird.s++;
}
else if(pillar2.l>0&&pillar2.l<=60)
{
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,BLACK);
}
else if(pillar2.l<=0&&pillar2.l+60>0)
{
lcd_fill(0,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(0,pillar2.h2,pillar2.l+60,239,WHITE);
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,WHITE);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,WHITE);
pillar1.l-=10;
pillar2.l-=10;
lcd_fill(0,0,pillar2.l+60,pillar2.h1,BLACK);
lcd_fill(0,pillar2.h2,pillar2.l+60,239,BLACK);
lcd_fill(pillar1.l,0,pillar1.l+60,pillar1.h1,BLACK);
lcd_fill(pillar1.l,pillar1.h2,pillar1.l+60,239,BLACK);
}
}
/*********************************************************************
判断鸟是否撞到柱子或者墙
*/
int judge(struct Pillar pillar)
{
if(bird.y<0||bird.y>239)
{
return 0;
}
if(bird.y>=0&&bird.y<=pillar.h1)
{
if(pillar.l-bird.x-8<=0&&pillar.l+60-bird.x>=0)
{
return 0;
}
}
if(bird.y+8>=pillar.h2&&bird.y+8<=239)
{
if(pillar.l-bird.x-8<=0&&pillar.l+60-bird.x>=0)
{
return 0;
}
}
return 1;
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_RNG, ENABLE);
RNG_Cmd(ENABLE);
lcd_init();
lcd_fill(0,0,250,250,WHITE);
bird.s=0;
bird.h=0;
creatbird();
creatpillar2();
lcd_fill(pillar2.l,0,pillar2.l+60,pillar2.h1,WHITE);
lcd_fill(pillar2.l,pillar2.h2,pillar2.l+60,239,WHITE);
creatpillar1();
while(judge(pillar2)&&judge(pillar1))
{
movebird();
movepillar();
}
lcd_show_string(50, 120, 32, "GAME OVER");
if(bird.s==0)
{
lcd_show_string(50, 152, 32, "SCORE=%d",0);
}
else
{
lcd_show_string(50, 152, 32, "SCORE=%d",bird.s-1);
}
while(1)
{
}
}
实验结果
