Hi3861 Tetris Mini Game (with source code)

I believe many people have already watched the video !

1. Principle

I believe everyone has played Tetris. First, divide the scene into a movable part and a fixed part;

unsigned short data_blk[16]; //The fixed part of the game 
unsigned short data_act[4]; //The mobile part of the game 
unsigned char display_blk_data[53] = {0x40,0xff,0x55}; //The game scene part is used to display 
unsigned char display_nst_data [17] = {0x40}; //The game shows the next block that will be played 
unsigned char data_nst; //The content of the next block 
unsigned int score = 0; //Score 
unsigned int delay = 100000; //Decline delay control Speed 
char row_act = -1; //Number of rows where the active square is located 
hi_i2c_data display_blk; //Used to display 
hi_i2c_data display_nst; //Used to display

The size of the fixed scene part is 16x12, represented by 16 unsigned short (16 bits), and only the lower 12 bits are used;

The size of the movable part is 4x12, which is represented by 4 unsigned short (16 bits), and only the lower 12 bits are used;

All blocks (19 types) are predefined as block[19][4], and the next preview will use an unsigned char type (0-18) to represent one of the 19;

Use row_act (the row number of the active square) to control the movement of the active square.

Two, display

void display(void)
{
    //show the canvas
    unsigned short temp;
    for(unsigned char i=0;i<8;++i)
    {
        for(unsigned char j=0;j<12;++j)
        {
            for(unsigned char k=0;k<4;++k)
            {
                display_blk_data[3+j*4+k] = 0x00;
                temp = i*2>=row_act && i*2<row_act+4 ? data_blk[i*2]|data_act[i*2-row_act] : data_blk[i*2];
                display_blk_data[3+j*4+k] |= temp&1<<j ? img[k] : 0x00;
                temp = i*2+1>=row_act && i*2<row_act+3 ? data_blk[i*2+1]|data_act[i*2+1-row_act] : data_blk[i*2+1];
                display_blk_data[3+j*4+k] |= temp&1<<j ? img[k]<<4 : 0x00;
            }
        }
        oled_write_data(0, i, &display_blk);
    }
    //show the nest block
    for(unsigned char i=0;i<2;++i)
    {
        for(unsigned char j=0;j<4;++j)
        {
            for(unsigned char k=0;k<4;++k)
            {
                display_nst_data[j*4+k+1] = 0;
                display_nst_data[j*4+k+1] |= block[data_nst][i*2]&0x10<<j ? img[k] : 0x00;
                display_nst_data[j*4+k+1] |= block[data_nst][i*2+1]&0x10<<j ? img[k]<<4 : 0x00;
            }
        }
        oled_write_data(64, i+1, &display_nst);
    }
    //show the score
    oled_write_num(64, 7, score, 0);
}

The display function consists of three parts: game scene, next block preview, and score;

Focus on the game scene part:

The outermost i loops a total of 8 times, each time displaying two of the 16 lines;

The second layer j loops a total of 12 times, each time processing one pixel in a row;

The third layer of k loop converts the first game pixel into 4x4 pixels for display

 temp = i*2>=row_act && i*2<row_act+4 ? data_blk[i*2]|data_act[i*2-row_act] : data_blk[i*2];

temp = number of rows encountered movable part? background + foreground: background;

display_blk_data[3+j*4+k] |= temp&1<<j ? img[k] : 0x00;

Pixel data for display|= dominant pixel? One column in img: not displayed;

The next part of the trailer is similar to the above, I believe it can be understood by analogy;

Let me briefly introduce the part that displays the score "void oled_write_num(hi_u8 x, hi_u8 y, unsigned int n, hi_bool zero)"

xy is the coordinate of the value to be displayed, n is the value to be displayed, whether zero displays the preceding 0;

