首页 > 其他分享 >在ARM开发板上实现2048小游戏

在ARM开发板上实现2048小游戏

时间:2024-09-20 10:55:00浏览次数:16  
标签:ev int bmp 2048 lcd 小游戏 fd include ARM

 event.h

屏幕点击事件.h文件:获取屏幕的xy坐标,获取手指滑动的方向,获取点击事件。

#ifndef __EVENT_H_
#define __EVENT_H_


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <linux/input.h>

#define BIN_TOUCH 0x14a

#define up 1
#define down 2
#define left 3
#define right 4



void get_event_xy(int *x,int *y);
int get_event_dirention();
void photo();
int get_event_button();

#endif

event.c 

屏幕点击事件.c文件:实现屏幕的xy坐标,获取手指滑动的方向,获取点击事件函数。

#include "event.h"
#include "lcd.h"
void get_event_xy(int *x,int *y)//获取像素的坐标
{
    int fd_ev = open("/dev/input/event0",O_RDONLY);//打开屏幕()
    if(fd_ev ==-1)
    {
        perror("open error");
        exit(1);
    }
    printf("open duccess\n");

    struct input_event ev;
    while(1)
    {
        int ret =read(fd_ev,&ev,sizeof(ev));
        if(ret ==-1)
        {
            perror("read ev error");
            exit(1);
        }

        if(ev.type == EV_ABS && ev.code ==ABS_X)
        {
            *x = ev.value;
        }
        if(ev.type == EV_ABS && ev.code ==ABS_Y)
        {
            *y = ev.value;
        }
        if(ev.type == EV_KEY &&ev.code ==BIN_TOUCH && ev.value == 0)
        {
            break;
        }

    }
    close(fd_ev);
}

int get_event_dirention()
{//按键方向的判断
    int fd_ev = open("/dev/input/event0",O_RDONLY);
    if(fd_ev ==-1)
    {
        perror("open error");
        exit(1);
    }
    printf("open duccess\n");

    struct input_event ev;
    int x0 =-1,y0=-1;
    int x1=-1,y1 =-1;

    while(1)
    {
        int ret =read(fd_ev,&ev,sizeof(ev));
        if(ret ==-1)
        {
            perror("read ev error");
            exit(1);
        }

        if(ev.type == EV_ABS && ev.code ==ABS_X)
        {
            if(x0==-1)
            {
                x0=ev.value;
            }else{
                x1=ev.value;
            }
        }
        if(ev.type == EV_ABS && ev.code ==ABS_Y)
        {
            if(y0==-1)
            {
                y0=ev.value;
            }else{
                y1=ev.value;
            }
        }
        if(ev.type == EV_KEY &&ev.code ==BIN_TOUCH && ev.value == 0)
        {
            int x_d =x1-x0;
            int y_d=y1-y0;
            printf("x0=%d,y0=%d,x1=%d,y1=%d\n",x0,x1,y0,y1);
            printf("x_d=%d,y_d=%d\n",x_d,y_d);
            if(abs(x_d)>abs(y_d))
            {
                if(x_d>100)
                {
                    close(fd_ev);
                    return right;
                }else if(x_d<-100)
                {
                    close(fd_ev);
                    return left;
                }
            }else{
                if(y_d>100)
                {
                    close(fd_ev);
                    return down;
                }else if(y_d<-100)
                {
                    close(fd_ev);
                    return up;
                }
            }
        }

    }
    close(fd_ev);
}

int get_event_button()
{//对按键的监听
    int x2 = -1, y2 = -1;
    get_event_xy(&x2, &y2);
    // 转换成像素点的坐标
    int x0 = x2 /1.28;
    int y0 = y2 /1.25;
    int w=150 ,h=80;
    //符合区间就返回那个值
    if((x0 > 480 && x0 < 480 + w) && (y0 > 250 && y0 < h + 250)) // 重启
    {
        return 1;
    }
    else if ((x0 > 480 && x0 < 480 + w) && (y0 > 340 && y0 < h + 340)) // 退出
    {
        return 2;
    }
    else
    {
        return -1;
    }
}

lcd.h

系统IO操作LCD,内存映射操作显示屏。

#ifndef __LCD_H_
#define __LCD_H_

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <sys/ioctl.h>

//BMP图片信息结构体
typedef struct bmp_info
{
    int size;       //图片的大小
    int width;      //图片的宽度
    int height;     //图片的高度
    short depth;    //图片的色深
    char* data;     //保存像素数组首地址
}BMP;


