【嵌入式】探索嵌入式世界:在ARM上构建俄罗斯方块游戏的奇妙之旅

前言:

随着科技的不断进步,嵌入式系统已经渗透到我们生活的方方面面,从家用电器到工业自动化,无处不在。在众多嵌入式应用中,游戏作为一种娱乐形式,不仅能够丰富人们的业余生活,还能有效锻炼逻辑思维和反应能力。本文将详细介绍一款基于ARM开发板GEC6818和嵌入式Linux操作系统开发的俄罗斯方块游戏。这款游戏以其经典的玩法、简洁的界面设计和流畅的运行性能,为用户带来了既富有挑战性又充满乐趣的游戏体验。文章将从设计思路、功能描述、程序流程、各模块实现等方面,全面解析这款游戏的制作过程和关键技术。

giteehttps://gitee.com/q-haodong/test_-arm/tree/master/20240619_test_tetris2
效果演示https://live.csdn.net/v/405152?spm=1001.2014.3001.5501

基于嵌入式Linux俄罗斯方块

1. 简介

随着嵌入式技术的快速发展,嵌入式系统在各个领域的应用日益广泛。本项目以ARM开发板GEC6818为平台,基于嵌入式Linux操作系统,实现了一款具有基本功能的俄罗斯方块游戏。游戏设计遵循模块化思想,将系统分解为图形显示、触摸事件处理、游戏控制、界面显示、链表管理、移动逻辑以及主控等多个模块,以提高代码的可维护性和扩展性。通过C语言编程,利用多线程技术,实现了方块的移动、变形、随机生成、触屏控制、暂停恢复、嵌套消行和计分等功能。游戏界面简洁直观,提供了分数和等级显示,确保玩家能够轻松跟踪游戏进度。在性能方面,游戏运行流畅,代码规范,附有详细注释和文档,便于理解和维护。此外,通过全面测试,确保了游戏的稳定性和可靠性。最终,本项目不仅锻炼了嵌入式系统开发能力,也提供了一个既具有挑战性又富有趣味性的游戏体验。

2. 总体设计思路及功能描述

2.1 设计思路

本俄罗斯方块游戏的设计采用模块化的编程思想,将游戏分解为多个功能模块,每个模块负责不同的任务。主要模块包括图形显示模块、触摸事件处理模块、游戏控制模块、界面显示模块、链表管理模块、移动逻辑模块以及主控模块。程序使用C语言编写,运行在ARM平台上,利用多线程技术来提高游戏的响应速度和性能。

2.2 功能描述

  1. 图形显示模块:负责加载和显示BMP图片到屏幕上,支持指定区域的图片显示,用于游戏方块和背景的绘制。
  2. 触摸事件处理模块:监听触摸屏事件,将用户的触摸操作转换为游戏内的控制指令。
  3. 游戏控制模块:包含游戏的暂停和重启功能,允许玩家在任何时候暂停游戏,并在适当的时候恢复或重新开始。
  4. 界面显示模块:管理游戏的开始界面和结束界面,提供用户交互的界面元素。
  5. 链表管理模块:使用链表数据结构管理游戏中的方块布局,实现方块的动态添加和删除。
  6. 移动逻辑模块:控制方块的移动、变形和消行等逻辑,确保游戏规则的准确执行。
  7. 主控模块:作为程序的入口,初始化游戏环境,创建和管理线程,控制游戏的主循环。

2.3 程序流程图

在这里插入图片描述
程序流程从初始化游戏环境开始,显示欢迎界面,然后进入一个循环等待用户的触摸操作。一旦检测到触摸事件,程序将处理这些输入并更新游戏状态。随后,程序检查游戏是否结束,如果是,则显示游戏结束界面,并等待用户决定是否重启游戏或退出。如果用户选择重启,程序将重新初始化游戏环境;如果选择退出,则程序将结束运行。

3. 各部分程序功能及详细说明

3.1 游戏界面函数

3.1.1 游戏界面中的图片显示

代码中使用BMP文件格式来显示图像资源,这些图像用于游戏的图形界面,如方块、背景、按钮等元素。显示BMP图像的功能主要通过bmp_show.h头文件中声明的函数来实现。以下是与BMP显示相关的代码片段和解释:

  1. BMP显示函数声明 : 在bmp_show.h中,声明了两个函数bmp_show_mix和bmp_show_self,用于显示BMP图像:
int bmp_show_mix(int x0, int y0, int width, int height, char *name);
int bmp_show_self(int x0, int y0, int width, int height, char *name);
  1. BMP文件打开与读取 : 在bmp_show.c中,bmp_show_mix函数首先打开BMP文件,并读取文件状态,然后读取BMP图像数据:
int fd_bmp = open(name, O_RDONLY);
struct stat pst;
fstat(fd_bmp, &pst);
char *buf = (char *)malloc(pst.st_size);
lseek(fd_bmp, 54, SEEK_SET); // 跳过BMP文件头
read(fd_bmp, buf, pst.st_size - 54); // 读取BMP像素数据
  1. 内存映射Framebuffer : 使用mmap函数将显示设备的帧缓冲区(Framebuffer)映射到用户空间,以便于直接操作显示内存:
char *p = (char *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd_lcd, 0);
  1. 图像数据复制 : 将读取的BMP图像数据复制到Framebuffer的指定位置:
for (j = 0; j < height; j++) {
   
    for (i = 0; i < width; i++) {
   
        memcpy(p + lcd_offset, buf + bmp_offset, 3); // 从BMP缓冲区复制到Framebuffer
    }
}
  1. 显示特定区域的BMP图像 : bmp_show_mix函数允许指定显示图像的起始位置(x0, y0)和大小(width, height),这可以用于在界面上显示图像的特定部分:
bmp_show_mix(x0, y0, width, height, name);
  1. 显示整个BMP图像 :bmp_show_self函数用于显示整个BMP图像,通常用于显示背景或全屏图像:
bmp_show_self(x0, y0, width, height, name);
  1. 释放资源 : 在图像显示完成后,需要释放分配的内存并关闭内存映射和文件描述符:
munmap(p, 800 * 480 * 4); // 关闭内存映射
close(fd_lcd); // 关闭Framebuffer文件描述符
close(fd_bmp); // 关闭BMP文件描述符
free(buf); // 释放分配的内存

3.1.2 游戏开始界面

  1. 效果
    在这里插入图片描述

  2. 功能: 展示游戏的初始界面,通常包含游戏的标题、开始游戏的按钮等元素。

  3. 实现: 使用bmp_show_mix函数加载和显示欢迎屏幕的背景图片

  4. 代码

void show_interface_welcome()
{
   
    bmp_show_mix(0, 0, 800, 480, "./tetris_pic/welcom_bk1.bmp"); // 显示欢迎界面背景

    int x, y, event_type;
    int button_down = 0; // 用于记录按钮是否被按下

    while (1)
    {
   
        if (capture_touch_events(&x, &y, &event_type) == -1)
        {
   
            // 触摸事件捕获失败,可能需要处理错误或退出
            break;
        }

        if (event_type == 1)
        {
    // 触摸按下事件
            if (x > 440 && x < 620 && y > 360 && y < 460)
            {
   
                // 用户按下了按钮区域
                bmp_show_self(BUTTON_X, BUTTON_Y, BUTTON_W, BUTTON_H, "./tetris_pic/welcom_bk1_push.bmp"); // 显示按钮按下的图片
                button_down = 1;
            }
            printf("Touch down at (%d, %d)\n", y, y);
        }
        else if (event_type == 0 && button_down)
        {
      
            // 触摸离开事件且按钮之前被按下
            bmp_show_self(BUTTON_X, BUTTON_Y, BUTTON_W, BUTTON_H, "./tetris_pic/welcom_bk1.bmp"); // 恢复按钮正常状态
            button_down = 0;
            printf("Touch up at (%d, %d)\n", y, y);

            if (x > 440 && x < 620 && y > 360 && y < 460)
            {
   
                break; // 离开循环,进入游戏
            }
        }
    }
}

3.1.3 游戏主界面

  1. 效果
    在这里插入图片描述

  2. 功能: 展示游戏进行中的界面,包括方块下落区域、下一个方块的预览区、分数和等级显示等。

  3. 实现: 在主循环中持续更新界面,显示当前活动方块、分数和等级。

  4. 代码

int main(int argc, char *argv[])
{
   

    show_interface_welcome();

    struct ls_all *head;

    // 显示背景图片
    bmp_show_mix(0, 0, 800, 480, "./tetris_pic/bck.bmp");

    int rt;

    pthread_t idt, idr;

    // 获取两种随机形状并初始化,得到初始化结构体
    srand((unsigned int)time(NULL));
    shp = ((unsigned int)rand()) % 7 + 1;
    shp_next = ((unsigned int)rand()) % 7 + 1;

    bk = bk_init(shp);
    bk_next = bk_init(shp_next);

    // 初始化掉落方块结构体
    head = ls_init();

    // 初始化分数和速度
    score = 0;
    speed = 0;

    // 显示移动方块 及 提示方块
    the_show(bk);
    the_show_next(bk_next);
    score_show(0); // 显示成绩

    // 创建控制方块移动线程
    pthread_create(&idt, NULL, auto_down, (void *)head);

    // 时间更新线程,时间到且无操作自动更新dir为下落状态
    pthread_create(&idr, NULL, time_out, NULL);

    while (1)
    {
   

        // 锁定互斥锁以安全地读取 dir
        pthread_mutex_lock(&dir_mutex);
        int current_dir = dir; // 假设这是在循环中读取 dir 变量的地方
        pthread_mutex_unlock(&dir_mutex);

        if (paused == 1 || gameover == 1 || current_dir == -2)
        {
   
            usleep(100);
            continue;
        }

        if (current_dir == -1)
        {
    // 变形
            change_type(bk);
            the_show_bck_type(bk);
        }
        else
        {
    // 移动
            change_dir(bk->p, current_dir);
            the_show_bck_dir(bk->p, current_dir);
        }

        // 移动检查是否越界及掉落到底部
        bk = move_check(head, current_dir);
        if (bk == NULL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q_hd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值