自制的MATLAB拼图游戏GUI界面版详解(下篇)

简介: 自制的MATLAB拼图游戏GUI界面版详解(下篇)

1. 前言


   这篇博文紧接着前面一篇继续介绍MATLAB GUI拼图游戏的详细过程,这一篇终于到了介绍最后的拼图逻辑功能实现的部分了。本篇博文中拼图逻辑的代码与早前的基础版本相似,在其基础上做了一些改进使其能适应新加入的功能。因为GUI界面版内容较多,这里限于篇幅分成了上、中、下连续三篇依次讲解,可点击跳转查看拼图游戏完整代码。希望本文能给对MATLAB GUI界面设计或小游戏感兴趣的朋友有所启发。


2. 开始并完成拼图


   简单来说,我们这一节实现的功能是:点击“开始拼图”按钮后,在拼图区点击拼图块能够移动拼图并最终能够完成游戏,得到完整的拼好的拼图。效果如下:



   这里自上而下逐个函数讲解,首先和上篇的方法一样,我们打开之前设计好的包含GUI界面的fig文件,选中开始拼图的按钮,右击选择“查看回调”,点击“Callback”即可跳转至该函数的定义编辑文件中,在该函数中添加如下代码:


java
% --- 按钮pushbutton_run被点击时执行.
function pushbutton_run_Callback(hObject, eventdata, handles)
% 获取选中的图片绝对路径
file_name=get(handles.edit_path,'String');
if exist(file_name,'file')==0 % 不存在则读取默认图片
   pic_data=imread('jigsawImage.jpeg');
else
   pic_data=imread(file_name);
end
n=get(handles.popupmenu_rank,'value'); % 获取下拉选择框的值
rank_Tag=n+2;% 计算选择的拼图阶数
% 确定每块拼图长宽
len=min([size(pic_data,1),size(pic_data,2)]);
len_col=round(len/rank_Tag);
len_row=round(len/rank_Tag);
% 转换图片为正方形
pic_data=imresize(pic_data,[rank_Tag*len_col rank_Tag*len_row]);
jigsaw(handles,rank_Tag, pic_data);% 调用拼图游戏


   以上代码中,第3-18行其目的是为了获得当前初始的图片数据pic_data、拼图的阶数rank_Tag及所有控件的句柄handles,第19行调用拼图函数jigsaw,这个函数之前没有创建,这里新建一个m文件命名为jigsaw,在里面添加实现拼图逻辑的代码。


3. 拼图主函数介绍


3.1 分割拼图并显示


   在编写拼图游戏主函数前,先写几个主函数中要用到的子函数,这部分先在jigsaw.m文件中添加两个用于分割拼图并显示的函数。这节代码与前面的基础版本基本类似,与前面的思路相同,将整幅图像按阶数分成对应份数并用数字表示。不过这里因选择的图片尺寸不一,因此需要先计算每块拼图的宽高,可由图片的整体尺寸除以拼图阶数求得;同时因为不是默认的单一坐标系,在显示拼图的时候为避免混乱需要指定显示拼图的坐标轴。将图片分割并显示的函数的代码如下:


java
function x = choose(image,index,pic_data,rank_Tag)
%% 根据索引选择对应位置上的拼图块
len_row=size(pic_data,1)/rank_Tag; % 每块拼图的宽度 
len_col=size(pic_data,2)/rank_Tag; % 每块拼图的高度
if index>0 %标记为1,2,3,4,5,6,7,8的拼图块
    % 计算出行数row以及列数column
    row=fix((index-1)/rank_Tag);
    column=mod(index-1,rank_Tag);
    % 分割出对应拼图块数据
    x=image(1+row*len_row:len_row*(row+1),1+column*len_col:len_col*(column+1),:);
else
    x=uint8(255*ones(len_col,len_row,3));% 拼图块0矩阵数据