//系统IO操作显示屏
void open_lcd();
void close_lcd();
void write_lcd(unsigned int color);
void rgbrun();

//内存映射操作显示屏
void lcd_init();
void lcd_uninit();
void lcd_draw_point(int i, int j, unsigned int color);
void lcd_draw_background(unsigned int color);
void lcd_draw_word(int x0,int y0,int w, int h,unsigned int color,char *lcd_draw_word);
void lcd_draw_bmp(int x, int y, char* bmpname);
BMP get_bmp_info(char *bmpname);



#endif

lcd.c

系统IO完成:打开LCD函数,关闭LCD函数,写入像素点颜色函数,刷屏函数。

#include "lcd.h"

int fd_lcd = -1; //显示屏的文件描述符
int* plcd = NULL; //内存映射区域的首地址
struct fb_var_screeninfo fbinfo; //屏幕属性信息结构体

/********************系统IO操作显示屏************************/
/**
 * @brief 打开LCD显示屏
 * 
 */
void open_lcd()
{
    fd_lcd = open("/dev/fb0", O_WRONLY);
    if(fd_lcd == -1)
    {
        perror("open error");
        exit(1);
    }
    printf("open /dev/fb0 success\n");
}

/**
 * @brief 关闭显示屏
 * 
 */
void close_lcd()
{
    close(fd_lcd);
}

/**
 * @brief 往屏幕文件中写入数据
 * 
 * @param color 需要显示的颜色
 */
void write_lcd(unsigned int color)
{
    int array[480][800] = {0}; //二维数组  对应显示屏上的像素点

    for(int i = 0; i < 480; i++) //行
    {
        for(int j = 0; j < 800; j++) //列
        {
            array[i][j] = color;
        }
    }

    //把颜色数据写入到显示屏文件中
    write(fd_lcd, array, 800*480*4);
}
/**
 * @brief 实现显示屏的刷屏
 * 
 */
void change_lcd()
{
    unsigned int color[3] = {0xFF0000, 0x00FF00, 0x0000FF};

    int i = 0;
    while(1)
    {
        write_lcd(color[i]);
        sleep(3); //延时3秒
        i++;

        if(i > 2)
            i = 0;

        //把光标偏移到文件开头
        lseek(fd_lcd, 0, SEEK_SET);
    }

}

屏幕的初始化:打开屏幕,内存映射。

/*********************内存映射操作显示屏********************/

/**
 * @brief 屏幕初始化:打开显示屏,内存映射
 * 
 */
void lcd_init()
{
    //可读可写方式打开显示屏
    fd_lcd = open("/dev/fb0", O_RDWR);
    if(fd_lcd == -1)
    {
        perror("open error");
        exit(1);
    }
    printf("open /dev/fb0 success\n");

    //使用ioctl获取屏幕的属性信息
    int ret = ioctl(fd_lcd, FBIOGET_VSCREENINFO, &fbinfo);
    if(ret == 0)
    {
        printf("LCD: %d, %d, %d\n", fbinfo.xres, fbinfo.yres, fbinfo.bits_per_pixel);
    }

    //进行内存映射
    plcd = mmap(
            NULL,               //映射区域的地址由系统自行分配
            //800*480*4,         //映射区域的大小
            fbinfo.xres * fbinfo.yres * fbinfo.bits_per_pixel / 8, //映射区域的大小
            PROT_READ | PROT_WRITE, //可读可写
            MAP_SHARED,         //共享映射,操作立马响应
            fd_lcd,             //文件描述符
            0                   //文件映射偏移量,为0,表示从文件开头进行映射
    );
    if(plcd == MAP_FAILED)
    {
        perror("mmap error");
        exit(1);
    }
    printf("mmap  success\n");
}

关闭屏幕,解除映射。 

/**
 * @brief 接触显示屏初始化: 解除映射,关闭显示屏
 * 
 */
void lcd_uninit()
{
    //解除内存映射
    munmap(plcd, 800*480*4);
    //关闭显示屏
    close(fd_lcd);
}
/**
 * @brief 画背景色(让整个屏幕显示某种颜色)
 * 
 * @param color 颜色
 */
void lcd_draw_background(unsigned int color)
{
    for(int i = 0; i < 480; i++) //行
    {
        for(int j = 0; j < 800; j++) //列
        {
            lcd_draw_point(i, j, color);
        }
    }
}
/**
 * @brief 显示一个字符
 * 
 * @param x0 
 * @param y0 第y行的第x个像素点为字符的最左上角的坐标
 * @param w 宽
 * @param h 高
 * @param color 颜色 
 * @param word 需要显示的字符的字模数据
 */
void lcd_draw_word(int x0, int y0, int w, int h, unsigned int color, char* word)
{
    int x, y; //表示需要进行显示的像素点的下标 第y行的第x个像素点

    for(int i = 0; i < w*h/8; i++) //对点阵数组进行遍历
    {
        for(int j = 0; j < 8; j++) //对点阵矩阵中的元素一个一个bit进行分析
        {
            //word[i]对应有8bit, 一个一个bit来分析,从左往右,高字节到子字节
            if(word[i] & (1<<(7-j)))
            {
                y = i/(w/8); //第几行  w/8一行有多少字节
                x = i % (w/8) * 8 + j;
                //i%(w/8) 当前元素在这一行前面有多少个字节的元素
                //i%(w/8)*8 当前元素在这一行前面有多少个像素点
                lcd_draw_point(y+y0, x+x0, color);
            }
        }
    }
}
/**
 * @brief Get the bmp info object
 * 
 * @param bmpname 需要进行属性信息获取的bmp图片路径
 * @return BMP 返回获取到的属性信息
 */
BMP get_bmp_info(char* bmpname)
{
    //打开图片
    int fd_bmp = open(bmpname, O_RDONLY);
    if(fd_bmp == -1)
    {
        perror("open bmp error");
        exit(1);
    }

    //判断该文件是否为bmp文件
    char bm[2] = {0};
    read(fd_bmp, bm, 2);
    if(!(bm[0] == 'B' && bm[1] == 'M'))
    {
        printf("%s is not BMP file\n", bmpname);
        exit(1);
    }

    //获取BMP图片数据
    BMP bmpinfo;

    //文件大小
    lseek(fd_bmp, 0x02, SEEK_SET);
    read(fd_bmp, &bmpinfo.size, 4);

    //宽度和高度
    lseek(fd_bmp, 0x12, SEEK_SET);
    read(fd_bmp, &bmpinfo.width, 4);
    read(fd_bmp, &bmpinfo.height, 4);

    //色深 
    lseek(fd_bmp, 0x1C, SEEK_SET);
    read(fd_bmp, &bmpinfo.depth, 2);

    //像素数组
    char* p = malloc(bmpinfo.size - 54); //图片的总大小-文件头大小
    lseek(fd_bmp, 0x36, SEEK_SET); //偏移到像素数组的位置
    read(fd_bmp, p, bmpinfo.size-54);
    bmpinfo.data = p;

    //关闭文件
    close(fd_bmp);

    return bmpinfo;
}

/**
 * @brief 显示一张bmp图片
 * 
 * @param x 
 * @param y 第y行的第x个像素点为图片最左上角的坐标
 * @param bmpname 图片的路径
 */
void lcd_draw_bmp(int x, int y, char* bmpname)
{
    //获取bmp图片信息
    BMP bmp = get_bmp_info(bmpname);
    printf("bmpname = %s, size = %d, width = %d, height = %d, depth = %d\n", 
            bmpname, bmp.size, bmp.width, bmp.height, bmp.depth);

    //一行的有效字节数
    int line_bytes = bmp.width * bmp.depth / 8;
    
    //一行的填充字节数
    int pad_bytes = (line_bytes % 4 == 0) ? 0 : (4 - line_bytes%4);

    //解析像素数组中的数据
    unsigned int color = 0; //像素点的颜色
    unsigned char a, r, g, b; //颜色分量

    //开始进行解析
    char* p = bmp.data;

    for(int h = 0; h < abs(bmp.height); h++)
    {
        for(int w = 0; w < abs(bmp.width); w++)
        {
            //获取颜色分量
            b = *(p++);
            g = *(p++);
            r = *(p++);
            //只考虑24位和32位图片
            a = (bmp.depth == 24) ? 0 : *(p++);
            //颜色分量合成颜色数据
            color = a<<24 | r<<16 | g<<8 | b;
            //描点
            // width > 0 : 每一行的像素点从左往右存放
            // width < 0 : 每一行的像素点从右往左存放
            // height > 0 : 每一行像素点从下往上存放
            // height < 0 : 每一行像素点从上往下存放
            int m = x + w;//横坐标  一行中的第几个像素点
            int n = y+bmp.height-1 - h; //纵坐标  第几行
            lcd_draw_point(n, m, color);
        }
        //跳过填充字节数
        p += pad_bytes;
    }
    free(bmp.data); //把申请的空间释放
}

2048.h

#ifndef __2048_H_
#define __2048_H_

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <pthread.h>
char *pic_bmp(int n);

void menu();//主页面
int Rander();//随机数
void InitNode();//初始化节点
int UP();//上下左右
int RIGHT();
int LEFT();
int DOWN();
void move();//移动方向判断
int over();//判断结束条件
void start();//开始函数
void Init_menu();//初始化页面
void *Botton(void *arg);//线程按键


#endif

2048.c


#include "2048.h"
#include "event.h"
#include "lcd.h"
int map[4][4] = {0};//存放数组
int score = 0;//分数
char input;//判断屏幕上下左右接收的数据
int gameover = 1;//判断游戏是否继续
int flag = 1;//判断数组是否移动

//==初始化函数用在重新开始和结束按钮=====
void Init_menu()
{
	lcd_draw_background(0xFFFFFF);
	int i = 0;
	int j = 0;
	score = 0;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			map[i][j] = 0;
		}
	}

	InitNode();
	menu();
}

//=====上下左右移动的函数=====
int UP()
{ // 向上
    int now, next; // 定义当前和下一个元素的变量
    int i, j, k;
    for (j = 0; j < 4; j++) // 外层循环遍历列(竖直方向)
    {
        for (i = 0; i < 4; i++) // 内层循环遍历行(从上到下)
        {
            now = map[i][j]; // 获取当前位置的元素值
            if (now != 0) // 如果当前元素不为0(即非空)
            {
                k = i + 1;
                while (k < 4) // 从当前行的下一行开始向下查找
                {
                    next = map[k][j]; // 获取下一个位置的元素值
                    if (next != 0) // 如果下一个位置不为0(即非空)
                    {
                        if (now == next) // 如果当前元素与下一个元素相等
                        {
                            flag = 1; // 设置标志位为1,表示发生了合并操作
                            score += map[k][j]; // 更新分数,加上合并后的值
                            map[i][j] = 2 * map[k][j]; // 当前位置更新为两者之和
                            map[k][j] = 0; // 下一个位置清零(合并后该位置为空)
                        }
                        k = 4; // 跳出循环
                    }
                    k++; // 继续向下查找
                }
            }
        }
    }
    for (j = 0; j < 4; j++) // 第二个for语句是把当前控制方向的非零元素移动当前方向的前面
    {
        for (i = 0; i < 4; i++)
        {
            now = map[i][j];
            if (now == 0) // 如果当前位置为空(值为0)
            {
                k = 1 + i;
                while (k < 4) // 从当前位置的下一行开始向下查找
                {
                    next = map[k][j]; // 获取下一个位置的元素值
                    if (next != 0) // 如果下一个位置不为空
                    {
                        flag = 1; // 设置标志位为1,表示发生了移动操作
                        map[i][j] = next; // 当前位置更新为下一个位置的值
                        map[k][j] = 0; // 下一个位置清零
                        k = 4; // 跳出循环
                    }
                    k++; // 继续向下查找
                }
            }
        }
    }
    // 返回注释代码
return flag;
}

向上移动代码中注释很清楚,其他方向同理,暂不例出了。

//======创建线程对重新开始按钮和游戏结束按钮=======
void *Botton(void *arg)
{
	int button = -1;
	while (1)
	{
		button = get_event_button();
		if (button == 1)
		{
			Init_menu();
		}
		else if (button == 2)
		{
			lcd_draw_bmp(0, 0, "game_over.bmp");
			lcd_draw_bmp(0, 0, "game_over.bmp");
			lcd_draw_bmp(0, 0, "game_over.bmp");
			exit(1);
		}
	}
}

main.c

#include "lcd.h"
#include "event.h"
#include "2048.h"
#include <pthread.h>


int main()
{
    lcd_init();
    lcd_draw_background(0xFFFFFF);
    pthread_t pid;
    int ret=pthread_create(&pid,NULL,Botton,NULL);
    start();
    lcd_uninit();
    return 0;
}

演示图片如下: 初始状态

向下移动 

 

完整项目:

链接:https://pan.baidu.com/s/1vO1ZAuLbndoxoN5Kt02ALg?pwd=ucyl 

提取码:ucyl

标签:ev,int,bmp,2048,lcd,小游戏,fd,include,ARM
From: https://blog.csdn.net/qq_63147384/article/details/142372107

相关文章

  • 【OpenHarmony实战开发】第20课-数据转码应用开发实战(下)
    1背景对于刚入门OpenHarmony开发的小伙伴来说,如果有一个合适的实战项目来练手,对自身的技术能力提升是非常有帮助的,本文将以一个小项目——数据转码应用,来讲解应用开发全流程。在《OpenHarmony数据转码应用开发实战(中)》我们讲述了核心解转码工具包的实现,以及UI组件数据绑定,那......
  • 【OpenHarmony实战开发】第19课-数据转码应用开发实战(中)
    1背景对于刚入门OpenHarmony开发的小伙伴来说,如果有一个合适的实战项目来练手,对自身的技术能力提升是非常有帮助的,本文将以一个小项目——数据转码应用,来讲解应用开发全流程。在《OpenHarmony数据转码应用开发实战(上)》中我们讲述了项目的需求、设计以及项目创建、UI界面开发......
  • python, Pycharm开发环境配置!
    1.windows官网下载地址windowspythonDownloadwindowpycharmDownload专业版30天试用,可以下载社区版2.先下载安装python64,32位都可,一般现在都是64位双击安装,勾选Addpython.exetoPATH中如果想自定义安装,点击Customizeinstallation自定义安装,......
  • ARM汇编指令
    一、学习ARM汇编的目的        学习arm汇编的主要目的是为了编写arm启动代码,启动代码启动以后,引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有:初始化异常向量表;初始化各工作模式的栈指针......
  • BlockCraft小游戏开发搭建
    BlockCraft小游戏开发搭建(张先生13101716752微电)BlockCraft游戏模式开发以下是关于BlockCraft游戏开发的一些要点:一、游戏概念与设计游戏主题与背景方块建造与探索以方块为基本元素构建一个开放世界的游戏主题。游戏背景设定在一个充满无限可能的虚拟世界,这个世界由各种......
  • 关于HarmonyOS的学习
    day34一、设计模式   +设计模式是一些对解决问题的总结和经验,设计模式是解决某个问题的最佳的方案,无数人优秀人的程序员的实验和错误总结   +共23种1.工厂模式     =>对于批量创建对象的一种解决方案     =>函数可以传递参数......
  • HarmonyOS开发之RichEditor组件实现评论编辑功能
    随着社交媒体和即时通讯应用的普及,用户对于内容创作的需求日益增长,特别是对于评论、回复等互动形式。为了满足这一需求,HarmonyOSNEXT提供了强大的RichEditor组件,支持图文混排和文本交互式编辑,使得开发者可以轻松构建功能丰富的编辑界面。本文将通过几个具体场景,详细介绍如何利用Ri......
  • 鸿蒙(HarmonyOS)--函数、类的声明和使用
    目录1.函数1.1函数的声明1.2可选参数 1.2.1 参数名?:类型  1.2.2参数名:类型=值 1.3Rest参数 1.4返回类型1.4.1显示返回1.4.2隐示返回1.4.3无返回类型1.5函数的作用域1.5.1全局作用域1.5.2局部作用域1.6函数调用1.7函数类型 1.8 箭头函数/l......
  • 鸿蒙(HarmonyOS)--编程语言-ArkTS 语言基础
    目录 ArkTS基础知识1声明1.1变量声明1.2常量声明1.3自动类型推断 2类型2.1基本类型 2.1.1 string2.1.2  number2.1.3boolean2.2引用类型2.2.1Object类型 2.2.2 Array类型2.2.3Void类型 2.3枚举类型 Enum2.4联合类型 Union 2.5 类型别......
  • ARM基础知识点及简单汇编语法
    计算机最小系统是一个能启动并运行基本功能的系统,其组成包括:处理器(CPU):执行指令的核心组件。内存:RAM:存储运行中的程序和数据。ROM:存储引导程序或固件。存储:用于存储操作系统和应用程序的设备,如闪存。输入输出接口:基本的通信接口,如串行接口(UART)和GPIO(通用输入输出)。电源:提供......