1. 前言
从网络上可以看到用Java, C++等语言编写的拼图小游戏多不胜数,MATLAB作为高校几乎必设的一名基础编程语言或工具,大多都是用在数据计算、科学研究及实验仿真上,而网上流传的其编写小游戏的代码都是那几个老旧版本。这篇博文紧接着上篇继续介绍MATLAB GUI拼图游戏的详细过程,相比于早前的基本版本增加了图片选择、难度设置、数字提示的功能,使其在功能上更像一个游戏的样子。因为GUI界面版内容较多,这里限于篇幅分成了上、中、下连续三篇依次讲解。希望本文能给对MATLAB GUI界面设计或小游戏感兴趣的朋友有所启发。
2. 拼图游戏初始图片显示
简单来说,我们这一节实现的功能是:刚运行程序后,在两个用于显示原始图片和拼图图片的坐标轴上显示一张预先设置的初始图片(否则该区域空白,有失美观),在下方任务栏和界面左上角显示自行设置的图标。其效果如下:
要实现以上功能,需要在GUI界面中各控件创建时,添加相应代码使在界面初始化时显示我们设置的图片。每个控件都有一个回调函数CreateFcn,该函数只有在创建该控件的时候才会被调用。和上篇的方法一样,我们打开之前设计好的包含GUI界面的fig文件,分别选中用于显示初始图片和拼图图片的两个坐标轴,右击选择“查看回调”,点击“CreateFcn”即可跳转至该函数的定义编辑文件中,如下图所示:
axes_jigsaw_CreateFcn是这里创建显示拼图的坐标轴(axes_jigsaw)时调用的函数,在该函数中添加下面的代码:
matlab % --- Executes during object creation, after setting all properties. function axes_jigsaw_CreateFcn(hObject, eventdata, handles) % 创建图形时执行,设置拼图区坐标显示拼图原始图片 image(imread('jigsawImage.jpeg')); set(hObject,'Visible','off','Tag','axes_jigsaw')% 关闭坐标轴显示
代码第4行表示先读入事先准备好的放在同一目录下的名为‘jigsawImage.jpeg’的图片并在该坐标轴显示,第5行设置该坐标轴状态为隐藏(不影响该坐标上图片的显示)。同理在显示初始图片的坐标轴(axes_original)的CreateFcn函数下添加以下代码:
matlab % --- Executes during object creation, after setting all properties. function axes_original_CreateFcn(hObject, eventdata, handles) % 创建图形时执行,设置原始图片显示 image(imread('jigsawImage.jpeg')); set(hObject,'Visible','off','Tag','axes_original');
以上代码添加后再次运行程序则会在开始时显示图片,那么如何设置图标呢?在MATLAB 2016的窗口属性中可直接设置图标,而在之前的版本中则需要调用javax设置。采用上述相同的方式,在窗口(figure1)中右击选择“查看回调”选择“CreateFcn”,并跳转至该回调函数中,添加如下代码:
matlab % --- Executes during object creation, after setting all properties. function figure1_CreateFcn(hObject, eventdata, handles) javaFrame=get(hObject,'javaFrame'); % 获取图形句柄 % 为窗口设置图标 set(javaFrame,'FigureIcon',javax.swing.ImageIcon('Puzzle_icon.png')) % Puzzle_icon.png为指定的图标 warning off all; % 忽略警告
以上代码中,第4行为获取坐标轴中javaFrame对象的句柄,第6行设置窗口图标为文件名为“Puzzle_icon.png”的图片(文件需与m文件在同一文件夹下),第7行忽略警告。至此本节初始图片和图标功能完成,需要注意的是在“CreateFcn”中的代码只在创建时执行一次,因此若需修改效果需按上面的方法再次修改代码并生效。
3. 拼图游戏阶数选择功能
为了能够调整拼图难度,这里选择一个弹出式菜单控件供用户选择拼图阶数,其效果简单演示如下:
在上篇中就以及在设计GUI时,为这个弹出式菜单控件(popupmenu_rank)添加了供选择的菜单项。这里只需为其添加回调函数,对此这里再次通过上面的方法跳转至该控件的“Callback”函数中,或者在fig对应的m文件中直接找到这个函数,在其中添加如下代码:
matlab % --- 下拉选择框popupmenu_rank被点击时执行. function popupmenu_rank_Callback(hObject, eventdata, handles) global flag; % 是否可点击拼图块标识 flag=false; % 下拉框改变,点击开始前不能点击 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]); % 数字标识 Tag=[1:1:rank_Tag^2-1,0]; Tag=reshape(Tag,rank_Tag,rank_Tag); Tag=Tag'; % 显示拼图 axes(handles.axes_jigsaw) image(pic_data); axis off; % 根据选择情况决定是否显示数字标识 ismask=get(handles.checkbox_num,'Value'); axes(handles.axes_jigsaw); for i=1:size(Tag,1) for j=1:size(Tag,2) 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') end end if ~ismask h=findall(gca,'type','text'); delete(h); end
在以上代码中,第4-5行使用了一个全局变量flag这个变量与上篇中提到的是相同的变量,MATLAB中全局变量是可以在不同函数、文件中使用的,只要有所声明。这里flag的作用在作为一个标志决定是否能够点击拼图区的拼图使其移动,需要遵循的一条是即便上次游戏还在运行只要点击了难度选择菜单,立即将标志置为false表示重新设置重新开始游戏了,在开始游戏前点击无效了(涉及点击事件响应,下篇介绍)。
这部分思路是首先读取用于显示文件路径的标签上的显示的路径,并读取该路径下上的图片并显示,然后读取当前菜单栏选中项并据此计算出拼图阶数,根据阶数将拼图分成对应的拼图块并调整图片尺寸。由于需要考虑到点击菜单栏选择难度时可能已勾选“显示提示数字”的勾选框,因此需添加一段代码判断勾选状态并根据勾选状态决定是否显示数字。代码中许多代码与上篇中实现图片选择功能部分的代码一致,其中的代码注释也比较完善这里就不多介绍了。
4. 拼图块数字提示功能
这节要实现的功能是:游戏开始前或进行中,可以对对应的拼图块标识相应的数字作为提示信息,提示的数字应能够适应相应的拼图阶数。效果如下:
其实这部分实现在上篇也有所提及,采用上面的方法跳转至数字提示勾选框的回调函数“Callback”的代码编辑中,在该函数中添加如下代码:
matlab % --- checkbox_num按钮被点击时执行. function checkbox_num_Callback(hObject, eventdata, handles) global Tag;% 拼图块的数字标识 % 读取图片文件 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; % 如果拼图块标识Tag与当前的拼图阶数不符,根据当前阶数重置 if size(Tag,1)~=rank_Tag||size(Tag,2)~=rank_Tag Tag=[1:1:rank_Tag^2-1,0]; Tag=reshape(Tag,rank_Tag,rank_Tag); Tag=Tag'; end % 计算每个拼图块的长宽 len=min([size(pic_data,1),size(pic_data,2)]); len_col=round(len/rank_Tag); len_row=round(len/rank_Tag); % 根据选择情况决定是否显示数字标识 ismask=get(handles.checkbox_num,'Value'); axes(handles.axes_jigsaw); for i=1:size(Tag,1) for j=1:size(Tag,2) % 在每块拼图的中心位置添加对应数字显示 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') end end if ~ismask % 选择不显示,则删除所有text图形 h=findall(gca,'type','text'); delete(h); end
【代码解释】
代码第3行首先申明了一个全局变量Tag,它保存有当前拼图块的标号矩阵,通过对Tag的操作将图片与其对应起来,因为其他函数中也需要用到这里干脆设置为全局变量。代码第28-40行,理由text函数在坐标轴中显示文本,而显示的文本内容是每块拼图块对应的数字,通过遍历Tag能够得到,而要显示的位置可通过每块拼图的长宽计算得到,长宽除以2即要显示数字的中心位置。如第34行所示,利用text函数显示文本,第37-40行,根据获取的勾选的结果决定是否显示text文本,第39行表示删除所有text对象。