end
function drawmap(A,handle,pic_data,rank_Tag)
% A:当前标签矩阵
% handle:图形句柄
% pic_data:图片数据
% rankTag:拼图阶数
%% 将运算数字与对应拼图对应显示图片
origin=pic_data;
len_row=size(pic_data,1)/rank_Tag; % 每块拼图的宽度 
len_col=size(pic_data,2)/rank_Tag; % 每块拼图的高度
% 对要显示的拼图进行赋值
for row=1:rank_Tag
    for col=1:rank_Tag
    pic_data(1+(row-1)*len_row:len_row*row,1+(col-1)*len_col:len_col*col,:)=choose(origin,A(row,col),pic_data,rank_Tag);
    end
end
axes(handle) % 选定坐标轴
image(pic_data) % 显示拼图
set(handle,'Visible','off');% 隐藏坐标轴


   在以上代码中,choose函数的输入参数除了image, index还加入了pic_data, rank_Tag,分别是用于拼图的图像数据以及当前拼图阶数。在对图片进行分割时应考虑到拼图的阶数,因此在代码第10行按照拼图块标记选择原图中相应数据进行赋值。drawmap函数中加入了handle表示要显示拼图的坐标轴句柄,以便在第31行指定坐标系。

3.2 移动拼图


   为了实现拼图的移动,需要知道鼠标点击的位置,这里鼠标位置所在的拼图块的行列号由row, col传入。判断是否移动拼图的标准是点击位置是否在空白拼图块(标号为0)的4邻域内(行号或列号相差1),因此我们只需比较row, col加或减1的标号矩阵的值是否为0,如为0表示位置相邻则交换两个位置上的值。



   值得注意的是为保证程序不会出错,需要先判断row, col的值是否在可点击范围内,同时为了实时显示游戏过程中的移动步数,有必要声明一个变量用于计数。移动拼图块的代码如下:

java
function tag=movejig(tag,row,col,handle_step)
global steps; % 计步
 %% 4个if分4种情况对不同位置处的点坐标与矩阵行列式统一
    last_tag=tag;
    num = tag(row,col);%鼠标位置与号码牌一致
    [max_row,max_col]=size(tag);
    % 检测点击位置是否处于0号临域
    if (row-1)<=max_row && (row-1)>0 % 点击在范围内
        if tag(row-1,col)==0 % 空白块在点击位置的上一行
            tag(row-1,col) = num; % 交换两个位置上的值
            tag(row,col) = 0;
        end
    end
    if (row+1)<=max_row && (row+1)>0
        if tag(row+1,col)==0 % 空白块在点击位置的下一行
            tag(row+1,col) = num;
            tag(row,col) = 0;
        end
    end
    if (col-1)<=max_col && (col-1)>0
        if tag(row,col-1)==0 % 空白块在点击位置的左边一列
            tag(row,col-1) = num;
            tag(row,col) = 0;
        end
    end
    if (col+1)<=max_col && (col+1)>0
        if tag(row,col+1)==0 % 空白块在点击位置的右边一列
            tag(row,col+1) = num;
            tag(row,col) = 0;
        end
    end
   zt = abs(tag-last_tag); % 比较两个矩阵
   if handle_step~=0 && sum(zt(:))~=0 % 矩阵已改变表示,拼图发生移动
       if exist('steps','var')
           steps=steps+1; % 步数加1
           set(handle_step,'String',num2str(steps))% 实时显示步数
       end
   end


   代码通过改变标记矩阵tag后根据tag的值显示拼图对应拼图块达到逻辑和显示上的拼图移动,第33-39行判断拼图是否发生移动并据此记录移动拼图块的步数,并显示在显示步数的文本标签上。


3.3 打乱拼图


   打乱拼图采用的是模拟手动打乱拼图的方式,不断随机产生点击位置坐标,利用上面编写的移动拼图函数,不断随机打乱拼图。随机动作的次数应随拼图的阶数而增加,重复一定次数可将拼图完全打乱,返回一个打乱之后的标记矩阵。打乱拼图的代码如下:

java
function y = Disrupt(rank_Tag)
%% 按人工打乱方式,随机打乱原拼图排列顺序
% y初始为顺序矩阵,用于打乱拼图
y=[1:1:rank_Tag^2-1,0];
y=reshape(y,rank_Tag,rank_Tag);
y=y';
for i = 1:300*rank_Tag % 打乱次数应随阶数增加而增加
    row=randi([1,rank_Tag]);% 产生一个范围在1到rank的整数
    col=randi([1,rank_Tag]);
    y=movejig(y,row,col,0);% 按随机产生的动作打乱拼图
end


3.4 拼图主函数


   主函数的设计思路是首先将标记矩阵打乱,并按照标记矩阵中的排列显示拼图块,然后需要获得鼠标点击处的位置坐标以移动拼图,每次移动后判断拼图顺序是否已经正确,顺序正确后结束游戏。获取鼠标点击位置坐标的方法是利用figureWindowButtonDownFcn属性定义一个坐标获取的回调函数。当在图上按下鼠标的时候,就会自动执行回调函数来获取坐标值。主函数的代码如下:


java
function jigsaw(handles, rank_Tag, pic_data)
% handles:图形句柄
% rank_Tag:拼图阶数
% pic_data:读入图片的全路径
%% 拼图主函数
Tag_A= Disrupt(rank_Tag);% 将标记矩阵的排列顺序打乱
drawmap(Tag_A,handles.axes_jigsaw,pic_data,rank_Tag);% 按照标记矩阵显示拼图
global flag;% flag决定在拼图区点击是否移动拼图
global Tag; % Tag是标记矩阵,定义成全局变量,方便传递参数
global steps;% steps用于计数,累计移动拼图的步数
% 初始化变量
flag=true; % 开始游戏后,可以点击移动拼图
steps=0;
Tag=Tag_A;
len_row=size(pic_data,1)/rank_Tag; % 每块拼图的宽度 
len_col=size(pic_data,2)/rank_Tag; % 每块拼图的高度
mask_number(Tag,handles,len_row,len_col) % 改进选择决定是否显示数字标记
set(handles.text_steps,'String',num2str(steps)) % 显示初始步数
set(gcf,'windowButtonDownFcn',{@ButtonDownFcn,handles,pic_data,rank_Tag}); % 点击鼠标时调用ButtonDownFcn函数
function mask_number(tag,handles,len_row,len_col)
%% 判断是否需要显示数字提示的函数
ismask=get(handles.checkbox_num,'Value');% 获取CheckBox的值(是否勾选)
rank_Tag=size(tag,1);% 拼图阶数
axes(handles.axes_jigsaw);% 确定坐标轴
% 根据是否勾选决定是否显示text标识
for i=1:size(tag,1)
   for j=1:size(tag,2)
       if ismask
            text(len_col/2*(2*j-1)-10,len_row/2*(2*i-1),num2str(tag(i,j)),'FontSize',55-rank_Tag*5,'Color','c')
       else
           % 未勾选,在该位置设置text为空,实现不显示
           text(len_col/2*(2*j-1)-10,len_row/2*(2*i-1),'','FontSize',55-rank_Tag*5,'Color','c')
       end
   end
end


   主函数中调用了mask_number其作用在于根据控件CheckBox的勾选状态决定是否在拼图块上显示数字提示,这个在前面的代码中已多次提及了。第25行是设置windowButtonDownFcn属性的回调函数,gcf表示当前图形窗口句柄,ButtonDownFcn是回调函数名,@ButtonDownFcn表示其函数句柄,后面handles, pic_data, rank_Tag是回调函数的输入参数,整条代码就是设置当在当前图形窗口中点击鼠标时就会转而执行ButtonDownFcn函数,该函数的代码在下节介绍。


4. 鼠标事件回调函数


   每次点击鼠标时就会执行一次回调函数,因此可以在回调函数中编写程序获取当前鼠标位置并据此移动一次拼图,然后判断拼图是否完成。定义回调函数ButtonDownFcn( ),输入参数src、event为系统约定变量,handles, pic_data, rank_Tag分别为需要用到的控件句柄、拼图的图片数据、拼图阶数,函数代码如下:

java
function ButtonDownFcn(src,event,handles,pic_data,rank_Tag)
%% 回调函数,鼠标点击事件发生时调用
global flag;% flag声明,共用全局变量
global Tag; % 全局变量声明
if flag % 若flag为true,允许移动拼图
    pt=get(gca,'CurrentPoint'); % 获取当前鼠标点击位置坐标
    xpos=pt(1,1); % 鼠标点击处的横坐标实际值
    ypos=pt(1,2); % 鼠标点击处的纵坐标实际值
    len_row=size(pic_data,1)/rank_Tag; % 每块拼图的宽度 
    len_col=size(pic_data,2)/rank_Tag; % 每块拼图的高度
    col = ceil(xpos/len_row); % 将横坐标值转换为列数
    row = ceil(ypos/len_col); % 将纵坐标值转换为行数
    % 判断鼠标点击位置是否在有效范围内  
    if(col<=rank_Tag && col>0)&&(row<=rank_Tag && row>0)   
        Tag=movejig(Tag,row,col,handles.text_steps); % 按点击位置移动拼图
        drawmap(Tag,handles.axes_jigsaw,pic_data,rank_Tag) % 显示拼图
        mask_number(Tag,handles,len_row,len_col) % 是否显示数字提示
        % order为顺序矩阵,以此判断拼图是否完成
        order=[1:1:rank_Tag^2-1,0];
        order=reshape(order,rank_Tag,rank_Tag);
        order=order';
        zt = abs(Tag-order); % 比较两个矩阵
        if sum(zt(:))==0 % 顺序已经完全吻合
            axes(handles.axes_jigsaw)
            % 游戏完成,补全拼图
            image(pic_data) % 显示全图
            set(handles.axes_jigsaw,'Visible','off');% 隐藏坐标轴
            msgbox('You did a good job ,恭喜完成!!!') % 提示完成信息
            flag=false;% 游戏已经完成,设置flag为false
        end
    else % 点击在外部区域
        return % 直接返回,不作处理
    end
end


【代码解释】


   代码第6行先判断全局变量flag的值,决定是否执行下面的操作(是否相应鼠标点击),由前面的代码可知当只有在重新选择图片、选择难度(阶数)的时候flag的值会被置为false来防止误操作,而当点击运行游戏按钮时该值会被重新置为true使程序运行。第7-9行获取鼠标点击处的坐标实际值,我们需要转换成拼图块的行列数,所以第11-15行获得拼图块的长宽,并由此计算行列数。第18行判断鼠标点击位置是否在拼图的显示区域内,第19-20行调用上面的函数按点击位置移动拼图并显示。第25-29行产生一个顺序的矩阵,并将当前的标号矩阵与该矩阵进行比较,以判断拼图是否已拼好。第31-36行在拼图拼好后显示完整图像并提示完成。第37行重新将flag的值置为false在重新开始游戏前不能再移动拼图。

相关文章
|
1月前
|
传感器 算法 vr&ar
六自由度Stewart控制系统matlab仿真,带GUI界面
六自由度Stewart平台控制系统是一种高精度、高稳定性的运动模拟装置,广泛应用于飞行模拟、汽车驾驶模拟、虚拟现实等领域。该系统通过六个独立的线性致动器连接固定基座与移动平台,实现对负载在三维空间内的六个自由度(三维平移X、Y、Z和三维旋转-roll、pitch、yaw)的精确控制。系统使用MATLAB2022a进行仿真和控制算法开发,核心程序包括滑块回调函数和创建函数,用于实时调整平台的位置和姿态。
|
6天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于yolov4深度学习网络的公共场所人流密度检测系统matlab仿真,带GUI界面
本项目使用 MATLAB 2022a 进行 YOLOv4 算法仿真,实现公共场所人流密度检测。通过卷积神经网络提取图像特征,将图像划分为多个网格进行目标检测和识别,最终计算人流密度。核心程序包括图像和视频读取、处理和显示功能。仿真结果展示了算法的有效性和准确性。
53 31
|
5天前
|
供应链 算法 调度
排队算法的matlab仿真,带GUI界面
该程序使用MATLAB 2022A版本实现排队算法的仿真,并带有GUI界面。程序支持单队列单服务台、单队列多服务台和多队列多服务台三种排队方式。核心函数`func_mms2`通过模拟到达时间和服务时间,计算阻塞率和利用率。排队论研究系统中顾客和服务台的交互行为,广泛应用于通信网络、生产调度和服务行业等领域,旨在优化系统性能,减少等待时间,提高资源利用率。
|
16天前
|
算法
超市火灾烟雾蔓延及人员疏散的matlab模拟仿真,带GUI界面
本项目基于MATLAB2022A开发,模拟了大型商业建筑中火灾发生后的人员疏散与烟雾扩散情况。算法通过设定引导点指导人员疏散,考虑视野范围、随机运动及多细胞竞争同一格点的情况。人员疏散时,根据是否处于烟雾区调整运动策略和速度,初始疏散采用正态分布启动。烟雾扩散模型基于流体方程,考虑了无风环境下的简化。
|
18天前
|
存储 算法 数据安全/隐私保护
基于方块编码的图像压缩matlab仿真,带GUI界面
本项目展示了基于方块编码的图像压缩算法,包括算法运行效果、软件环境(Matlab 2022a)、核心程序及理论概述。算法通过将图像划分为固定大小的方块并进行量化、编码,实现高效压缩,适用于存储和传输大体积图像数据。
|
4月前
|
安全
【2023高教社杯】D题 圈养湖羊的空间利用率 问题分析、数学模型及MATLAB代码
本文介绍了2023年高教社杯数学建模竞赛D题的圈养湖羊空间利用率问题,包括问题分析、数学模型建立和MATLAB代码实现,旨在优化养殖场的生产计划和空间利用效率。
225 6
【2023高教社杯】D题 圈养湖羊的空间利用率 问题分析、数学模型及MATLAB代码
|
4月前
|
存储 算法 搜索推荐
【2022年华为杯数学建模】B题 方形件组批优化问题 方案及MATLAB代码实现
本文提供了2022年华为杯数学建模竞赛B题的详细方案和MATLAB代码实现,包括方形件组批优化问题和排样优化问题,以及相关数学模型的建立和求解方法。
141 3
【2022年华为杯数学建模】B题 方形件组批优化问题 方案及MATLAB代码实现
|
4月前
|
数据采集 存储 移动开发
【2023五一杯数学建模】 B题 快递需求分析问题 建模方案及MATLAB实现代码
本文介绍了2023年五一杯数学建模竞赛B题的解题方法,详细阐述了如何通过数学建模和MATLAB编程来分析快递需求、预测运输数量、优化运输成本,并估计固定和非固定需求,提供了完整的建模方案和代码实现。
111 0
【2023五一杯数学建模】 B题 快递需求分析问题 建模方案及MATLAB实现代码
|
7月前
|
数据安全/隐私保护
耐震时程曲线,matlab代码,自定义反应谱与地震波,优化源代码,地震波耐震时程曲线
地震波格式转换、时程转换、峰值调整、规范反应谱、计算反应谱、计算持时、生成人工波、时频域转换、数据滤波、基线校正、Arias截波、傅里叶变换、耐震时程曲线、脉冲波合成与提取、三联反应谱、地震动参数、延性反应谱、地震波缩尺、功率谱密度
基于混合整数规划的微网储能电池容量规划(matlab代码)
基于混合整数规划的微网储能电池容量规划(matlab代码)

热门文章

最新文章