9. Matlab GUI:動畫與交互
Matlab在創建每一個圖形對象時,都為該對象分配唯一的一個值,稱其為圖形對象句柄(Handle)。
(1)計算機屏幕作為根對象(root)是由系統自動建立,其句柄值為0,沒有父對象。(2)Matlab採用figure()函數創建圖形窗口對象,其句柄值為一正整數,父對象是root。(3)Matlab採用axes()函數創建坐標軸對象,其句柄值為一浮點數(除了root和figure,其他圖形對象的句柄值都為浮點數),父對象是figure。可以通過下圖來更清晰地理解root,figure,axes和其他圖形對象之間的關係:
為了在背景圖片上畫一隻憤怒的小鳥,我們需要準備一些佐料:一張背景圖片 + 一張憤怒的小鳥的目標圖片。我故意製作了一枚 800 * 600 的背景圖片,又從網上下載了一枚憤怒的小鳥的目標圖片,改成50*50(PS. 目標圖片必須是PNG格式的,才能夠很」自然地「畫在背景圖片上)。
背景圖片
透明的憤怒的小鳥png圖片
如果了解了上面介紹的繪圖機制,理解以下這段代碼就容易多了。
% filename is:: Figure_ShowPicture.m% clearclc; clear; close all;
% create figure
hFigure1 = figure(1);set(gcf, position,[200 200 800 600]);% create axes1 for bkground and axes2 for angrybird
hAxes_BKG = axes(Parent, hFigure1);set(gca, box,off, xtick,[], ytick,[], units,pixels, position,[0 0 800 600]);hAxes_AB = axes(Parent, hFigure1);set(gca, box,off, xtick,[], ytick,[], units,pixels, position,[200 300 50 50]);% prepare the background image
imgBackGround = imread(background2.jpg);
% prepare the angrybird image, in PNG format, with alp parameter
[angryBird, map, alpha] = imread(angrybird50.png);%
axes(hAxes_BKG);h_BKG = imshow(imgBackGround);axes(hAxes_AB);
h_AB = imshow(angryBird);
set(h_AB,AlphaData, alpha); % 這裡的AlphaData屬性的設置是關鍵
最後出來的效果是這樣的:
整個代碼的核心框架是 root - figure - axes,在這個框架下利用set()函數來設置各個屬性的參數。
(A)
在代碼中的set()函數,有對當前創建的圖形窗口hFigure1進行設置,比如:% create figurehFigure1 = figure(1);set(gcf, position,[200 200 800 600]);這裡的gcf,代表的是當前的圖形窗口(畫布)。中括弧中的200 200 的意思是,圖形窗口以整個屏幕的左下角為遠點,在x軸200和y軸200處開始,放置圖形窗口。中括弧中的800 600的意思是,圖形窗口的寬是800像素,高是600像素。切換到筆記本電腦的桌面,點開運行後的圖形窗口,截屏以方便各位理解 position 屬性各個參數的含義(注意整個窗口的左下位置離開整個屏幕的左下位置的距離)。
(B)
在代碼中的set()函數,有對當前創建的坐標軸對象(專門用來放置背景圖)的屬性進行設置,比如:
% create axes1 for bkground and axes2 for angrybird
hAxes_BKG = axes(Parent, hFigure1);
set(gca,box,off,xtick,[],ytick,[],units,pixels,position,[0 0 800 600]);
xtick,[],ytick,[] -- 的意思是去掉x軸和y軸的刻度
units,pixels -- 的意思是以像素為單位
position,[0 0 800 600] -- 的意思是,以圖形窗口的左下為原點(0,0) 坐標軸的寬為800,高為600
(C)
% prepare the background imageimgBackGround = imread(background2.jpg);% prepare the angrybird image, in PNG format, with alp parameter
[angryBird, map, alpha] = imread(angrybird50.png);讀取圖片用的是 imread()函數,直接在函數後的小括弧中填寫圖片路徑即可讀取圖片信息。然而,讀取.jpg和讀取.png的方式有細微的不同,讀取.png格式的圖片,需要通過[angryBird, map, alpha]獲取透明度 alpha值!這個在後面的代碼中會有用到。
(D)
% axes(hAxes_BKG);h_BKG = imshow(imgBackGround);axes(hAxes_AB);
h_AB = imshow(angryBird);set(h_AB, AlphaData, alpha); %創建函數有一個特點,要是沒有就創建,要是已經存在了的就切換。這裡的axes()函數就是這麼回事,因為hAxes_BKG大坐標軸之前已經創建過了,所以,axes(hAxes_BKG)就是切換到大坐標軸的意思。先切換到大坐標軸 hAxes_BKG,利用imshow()函數把背景圖片先畫到大坐標軸上;然後切換到小坐標軸 hAxes_AB,同樣利用imshow()函數把目標圖片畫到小坐標軸上。最後那個紅色的注釋%,我是故意添加的,這也是整個教程最精髓的地方,set(h_AB,AlphaData, alpha) 的意思是給憤怒的小鳥圖片設置好透明度,這樣就能真正實現在背景圖片上「自然地」畫上憤怒的小鳥的效果。
聰明的讀者(一休)可能已經意識到了,掌握了在背景圖片上畫目標圖片的方法,製作一枚憤怒的小鳥飛過天際的動畫(平拋or斜拋)指日可待啊!甚至,我們可以用憤怒的小鳥實現動畫和交互。
不要「甚至」了,說干就干 O(∩_∩)O~
我們試著製作一個帶動畫+交互的遊戲場景。我們不僅要讓憤怒的小鳥飛起來,而且,它還會響應滑鼠的點擊,滑鼠點哪裡,憤怒的小鳥就會飛向哪裡。
第一個關鍵詞:框架
function y = AngryBird_FlyingR(x)
end
我們採取的就是這麼一個框架,在一個函數中構建一個憤怒的小鳥的動畫交互機制。
這個框架需要一個窗口,所以我們首先要創建一個窗口:
% create figure and set its properties
hFigure1 = figure(1);
set(gcf, Name, Angry Bird is Flying, position,[200 200 600 600], NumberTitle,off, toolbar,none, MenuBar,none, color,w, DoubleBuffer,on);
在這個窗口的基礎上,我們需要兩個坐標軸,一個是背景坐標軸,另外一個是憤怒的小鳥所在的坐標軸。
% create axes1 for BKGround
hAxes_BKG = axes(Parent, hFigure1);
set(gca, box,off, xtick,[], ytick,[], units,pixels, position,[0 0 600 600]);
% create axes2 for AngryBird
hAxes_AB = axes(Parent, hFigure1);
set(gca, box,off, xtick,[], ytick,[], units,pixels, position,[275 275 50 50]);
imgMatrix = zeros(600,600, 3);
h_imgMatrix = imshow(imgMatrix, parent, hAxes_BKG); %注意這個h_imgMatrix變數
所以,我們的框架是
root - figure - axes_BKG + axes_AB
剩下的就是配合這個框架做的一些工作,比如載入透明的憤怒的小鳥:
% prepare the current working path, prepare the angrybird image in PNG format, read it with alp parameter
CWPath = fileparts(mfilename(fullpath));
AB_FileName = angrybird50.png;
AB_PathName = sprintf(%s\%s, CWPath, AB_FileName);
[angryBird, map, alpha] = imread(AB_PathName);
% show the angrybird
h_AB = imshow(angryBird);
set(h_AB, AlphaData, alpha);
為了配合整個程序的編寫,我們需要給自己一些反饋信息,特別是滑鼠點擊窗口返回的位置信息,最好能夠顯示在窗口的正下方。
Text_Position = uicontrol(Parent,hFigure1, Style,text, Position,[0 0 600 16], HorizontalAlignment,Left, String, , BackgroundColor,White, visible,on);
第二個關鍵詞:定時器
動畫的本質,是在指定的時間內,不斷地變化(當前情境下,主要是憤怒的小鳥會移動一個距離)。
這個定時器需要設置好一些初始化的參數:
% ------------ prepare parameters and initialize the timer ---------------
hAxes_AB_x = 275;
hAxes_AB_y = 275;
timePeriod = 0.01; %相當於100次刷新/1s
v_AB = 320; % 要給憤怒的小鳥一個初速度,通過與角度的計算,解構成x和y方向的速度
theta = 0; % 一開始設置的角度是0°,相當於讓憤怒的小鳥水平向右飛行
v_AB_x = v_AB * cos(theta);
v_AB_y = v_AB * sin(theta);
% Initialize the timer
t = timer(TimerFcn, {@timerCallback, hAxes_AB}, ExecutionMode, fixedDelay, Period, timePeriod);
% 啟動定時器
start(t);
這個定時器函數的具體內容如下:
%-->timerCallback Function
function timerCallback(obj, event, hAxes_AB)
hAxes_AB_position = get(hAxes_AB, position);
hAxes_AB_x = hAxes_AB_position(1);
hAxes_AB_y = hAxes_AB_position(2);
delta_d_x = v_AB_x * timePeriod;
delta_d_y = v_AB_y * timePeriod;
hAxes_AB_x = hAxes_AB_x + delta_d_x;
hAxes_AB_y = hAxes_AB_y + delta_d_y;
set(hAxes_AB, position,[hAxes_AB_x hAxes_AB_y hAxes_AB_position(3) hAxes_AB_position(4)]);
if hAxes_AB_x > 550 | hAxes_AB_x < 0
v_AB_x = -v_AB_x;
end
if hAxes_AB_y > 550 | hAxes_AB_y < 0
v_AB_y = - v_AB_y;
end
end
這個定時器所起到的作用,可以理解為:
規定一個時間,計算憤怒的小鳥在x和y軸飛過的一段距離,然後重新設置憤怒小鳥所在的那個坐標軸所在的內置(相當於移動憤怒的小鳥)。
第三個關鍵詞:綁定機制
% ------------ Binding Event-Function --------------
set(hFigure1, DeleteFcn, {@DeleteFcn, t});
set(hAxes_BKG, ButtonDownFcn, @ButtonDownFcn);
第一個綁定機制,是給整個hFigure1窗口設置一個DeleteFcn,目的是在退出整個窗口的時候,要想辦法把 定時器 刪除。
為什麼在窗口退出時要刪除定時器呢?整個程序啟動之後,一旦觸發定時器,它就從程序中獨立出來,同時又起到了不斷發送信號給當前窗口(除非它被delete,要不然,即便當前程序關閉,它的作用機制還是存在於內存中的,就好像一個獨立飛出衛星的宇航員,他還在外頭飄著呢,衛星沒了,你說恐怖不恐怖?)。所以,當我們從當前程序中退出來的時候,一定要想辦法把它給關掉。
%-->DeleteFcn Function
function DeleteFcn(hObject, eventdata, t)
stop(t);
end
第二個綁定機制,是給整個背景坐標設置一個點擊的回調函數。
%-->Button Down Function on the hAxes_BKG
function ButtonDownFcn(hObject, eventdata, handles)
AB_Position = get(hAxes_AB, position);
pt = get(hAxes_BKG, CurrentPoint);
hAxes_AB_x = AB_Position(1);
hAxes_AB_y = AB_Position(2);
pt_x = pt(1) * 600;
pt_y = pt(3) * 600;
% 4 situations:
if pt_x > hAxes_AB_x & pt_y > hAxes_AB_y
delta_x = pt_x - hAxes_AB_x;
delta_y = pt_y - hAxes_AB_y;
theta = atan(delta_y/delta_x);
v_AB_x = abs(v_AB * cos(theta));
v_AB_y = abs(v_AB * sin(theta));
else
if pt_x > hAxes_AB_x & pt_y < hAxes_AB_y
delta_x = pt_x - hAxes_AB_x;
delta_y = hAxes_AB_y - pt_y;
theta = atan(delta_y/delta_x);
v_AB_x = abs(v_AB * cos(theta));
v_AB_y = -abs(v_AB * sin(theta));
else
if pt_x < hAxes_AB_x & pt_y < hAxes_AB_y
delta_x = hAxes_AB_x - pt_x;
delta_y = hAxes_AB_y - pt_y;
theta = atan(delta_y/delta_x);
v_AB_x = -abs(v_AB * cos(theta));
v_AB_y = -abs(v_AB * sin(theta));
else
delta_x = hAxes_AB_x - pt_x;
delta_y = pt_y - hAxes_AB_y ;
theta = atan(delta_y/delta_x);
v_AB_x = -abs(v_AB * cos(theta));
v_AB_y = abs(v_AB * sin(theta));
end
end
end
tmpString = sprintf(hAxes_AB_x:%d, hAxes_AB_y:%d, pt_x:%d, pt_y:%d, hAxes_AB_x, hAxes_AB_y, pt_x, pt_y);
set(Text_Position, String, tmpString);
end
首先判斷四個不同的象限中(以憤怒的小鳥為原點,滑鼠點擊的位置有可能在四個象限的不同的位置,與此同時帶來的計算方式也是不同的)。本質上,它的目的也是為了計算新的速度。速度的方向的改變,timer機制不斷地去計算它位移的距離,重新設置憤怒的小鳥的位置。
推薦閱讀: