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

% clear

clc; 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 figure

hFigure1 = 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 image

imgBackGround = 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機制不斷地去計算它位移的距離,重新設置憤怒的小鳥的位置。


推薦閱讀:

TAG:MATLAB | 圖形用戶界面 | 交互設計 |