Hi3861之俄罗斯方块小游戏(附源码)

Stella981
• 阅读 520

目录:

一、原理
二、显示
三、方块移动
四、按键的实现
五、自然下降

视频相信不少人已经看过了!

一、原理

俄罗斯方块相信大家都玩过,首先把场景分成可移动部分、和固定部分;

Hi3861之俄罗斯方块小游戏(附源码)

unsigned short data_blk[16];  //游戏固定部分
unsigned short data_act[4];   //游戏移动部分
unsigned char display_blk_data[53] = {0x40,0xff,0x55};  //游戏场景部分用于显示
unsigned char display_nst_data[17] = {0x40};  //游戏显示将出场的下一个方块
unsigned char data_nst;   //下一个方块的内容
unsigned int score = 0;   //得分
unsigned int delay = 100000;   //下降延时控制速度
char row_act = -1;   //活动方块所在行数
hi_i2c_data display_blk;  //用于显示
hi_i2c_data display_nst;  //用于显示

固定场景部分大小为16x12, 用16个无符号short(16位)型表示,仅用到低12位;

可移动部分大小为4x12, 用4个无符号short(16位)型表示,仅用到低12位;

所有的方块(19种)有预定义为block[19][4],下一个预告用一个无符号char型(0-18)表示19个其中的一个;

通过row_act(活动方块所在行数)控制活动方块向下移动。

二、显示

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);
}

显示函数由三部分组成:游戏场景、下一块预告、分数;

重点介绍一下游戏场景部分:

最外层i循环共8次,每次显示16行中的两行;

第二层j循环共12次,每次处理一行中的一个像素;

第三层k循环把第个游戏像素换算成用于显示的4x4个像素

 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 = 行数遇到可移动部分 ? 背景+前景 : 背景;

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

用于显示的像素数据 |= 显性像素? img中的一列 : 不显示;

下一块预告部分与上面类似,相信能举一反三的理解一下;

再简单介绍一下显示分数的部分“void oled_write_num(hi_u8 x, hi_u8 y, unsigned int n, hi_bool zero)"

x y 是要显示的数值所在的坐标,n是要显示的数值,zero是否显示前面的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进行行列转置,

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

Hi3861之俄罗斯方块小游戏(附源码)

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

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

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

    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;
    }

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

Hi3861之俄罗斯方块小游戏(附源码)

    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]“,如果在原图旋转万一干涉了还要转回去!

解锁更多章节>>>

作者:Hallym6

想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com


【有奖直播——HarmonyOS驱动框架调试总结 火热报名中!】

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这