这一节先说说怎么写一个2048的GUI界面, 2048的规则很简单, 沿着某一个方向滑动可以将该方向上相同的数字合并.

例如一行数字为[1,2,2,4]向右边滑动就会变成[0,1,4,4], 这里是为了使用一个4×4的矩阵来表示数字面板, 所以使用零补齐而已.其他方向也是类似的. 但所每次滑动会随机在空为生成两个数字, 2或者4, 所以如果无法滑动就输了, 合并出现2048就赢了. 所以需要写移动面板的函数, 在面板生成随机数的函数, 判断输赢的函数, 当然还有开始和重启的函数, 我并没有写撤回的功能, 这个太无耻了…

这里就不再具体介绍如果如何实现左右上下的移动操作以及如何判断输赢的函数怎么写的, 我只介绍如何实现向右的操作, 其他方向都是类似的.

function new_a = operation(a)
a = a(a>0);
new_a = [];
k = 1;
while k <= length(a)
    if k == length(a)
        new_a = [new_a a(k)];
        break
    end
    if a(k) == a(k+1)
        new_a = [new_a a(k)+a(k+1)];
        k = k + 2;
    else
        new_a = [new_a a(k)];
        k = k + 1;
    end
end
end

实际上就是在判断右边的下一个与当前位置是否相同, 如果相同就可以合并前进两个格子, 即为第一个第一个语句new_a = [new_a a(k)+a(k+1)], 否则就之前进一个格子.

下面主要将怎么写出GUI, 例如下面的语句:

hf = figure('resize','off','name','',...
    'position', [mean_size*0.25 mean_size*0.1 mean_size*0.7467 mean_size*0.56],...
     'numbertitle', 'off');
hax = axes;
set(gcf,'menubar', 'none','color', [238 220 130]/255)
set(gca,'position',[0.2500    0.1100    0.7750    0.8150])
set(gca, 'xlim', [0, 6], 'ylim', [0, 6])
set(hax, 'xtick', [], 'ytick', [], 'box', 'on')
set(hax, 'color', [255,255,224]/255)
set(hax, 'DataAspectRatio', [1 1 1], 'PlotBoxAspectRatio', [1 1 1])
uicontrol('style', 'pushbutton', 'string', 'Start', 'fontsize', 18,...
          'fontweight','bold','fontname', 'Times New Roman',...
          'units', 'normal', 'position', [0.04 0.6 0.1 0.1], 'parent', hf, ...
          'fontweight', 'bold', 'callback', @start);

我们先新建一个figure, 接着在上面建一个axes, 并用set函数设置一些属性, 最主要的是uicontrol这个函数, 这个函数就是用来写控制按钮(pushbutton)或者滑条(slider), 选项(popup), 详情参见这里.这里我们只需要使用按钮即可, 只需要按下这个按钮就可以回调 跟在callback后面的函数start.

但是注意到这里的函数的输入参数与一般的输入不同, 回调函数的参数一般以全局变量的形式传入, 即为先要在一个m文件中global var_a, 然后在另一个m文件申明一下global var_a就可以直接使用变量var_a了, 而且更改变量var_a也是实时更新的. 为什么我们使用全局变量传入参数是因为callback的输入参数为src(源)本身而不是其属性.

function start(~, ~)
global board h start_tag rate_2 h_patch
if start_tag == 1
    h = zeros(15)*NaN;
    h_patch = h;
    board = zeros(4);
    rate_2 = 0.8;
    choice = randperm(16);
    site = choice(1:2);
    for k = 1:2
        if rand < rate_2
            board(site(k)) = 2;
        else
            board(site(k)) = 4;
        end
    end
    draw_board
    start_tag = 0;
else
    return
end
end

例如start函数中全局变量h_patch 用于填充画出各个数字面板的句柄(handle), 该参数需要在整个游戏中传来传去的, 因为做上下左右操作的时候需要在重新更新面板的. 注意到这里函数的输入是两个~, 也即为忽略所有的传入参数, 因为这里我们并没有改变uicontol的属性.

总结一下, 大致思路即为将操作的函数写在每个按钮的callback后面, 然后点击按钮即可执行该函数, 记得实时更新画面就可以按下方向键控制面板上的数字移动了. 如果你之前没有写过matlab的GUI界面, 现在应该就英国可以写一个2048的界面了.

附注:

这里讲一下回调函数的输入参数的意义. 这一部分与编写这个GUI已经无关了… 我们先定义一个回调函数:

function plot_callback(src,~)
   src.Color = 'red';
end

接着在命令行输入:

x = linspace(0,2*pi,100);
y = sin(x);
plot(x,y,'ButtonDownFcn',@lineCallback)

点击正弦函数的曲线即可将颜色变成红色, 所以传入的第一个参数类似于源的句柄, 可以用于改变其属性.我们还可以传入更多的参数用以改变源的属性.例如定义:

function plot_callback(src,~)
   src.Color = 'red';
   src.LineStyle = arg1;
   src.Marker = arg2;
end

这样就可以同时改变曲线的颜色, 线型, 形状.

x = linspace(0,2*pi,100);
y = sin(x);
plot(x,y,'ButtonDownFcn',{@lineCallback,'--','*'})

另外一个event的参数我还没有搞明白, 搞明白了再写…, 当然这两个参数在这里都是没有使用的.