void oled_write_num(hi_u8 x, hi_u8 y, unsigned int n, hi_bool zero)
{
    unsigned int number = n;
    unsigned char str_num[9];
    for(unsigned char i=0;i<8;++i)
    {
        str_num[7-i] = num[number%10];
        number /= 10;
    }
    str_num[8] = 0;
    if(zero)
    {
        oled_write_string_57(x, y, (hi_u8 *)str_num);
    }
    else
    {
        hi_u8 *p = str_num;
        for(;*p=='0';++p);
        oled_write_string_57(x, y, p);
    }    
}

这部分比较简单相信大家都能理解,把int型按位转换成字符串显示,

如果去除前面的0直接将字符串的起始地址向后移动,直到有非0数字。

如果想仔细研究显示原理请下载附件显示驱动芯片数据手册

 

三、方块移动

void block_left(void)
{
    //限制移动代码
    //move to right on screen left
    for(unsigned char i=0;i<4;++i)
    {
        data_act[i]>>=1;
    }
}

直接把活动方块进行移动操作即可,左右原理一样;

就这么简单? 当然不是!

在移动前还要加一些限制:到边界了不能再移动、有固定方块阻挡不能移动

下面就是限制移动代码,如果触发限制移动条件,直接返回,不进行移动操作

    //if close to edge give up move
    for(unsigned char i=0;i<4;++i)
    {
        if(data_act[i]&0x0001)
        {
            return;
        }
        if((data_act[i]>>1) & data_blk[row_act+i])
        {
            return;
        }
    }

这个最烧脑的就是方块的旋转了,发视频前就差旋转函数没有写了,直到昨天才调到合适,

先看一下基础代码:

static void block_turn(char* arg)
{
    (void)arg;
    unsigned short turned[4]={0, 0, 0, 0};
    unsigned char i;
    for(i=0;i<12;++i)
    {
        if(data_act[0]&1<<i || data_act[1]&1<<i || data_act[2]&1<<i || data_act[3]&1<<i)
        {
            break;
        }
    }
    for(unsigned char j=0;j<4;++j)
    {
        for(unsigned char k=0;k<4;++k)
        {
            turned[3-j] |= data_act[k]&1<<(i+j) ? 1<<(i+k) : 0;
        }
    }
    for(unsigned char j=0;j<4;++j)
    {
        data_act[j] = turned[j];
    }
}

首先是声明一个"turned[4]"用于存放旋转后的方块,为什么不直接在原图旋转呢?

第一个循环从低到高到位扫描找到方块所在列,

第二个循环从找到方块的列取4X4进行行列转置,

第三个循环把旋转后的方块更新到当前活动方块。

重点:前面讲了这是一个基础代码,功能实现了,但有一个问题不得不考虑:旋转后干涉吗?干涉怎么办?

解析:除了上面不会干涉,下左右都可能因为旋转干涉,干涉我就不转了呗。

如图旋转会造成方块下移:

    for(unsigned char j=0;turned[0]==0&&j<2;++j)
    {
        turned[0] = turned[1];
        turned[1] = turned[2];
        turned[2] = turned[3];
        turned[3] = 0;
    }

如果己经在边上了,可能会造成出界:

    for(;turned[0]&1<<12 || turned[1]&1<<12 || turned[2]&1<<12 || turned[3]&1<<12;)
    {
        for(unsigned char j=0;j<4;++j)
        {
            turned[j] >>= 1;
        }   
    }

因为是左对齐的,所以左边不会存在这个情况,且只有右边有富裕空间刚好利用一下。

最近再检测一下是否与固定方块干涉:

    for(unsigned j=0;j<4;++j)
    {
        if(turned[j] & data_blk[row_act+j])
        {
            return;
        }
    }

以上条件都满足了,才能执行最后的更新到当前活动方块,否则放弃旋转。

这也是为什么要事先声明一个“turned[4]“,如果在原图旋转万一干涉了还要转回去!

四、按键的实现(重点)

文章后续内容和附件可以点击下面的原文链接前往学习
原文链接:https://harmonyos.51cto.com/posts/1995#bkwz

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#bkwz





Guess you like

Origin blog.51cto.com/14901125/2561282