硬件配置: | 树莓派PCIO单片机 |
屏幕: | 240*240 SPI接口的显示屏 |
#define ELSFK_HIGHT 23
#define ELSFK_WIDTH 16
static uint8_t datas[ELSFK_HIGHT][ELSFK_WIDTH]={0};
主要数据结构就是一个二维数组datas
它是记录在屏幕的什么地方显示小方块的(一个小方块的像素点为10*10)。
下面我们详细讲一下他是怎么记录小方块的坐标的。
啥,坐标?
对,就是一个小方块在屏幕中显示的位置,
简单点讲就是用(x,y)来表示的。
我们规定屏幕的坐标是这样的,如图:
我们要在坐标(0,0)处显示一个点,就在对应的数组中置1就可以
比如:datas[0][0] = 1;就表示在屏幕的(0,0)处显示了一个方块
有了坐标,就知道了在哪显示方块了。
注意:最后一个点是datas[22][15] = 1;
[22]在前,[15]在后,大家要小心,
也就是先写第几行,再写第几列,
数组的下标都是从零开始的,
#define ELSFK_HIGHT 23
#define ELSFK_WIDTH 16
void draw(){
for(uint8_t i = 0; i < ELSFK_HIGHT; i++){||行
diamond_region.tLocation.iY = 10 + i * 10;
for(uint8_t j = 0; j < ELSFK_WIDTH; j++){||列
if(datas[i][j]){||有方块就显示
diamond_region.tLocation.iX = 10 + 10 * j;
||绘制一个小方块
arm_2d_rgb16_fill_colour(ptTile, &diamond_region, GLCD_COLOR_BLACK);
}
}
}
}
注意diamond_region.tLocation.iY = 10 + i * 10;这里是计算屏幕显示的绝对位置,我们一个小方块是10个像素点。
接着介绍我们的数据结构,接下来还是一个数组。
static uint16_t block[7][4] = {
{0x0e40,0x2620,0x0270,0x0464,},//T
{0x0446,0x0e80,0x6220,0x0170,},//L
{0x0226,0x08e0,0x6440,0x0710,},//J
{0x0c60,0x2640,0x0630,0x0264,},//Z
{0x06c0,0x4620,0x0360,0x0462,},//S
{0x0660,0x0660,0x0660,0x0660,},//O
{0x4444,0x0f00,0x2222,0x00f0,},//I
};
这个数组定义了俄罗斯方块的形状
一共分成 7 形状,有的形状有 4种状态,
不管是多少种状态,一个方块需要2字节来存储,也就是16bit来保存一个方块的信息。
如图:
十进制 | 十六进制 | 二进制 |
0 | 0 | 0000 |
1 | 1 | 0001 |
2 | 2 | 0010 |
3 | 3 | 0011 |
4 | 4 | 0100 |
5 | 5 | 0101 |
6 | 6 | 0110 |
7 | 7 | 0111 |
8 | 8 | 1000 |
9 | 9 | 1001 |
10 | a | 1010 |
11 | b | 1011 |
12 | c | 1100 |
13 | d | 1101 |
14 | e | 1110 |
15 | f | 1111 |
有了这个表就方便转换了,举个例子:
这就是第一个 T 字图形转换成十六进制为 0x0e40的过程,简单吧。
俄罗斯方块之函数
run(){
while(1){
||绘制方块函数
draw();
||获取按键值
key = elsfk_menu_get_key();
||生成一个新的方块图形
new_diamond();
||图形下降
diamond_down(key){
||按下左键左移
diamond_down_left();
||按下右键右移
diamond_down_right();
||按下上键改变形状
diamond_down_up();
||没有按键按下就下降
diamond_down_down();
}
||消层函数
elimination_layer();
||延时函数
elsfk_delay(1000);
}
}
大家移植的时候需要自己实现绘制方块函数draw();
和elsfk_menu_get_key();获取按键值函数
获取项目源码可以在公众号回复“游戏”。
现在我们就开始随机生成一个形状,并准备下落。
void elsfk_init(){
||随机生成一个数
diamond_next = (rand_() + rand())%28;
flag.new_diamond_flag = 0;
}
diamond_next这个变量保存下一个形状的下标,因为只有28种形状,所以要对28取模,保证下标不越界。
flag.new_diamond_flag这个变量为0,表示生成一个新的形状,为1表示形状正在下落,不需要生成新的形状了。
||绘制一个新形状
void new_diamond(){
uint16_t diamond ;
uint16_t wei;
if(0 == flag.new_diamond_flag){
||绘制完成,标记置为1
flag.new_diamond_flag = 1;
diamond_current = diamond_next;
||从block数组中取形状
diamond = block[diamond_current/4][diamond_current%4];
||在屏幕中间显示
diamond_X = 6;
||绘制4 行4列的形状图形
for(uint8_t i = 0; i < 4; i++){
for(uint8_t j = 0; j < 4; j++){
wei = (3-i)*4 + 3-j;
||置1 为显示小方块
datas[i][j + diamond_X] = (diamond>>wei) & 0x01;
}
}
||为下降函数准备的数据
for(uint8_t i = 0; i < 4; i++){
diamond_down_line[i] = 0;
for(uint8_t j = 0; j < 4; j++){
if(datas[j][i + diamond_X]){
diamond_down_line[i] = j + 1;
}
}
}
donw_line = diamond_down_line[0] ;
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line[i] > donw_line){
donw_line = diamond_down_line[i] ;
}
}
diamond_Y = donw_line;
//====right======
for(uint8_t i = 0; i < 4; i++){
diamond_down_line_right[i] = 0;
for(uint8_t j = 0; j < 4; j++){
if(datas[i][j+ diamond_X]){
diamond_down_line_right[i] = j + 1;
}
}
}
diamond_right = diamond_down_line_right[0];
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line_right[i] > diamond_right){
diamond_right = diamond_down_line_right[i] ;
}
}
//====lift======
for(uint8_t i = 0; i < 4; i++){
diamond_down_line_lift[i] = 0;
for(uint8_t j = 0; j < 4; j++){
if(datas[i][j+ diamond_X]){
diamond_down_line_lift[i] = j + 1;
break;
}
}
}
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line_lift[i]){
diamond_lift = diamond_down_line_lift[i];
break;
}
}
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line_lift[i] < diamond_lift){
if(diamond_down_line_lift[i]){
diamond_lift = diamond_down_line_lift[i] ;
}
}
}
}
||准备下一个形状数据
diamond_next = (rand_() + rand())%28;
}
donw_line表示下降到第几行,下降时要用到。
diamond_X表示在第几列,左右移动的时候要用到。
37~74行是处理左右移动用到的变量,原理和下降的相同。
21~36行是处理下降要用到的变量,原理如下图所示
void diamond_down(uint8_t key){
switch( key ){
case DIRECTION_LEFT:
||按下左键左移
diamond_down_left();
break;
case DIRECTION_RIGHT:
||按下右键右移
diamond_down_right();
break;
case DIRECTION_KEY_Y:
case DIRECTION_UP:
||按下上或Y键改变形状
diamond_down_up();
break;
case DIRECTION_KEY_A:
||按下A键清屏,重新玩
for(uint8_t i = 0; i < ELSFK_HIGHT; i++){
for(uint8_t j = 0; j < ELSFK_WIDTH; j++){
datas[i][j] = 0;
}
}
flag.new_diamond_flag = 0;
new_diamond();
break;
default:
||没有按键按下就下降
diamond_down_down();
break;
}
}
void diamond_down_down(){
uint8_t i,j,stop_flag = 0;
||超出行数,不能下降,直接结束
if(donw_line > ELSFK_HIGHT){return;}
for(i = 0; i < 4; i++){
||判断能否下降
if(diamond_down_line[i]){
if(datas[donw_line-(diamond_Y - diamond_down_line[i])][diamond_X+i]){
||下边有方块,则停止下降
stop_flag = 1;
}
}
}
||方块下降
if(0 == stop_flag){
for(i = 0; i < diamond_Y; i++){
for(j = 0; j < 4; j++){
if(diamond_down_line[j]){
if(datas[donw_line - i - 1][diamond_X+j]){
datas[donw_line - i][diamond_X+j] = datas[donw_line - i - 1][diamond_X+j];
datas[donw_line - i - 1][diamond_X+j] = 0;
}
}
}
}
donw_line++;
||下降到底停止
if(donw_line >= ELSFK_HIGHT){
stop_flag = 1;
}
}
||如果停止下降,重新生成方块形状
if(stop_flag == 1){
flag.new_diamond_flag = 0;
}
}
stop_flag = 1;代表方块已将到底或者下面有小方块不能在下降了。
flag.new_diamond_flag = 0;代表可以重新生成方块形状。
下降检测原理如下图
方块左移函数:
void diamond_down_left(){
int8_t i,j,x,y,stop_flag = 0;
for(i = 0; i < 4; i++ ){
if(diamond_down_line_lift[i]){
x = diamond_X + diamond_down_line_lift[i]-2;
if(x < 0){
stop_flag = 1;
return;
}
else if(datas[donw_line-diamond_Y+i][x]){
stop_flag = 1;
}
}
}
if(0 == stop_flag){
for(i = 0; i < diamond_Y; i++ ){
if(diamond_down_line_lift[i]){
for(j = diamond_down_line_lift[i]-1; j < diamond_down_line_right[i]; j++){
y = donw_line-diamond_Y+i;
x = diamond_X+j;
datas[y][x-1] = datas[y][x];
datas[y][x] = 0;
}
}
}
diamond_X--;
}
}
左移函数的原理和下降一样,如果左边有方块就停止左移,左边没有方块且没到最左边就左移。
方块右移函数:
void diamond_down_right(){
uint8_t i,j,x,y,stop_flag = 0;
if((diamond_X + diamond_right) >= (ELSFK_WIDTH)){
return;
}
for(i = 0; i < 4; i++){
if(diamond_down_line_right[i]){
if(datas[donw_line-diamond_Y+i][diamond_X + diamond_down_line_right[i]]){
stop_flag = 1;
}
}
}
if(0 == stop_flag){
for(i = 0; i < diamond_Y ; i++){
if(diamond_down_line_lift[i]){
for(j = diamond_down_line_lift[i]-1; j < diamond_down_line_right[i]; j++){
y = donw_line-diamond_Y+i;
x = diamond_X+diamond_down_line_right[i]-j + diamond_down_line_lift[i]-2;
if( datas[y][x]){
datas[y][x+1] = datas[y][x];
datas[y][x] = 0;
}
}
}
}
diamond_X++;
if((diamond_X + diamond_right) >= ELSFK_WIDTH){
stop_flag = 1;
}
}
}
原理和左移一样
方块变换函数:
void diamond_down_up(){
uint16_t wei,diamond;
uint8_t x,y,stop_flag = 0;
uint8_t diamond_Y_temp;
y = diamond_current/4;
x = diamond_current%4;
diamond = block[y][x];
for(uint8_t i = 0; i < 4; i++){
for(uint8_t j = 0; j < 4; j++){
wei = (3-i)*4 + 3-j;
if(datas[donw_line - diamond_Y + i][j + diamond_X] &&(!((diamond>>wei) & 0x01))){
stop_flag = 1;
}
}
}
if((diamond_X < 0) ||(diamond_X > (ELSFK_WIDTH - 4))){
stop_flag = 1;
}
if(stop_flag == 0){
x++;
x = x%4;
diamond_current = y * 4 + x;
diamond = block[y][x];
for(uint8_t i = 0; i < 4; i++){
for(uint8_t j = 0; j < 4; j++){
wei = (3-i)*4 + 3-j;
datas[donw_line - diamond_Y + i][j + diamond_X] = (diamond>>wei) & 0x01;
}
}
//===================
||变化完重新初始化移动需要的变量
for(uint8_t i = 0; i < 4; i++){
diamond_down_line[i] = 0;
for(uint8_t j = 0; j < 4; j++){
if(datas[donw_line - diamond_Y + j][i + diamond_X]){
diamond_down_line[i] = j + 1;
}
}
}
diamond_Y_temp = diamond_down_line[0] ;
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line[i] > diamond_Y_temp){
diamond_Y_temp = diamond_down_line[i] ;
}
}
donw_line = donw_line - (diamond_Y - diamond_Y_temp);
diamond_Y = diamond_Y_temp;
//====right======
for(uint8_t i = 0; i < 4; i++){
diamond_down_line_right[i] = 0;
for(uint8_t j = 0; j < 4; j++){
if(datas[donw_line - diamond_Y + i][j+ diamond_X]){
diamond_down_line_right[i] = j + 1;
}
}
}
diamond_right = diamond_down_line_right[0];
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line_right[i] > diamond_right){
diamond_right = diamond_down_line_right[i] ;
}
}
//====lift======
for(uint8_t i = 0; i < 4; i++){
diamond_down_line_lift[i] = 0;
for(uint8_t j = 0; j < 4; j++){
if(datas[donw_line - diamond_Y + i][j+ diamond_X]){
diamond_down_line_lift[i] = j + 1;
break;
}
}
}
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line_lift[i]){
diamond_lift = diamond_down_line_lift[i];
break;
}
}
for(uint8_t i = 1; i < 4; i++){
if(diamond_down_line_lift[i] < diamond_lift){
if(diamond_down_line_lift[i]){
diamond_lift = diamond_down_line_lift[i] ;
}
}
}
}
}
31行之后是处理变化完重新初始化移动需要的变量
方块变换原理如下图
小哥搜集了一些嵌入式学习资料,公众号内回复【1024】即可找到下载链接!
推荐好文 点击蓝色字体即可跳转
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)
![](https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211229_b61f4dae-6891-11ec-bc1d-fa163eb4f6be.png)