mel語初解之一-基礎和界面篇(5)
本文主要是針對初學Maya的人,如果你已經學了一個月以上的Maya,那你就可以學習這篇mel教程了。
我來寫一點mel掃盲教程,主要是針對初學Maya的人,如果你已經學了一個月以上的Maya,那你就可以學習這篇mel教程了。不一定要等到Maya掌握的非常熟練才去學mel,相反,如果你在初學Maya的時候就對mel有所了解,那對于將來更好的了解和掌握Maya會起到事半功倍的效果。
當然首先要提到的是mel的基本概念,如下:
mel是Maya Embedded Language(Maya內置語言)的縮寫,是一種強大的命令和腳本語言,讓你直接控制Maya的特征、進程和工作流程。
其次要提到的是應該抱怎樣的態(tài)度:
有許多人認為mel比較難,我的看法是并不比游戲攻關難,在網上經常看到有人為攻一關花掉一個月時間,學mel如果能達到這個境界一定會很了不起。學mel的確可能會枯躁一些,因為找不到像CS那么多人給你去殺。
mel是個可大可小的東西,如果你不是搞程序開發(fā)的,應該盡可能的讓mel給你節(jié)省時間,而不要在學習和編寫mel程序上花太多時間,應盡量編寫簡單且實用的程序。我的教程也是針對這一點寫的,如果你已經做好了準備,就和我一起進入mel的世界吧。
今天要講的是命令組合。
打開Maya,在命令行(Command Line)輸入"cone"并按回車,視圖中創(chuàng)建了一個圓錐。如果你沒有看到命令行,用Display->UI Elements->Command Line來顯示它。你也許會問用命令行創(chuàng)建圓錐跟用菜單創(chuàng)建有什么差別。我來告訴你,只是形式上的差別,本質上菜單和命令行都是在執(zhí)行mel命令,可以說你在Maya中的一舉一動都是在執(zhí)行mel命令。
現(xiàn)在打開mel編輯器(Script Editor),mel編輯器分兩部分,上面是歷史窗,下面是編輯窗。歷史窗顯示你剛剛執(zhí)行的命令和命令的結果或錯誤信息。編輯窗可以輸入命令,而且可以輸入多行命令,但每個MEL 命令之后必須以分號結束。選擇mel編輯器的菜單Edit->Clear History,把歷史窗清潔一下。
在編輯窗輸入"cone -ax 0 1 0 -r 1;"并按Ctrl+Enter或數(shù)字鍵那邊的Enter,視圖中創(chuàng)建了正立的一個圓錐。再看歷史窗,多了兩行,第一行是剛剛執(zhí)行過的命令,第二行是命令的返回值(Return Result)。我明天會著重講返回值,今天先來解釋一下標志(Flag)。剛才那個"-ax"和"-r"就是"cone"命令的標志,一般一個標志分長短兩種寫法,比如"-r"和"-radius"就是同一個標志,寫哪個都行。長標志看起來比較清楚,而短標志寫起來比較方便。因此,對于你比較熟悉的標志,盡量用短標志,不熟悉的就用長標志。改一下"cone"命令"-r"后面的數(shù)值,比如改成"cone -ax 0 1 0 -r 3"再執(zhí)行,發(fā)現(xiàn)創(chuàng)建出的圓錐半徑變大了,由此你可知道-r/-radius是控制圓錐半徑的。
當然"cone"命令還有很多標志,要深入了解這個命令,最好的辦法是看幫助文檔,Maya為此專門提供了一個mel命令(就是help命令)來快速察看幫助。執(zhí)行"help -doc cone",就可以打開cone命令的幫助文檔。要想看到所有mel命令的幫助,選擇Maya菜單Help->MEL Command Reference... Maya的幫助文檔中一共收錄了將近一千條mel命令,聽起來很恐怖吧。不過沒關系,要想在惡劣的環(huán)境下自在的生存,首先要學會對困難視而不見?磶椭鷷r按分類(Categories)去看,只要翻翻你感興趣的幾個分類就行了,如果你都不感興趣,就都不要看了,等用到了再去翻也不遲。
現(xiàn)在擺在你面前的一個嚴峻的問題就是你怎樣從這近一千條mel命令找到你想要的那條命令。別怕,你雖然不熟悉mel,但你還不至于不熟悉Maya的菜單吧。選擇Create->NURBS Primitives->Cone來創(chuàng)建一個圓錐,這時你會發(fā)現(xiàn)mel歷史窗中多了一行命令,其中"cone -p 0 0 0 -ax 0 1 0 -ssw 0 -esw 360 -r 1 -hr 2 -d 3 -ut 0 -tol 0.01 -s 8 -nsp 1 -ch 1;"就是一個完整的創(chuàng)建圓錐的命令。在命令行執(zhí)行一下這句命令,會發(fā)現(xiàn)可以得到跟菜單相同的結果。有了這種方法,就不用為你掌握的mel命令太少而犯愁了。
你也許已經意識到得到每一步操作的命令意味著什么了,就是可以把這些命令寫在一起,然后一下子執(zhí)行,就像Office里的宏和PhotoShop里的Actions一樣。好,那就開始吧。
現(xiàn)在Maya雖然已經支持默認燈光渲染了,不過我們建模時經常還是會打一盞燈來觀察一下效果。具體過程是先創(chuàng)建燈光,再把它移動到一個合適的位置,然后旋轉一下角度,然后還要調整亮度。如果你經常要打燈光觀察模型,你一定希望點一個按鈕來完成這一切,我們的實例1就來解決這個問題吧。
------------------------------------------------------
實例1:擺放燈光
首先清潔一下mel歷史窗準備記錄命令。
選擇Create->Lights->Directional Light創(chuàng)建一盞直射燈,把直射燈往左后方移動一下,再旋轉一下使它的方向指向視圖中心,Ctrl+a打開屬性編輯器,把燈光的屬性(Intensity)改為1.2。
這時再看看mel歷史窗,多了類似如下幾行:
defaultDirectionalLight(1, 1,1,1, "0", 0,0,0);
move -r -5.393374 -0.124913 3.360977 ;
rotate -r -os -33.086209 0 0 ;
rotate -r -os 0 -24.179269 0 ;
setAttr "directionalLightShape1.intensity" 1.2;
把這幾行字都選中,用鼠標左鍵拖放到Shelf工具條上,會發(fā)現(xiàn)Shelf上多了一個mel圖標。新建一個文件,按一下這個mel圖標,你會發(fā)現(xiàn)你成功了。
一般用戶編寫的常用的mel程序都是放在Shelf里的,因此你需要對Shelf了解一下。鼠標左鍵在Shelf左下角的三角形那里按下,在下拉菜單選擇Shelf Editor...打開Shelf編輯器。
關于Shelf編輯器的我就不多講了,你自己瞎按一會兒也會弄明白的。我要說的是既然你已經開始學mel了,要那么多亂七八糟的Shelf也沒什么用,你可以只留一個General,其它的都刪掉,然后新建一個Shelf,名為myTools。注意我所說的這些操作都是在Shelf編輯器中進行的,不要跑到"你的文檔\maya\5.0\prefs\shelves"那里去刪Shelf,否則再啟動Maya時那些Shelf會死而復生的。更改Shelf圖標也在Shelf編輯器中,你可以把那個難看的mel圖標換成你喜歡的美女。看看好萊塢人用的Maya工作界面,都是自己定制的Shelf,我們別的地方不如人家,這一點還是可以跟人家很像的。
現(xiàn)在來解釋一下剛才的幾句命令。憑你對英文的理解,其實也能猜到這幾句的意思吧。
"defaultDirectionalLight"就是創(chuàng)建一盞直射燈。
"move"就是移動,"-r/-relative"是經常用到的,意思是相對于物體當前的位置。
例如"move -r 1 0 0;"是說把物體沿X軸移動一個格子,而"move 1 0 0;"是說把物體的"translateX"屬性設為1,"translateY"和"translateY"屬性設為0。
"rotate"用了兩次,就是旋轉了兩次,看一下右邊通道框里的旋轉數(shù)值,你可以把這兩句并作一句,就是"rotate -35.534708 -20.070585 13.771703;"。"-os"是指物體坐標軸,相對"-ws"于世界坐標軸而言的。要想讓物體沿世界坐標軸旋轉,雙擊旋轉工具圖標打開工具設置窗,把Rotate Mode由Local改為Global即可。
"setAttr"是設置屬性,這可是一個關鍵性的命令,需要重視起來。
對于屬性你一定不會陌生的,它是設動畫的關鍵。屬性可以在屬性編輯器(Attribute Editor)和通道框(Channel Box)中修改,也可以用mel的"setAttr"直接修改。物體屬性的寫法是物體名+"."+屬性名(object.attribute),屬性名同mel標志一樣有長短兩種寫法。選擇通道的菜單Channels->Channel Names->...可以切換長短名的顯示,平常看到的那個"Nice"名只是為了好看,在mel和表達式中都不中用。
了解屬性對于我以后要講到的表達式有決定性的意義,表達式就是通過對物體的屬性與時間之間、屬性與屬性之間建立某種運算關系來控制動畫的。
根據(jù)前面的說法,"move 1 0 0 nurbsCone1;"同樣可以寫成:
setAttr "nurbsCone1.translateX" 1;
setAttr "nurbsCone1.translateY" 0;
setAttr "nurbsCone1.translateZ" 0;
或:
setAttr "nurbsCone1.tx" 1;
setAttr "nurbsCone1.ty" 0;
setAttr "nurbsCone1.tz" 0;
最后讓我們來編一個比較實用的mel命令組合吧。
------------------------------------------------------
多邊形人頭建模時,一般是先建一半臉,建好后再鏡像,再圓滑一下。
你建一半臉的時候,很難想像出鏡像圓滑后整張臉的樣子吧。你現(xiàn)在學會了mel命令組合,可以實現(xiàn)點一個按鈕來完成鏡像和圓滑了,這樣你可以在建模的過程中預覽一下完成的效果。這樣有用的程序后面還要繼續(xù)完善下去,先給它起個形象的名字叫myFullMoon(月圓),版本訂為1.0。
------------------------------------------------------
實例2:myFullMoon1.0
打開你的半邊臉文件,為了在操作過程中減少不必要的麻煩,我們先來把模型規(guī)范化一下:
如果你的模型是右半臉,把它改成左半臉。要保證你能在前視圖看到模型的正面臉,側視圖看到側面臉,頂視圖看到頭頂。把模型放到視圖中心線的左邊。選中模型,選擇Modify->Center Pivot,選擇Modify->Freeze Transformations,選擇Display->Component Display->Backfaces不顯示背面,如圖,記住你的模型寬度是幾個格子。別忘了把mel歷史窗打掃干凈。
規(guī)范好了,就存一下盤。
選中模型,現(xiàn)在可以開始記錄命令了。選擇菜單Edit Polygons->Extrude Face。mel歷史窗記錄了四行命令,你要知道的是只有一行是對你有用的。想知道為什么?你把每行命令單獨試試就知道了,只有
polyExtrudeFacet -ch 1 -kft 0 -pvx -3.926295877 -pvy -1.118658066 -pvz 4.649739981 -tx 0 -ty 0 -tz 0 -rx 0 -ry 0 -rz 0 -sx 1 -sy 1 -sz 1 -ran 0 -divisions 1 -off 0 -ltz 0 -ws 0 -ltx 0 -lty 0 -lrx 0 -lry 0 -lrz 0 -lsx 1 -lsy 1 -lsz 1 -ldx 1 -ldy 0 -ldz 0 -w 0 -gx 0 -gy -1 -gz 0 -att 0 -mx 0 -my 0 -mz 0 polySurface1.f[0:682];
這句可以擠出面。
現(xiàn)在要在通道框里改幾個屬性,Keep Face Tog屬性改為on,Scale X為-1,Translate X為模型寬度的負值(我的是-7.5)。
歷史窗多了三行有用的命令:
setAttr "polyExtrudeFace1.keepFacesTogether" on;
setAttr "polyExtrudeFace1.translateX" -7.5;
setAttr "polyExtrudeFace1.scaleX" -1;
現(xiàn)在你可以看出來模型面的法線反了,好,把它正過來。按F8回到物體選擇模式,選擇Edit Polygons->Normals->Reverse。
這回你該從這幾行命令中看出有用的命令了: polyNormal -normalMode 0 -ch 1 polySurface1.f[0:1449];
下一步是Polygons->Smooth,找到這句命令: polySmooth -mth 0 -dv 1 -c 1 -kb 1 -ksb 1 -khe 0 -kt 1 -sl 1 -dpe 1 -ps 0.1 -ro 1 -ch 1 polySurface1.f[0:1449];
最后是取消模型的選擇: select -cl ;
好了,操作結束。來整理一下有用的命令吧,結果如下:
polyExtrudeFacet -ch 1 -kft 0 -pvx -3.926295877 -pvy -1.118658066 -pvz 4.649739981 -tx 0 -ty 0 -tz 0 -rx 0 -ry 0 -rz 0 -sx 1 -sy 1 -sz 1 -ran 0 -divisions 1 -off 0 -ltz 0 -ws 0 -ltx 0 -lty 0 -lrx 0 -lry 0 -lrz 0 -lsx 1 -lsy 1 -lsz 1 -ldx 1 -ldy 0 -ldz 0 -w 0 -gx 0 -gy -1 -gz 0 -att 0 -mx 0 -my 0 -mz 0 polySurface1.f[0:682];
setAttr "polyExtrudeFace1.keepFacesTogether" on;
setAttr "polyExtrudeFace1.translateX" -7.5;
setAttr "polyExtrudeFace1.scaleX" -1;
polyNormal -normalMode 0 -ch 1 polySurface1.f[0:1449];
polySmooth -mth 0 -dv 1 -c 1 -kb 1 -ksb 1 -khe 0 -kt 1 -sl 1 -dpe 1 -ps 0.1 -ro 1 -ch 1 polySurface1.f[0:1449];
select -cl ;
把這幾行命令都選中,用鼠標左鍵拖放到Shelf工具條上,Shelf上出現(xiàn)mel圖標。重新載入模型文件,按一下這個mel圖標,你又一次成功了。
對Maya熟悉一點的人會覺得那三個setAttr用的很愚蠢,因為完全可以在polyExtrudeFacet命令的標志中解決問題。其實我們的第一步只是要求做到,第二步才是要求做好。以下是簡化過的結果:
polyExtrudeFacet -ch 1 -kft 1 -pvx -3.926295877 -pvy -1.118658066 -pvz 4.649739981 -translateX -7.5 -ty 0 -tz 0 -rx 0 -ry 0 -rz 0 -scaleX -1 -sy 1 -sz 1 -ran 0 -divisions 1 -off 0 -ltz 0 -ws 0 -ltx 0 -lty 0 -lrx 0 -lry 0 -lrz 0 -lsx 1 -lsy 1 -lsz 1 -ldx 1 -ldy 0 -ldz 0 -w 0 -gx 0 -gy -1 -gz 0 -att 0 -mx 0 -my 0 -mz 0 polySurface1.f[0:682];
polyNormal -normalMode 0 -ch 1 polySurface1.f[0:1449];
polySmooth -mth 0 -dv 1 -c 1 -kb 1 -ksb 1 -khe 0 -kt 1 -sl 1 -dpe 1 -ps 0.1 -ro 1 -ch 1 polySurface1.f[0:1449];
select -cl ;
關于使用mel命令組合的方法,最后要提到一點,如果你執(zhí)行命令時發(fā)覺mel歷史窗沒有變化,可以在mel編輯器勾選Script->Echo All Commands。一般我們不需要顯示那么多命令,因為很多對我們沒用,所以不選Echo All Commands。
通過myFullMoon1.0我們可以把一半臉變成整張臉,但如果你發(fā)現(xiàn)這張臉不夠美觀想回到半臉狀態(tài)修改怎么辦?我告訴你,按z鍵撤消上一步操作。這應該不是你不知道的,但你可能因旋轉相機觀察而已經操作了很多步無法撤回了,還存了盤。
如果你沒刪構造歷史,那你就是幸運的。介紹一個命令--"delete",就是你按鍵盤delete鍵所執(zhí)行的命令。執(zhí)行"delete polySurface1",瞧,把你那個整張臉模型都刪去了吧。如果你真的都刪去了,趕快撤消這一步,因為這不是你我想做的事。我們來編一個恢復成半張臉的程序myHalfMoon。
實例3:myHalfMoon
你一定猜到我這回要用函數(shù)了,對,把下面的幾句代碼執(zhí)行一下:
global proc myHalfMoon()
{
delete polySmoothFace1;
delete polyNormal1;
delete polyExtrudeFace1;
}
執(zhí)行完了,好像不起作用,因為你還不知道它的作用。你不知道你在不知不覺中已經定義了一個mel命令,命令的名稱就是"myHalfMoon"。試試吧,選中模型,在命令行輸入"myHalfMoon",按回車,命令就起作用了,F(xiàn)在如果說myHalfMoon就是個mel函數(shù),你應該覺得比天書容易理解一點了吧。
------------------------------------------------------
[注]
關于Maya中的函數(shù)概念問題,按以前的說法,說myHalfMoon是函數(shù)是不嚴謹?shù)摹?
"A function is a procedure with a return value. " [Maya 4.5 Help docs]
函數(shù)是帶有返回值的程序。
這樣myHalfMoon只能應稱之為程序(procedure),帶有返回值的程序才叫函數(shù)(Functions)。不過這樣反而帶來麻煩,我很高興看到Maya5.0已經改變了這種說法。
"User defined functions are called procedures. " [Maya 5.0 Help docs] 用戶定義的函數(shù)稱之為程序(procedure)。以前的定義不見了,言下之意函數(shù)的定義跟返回值沒什么關系了。
這下子可能給你帶來了更多的疑問,我先回答幾個吧。
問:Maya中的mel命令都是通過mel函數(shù)編出來的嗎?
答:不是。有900多個Maya內置命令,這些命令是寄生在Maya模塊文件(WinNT版的模塊文件就是dll文件或mll文件),是已經編譯好的匯編指令組合。內置命令命令一般都帶有標志(Flag),而mel函數(shù)命令沒有標志。mel函數(shù)命令通常被稱為“腳本命令”(Script Command),內置命令則直接稱“命令”(Command)。
問:怎樣才能知道一個命令是不是腳本命令?
答:用whatIs命令。
例如:"whatIs myHalfMoon;",結果中出現(xiàn)"Mel procedure"(mel程序)字樣,說明是腳本命令。
問:返回值是個什么東東?
答:以后會講到的。
相信你一定掌握了把myFullMoon也變成函數(shù)的本領了吧,如果還沒掌握我可要懷疑你的智商了。不過呢,反正我的教程也是白癡級的,我就把它說白了吧:
------------------------------------------------------
實例4:myFullMoon2.0
把Shelf上那個myFullMoon圖標用鼠標中鍵拖放到mel編輯窗,把mel代碼灑到編輯窗里。然后把這些代碼寫到"global proc myFullMoon(){ }"的大括號里,如下:
global proc myFullMoon(){polyExtrudeFacet -ch 1 -kft 1 -pvx -3.926295877 -pvy -1.118658066 -pvz 4.649739981 -translateX -7.5 -ty 0 -tz 0 -rx 0 -ry 0 -rz 0 -scaleX -1 -sy 1 -sz 1 -ran 0 -divisions 1 -off 0 -ltz 0 -ws 0 -ltx 0 -lty 0 -lrx 0 -lry 0 -lrz 0 -lsx 1 -lsy 1 -lsz 1 -ldx 1 -ldy 0 -ldz 0 -w 0 -gx 0 -gy -1 -gz 0 -att 0 -mx 0 -my 0 -mz 0 polySurface1.f[0:682];
polyNormal -normalMode 0 -ch 1 polySurface1.f[0:1449];
polySmooth -mth 0 -dv 1 -c 1 -kb 1 -ksb 1 -khe 0 -kt 1 -sl 1 -dpe 1 -ps 0.1 -ro 1 -ch 1 polySurface1.f[0:1449];
select -cl ;}
我不得不在一開始就強調格式問題,你的代碼雖然沒錯,但代碼不只是給機器用的,還要給人看。以下是改過格式的代碼:
global proc myFullMoon(){
polyExtrudeFacet -ch 1 -kft 1
-pvx -3.926295877 -pvy -1.118658066 -pvz 4.649739981
-translateX -7.5 -ty 0 -tz 0
-rx 0 -ry 0 -rz 0
-scaleX -1 -sy 1 -sz 1
-ran 0 -divisions 1 -off 0 -ltz 0 -ws 0
-ltx 0 -lty 0
-lrx 0 -lry 0 -lrz 0
-lsx 1 -lsy 1 -lsz 1
-ldx 1 -ldy 0 -ldz 0
-w 0
-gx 0 -gy -1 -gz 0
-att 0
-mx 0 -my 0 -mz 0
polySurface1.f[0:682];
polyNormal -normalMode 0 -ch 1
polySurface1.f[0:1449];
polySmooth -mth 0 -dv 1 -c 1
-kb 1 -ksb 1 -khe 0 -kt 1
-sl 1 -dpe 1 -ps 0.1 -ro 1 -ch 1
polySurface1.f[0:1449];
select -cl ;
}
格式其實也沒有固定的寫法,總之,盡量寫得能讓人看清楚就行了。把這些代碼粘貼到記事本上,然后存成一個mel文件,注意文件名必須是"myFullMoon.mel"(腳本命令名 + ".mel"),然后把這個文件放到"我的文檔\e:\My Documents\maya\4.5(5.0)\scripts"目錄中。這樣當你每次啟動Maya時,"myFullMoon"命令就能像Maya的其它命令一樣工作了。
把Shelf上以前的mel圖標刪掉(就是把mel圖標用鼠標中鍵拖進Shelf最右邊的小垃圾桶里)。把"myFullMoon"這幾個字拖到Shelf上重新建立圖標。圖標上加幾個字(mFM),如圖。以后升級"myFullMoon"的時候圖標命令依舊,只要改"myFullMoon.mel"文件就行了。
"myFullMoon"現(xiàn)在存在一個很大的缺陷,就是只能對那個半邊臉(polySurface1)起作用,換個物體就不行了,要是程序里那個"polySurface1"能隨意改成其它多邊形物體就好了。
呵,要想好好,就要使用變量(variable)。
----------------------------------------
Maya的變量名稱必須以一個美元符號($)開頭,這是為了和物體名區(qū)別開。這很容易接受,如果可以不寫"$",Maya會以為你的"polySurface1"也是變量。不過忘寫美元符號是視金錢如糞土的mel初學者常犯的錯誤,我盼望大家少犯這種錯誤,不要辜負了Alia$Wavefront的一片苦心。
變量名稱中不能包含有空格或其它的特殊字符,不能用中文。變量名稱是大小寫敏感(Case Sensitive)的,不能有大小寫錯誤。mel命令和mel函數(shù)也是大小寫敏感的,同樣不能有大小寫錯誤。
下面把有關變量的一些知識詳細介紹一下:
------------------------------------------------------
下面列出了五種數(shù)據(jù)類型:
類型 含義 例子
int 整數(shù) 10 ,-5 ,0
float 小數(shù) 392.6 ,7.0 和-2.667
string 一個或多個字母 “What's up, chief?"
Vector 三個浮點數(shù) 《3 ,7.7 ,9.1 》
matrix 多個浮點數(shù)組 <<1.1, 2, 3; 6.7, 5, 4.9>>
在上面的類型中,除matrix(矩陣)類型外,都可以構成Array(數(shù)組)。一個數(shù)組是某種數(shù)據(jù)類型的多個數(shù)據(jù)的序列。例如,一個包含三個元素的數(shù)組就是一個整數(shù),接著一個整數(shù),再接著一個整數(shù)。用戶可以把矩陣考慮為一個包含多個浮點數(shù)組的數(shù)組,或是一個二維浮點數(shù)組。
------------------------------------------------------
定義變量和為變量賦值
定義變量就是指明變量屬于何種數(shù)據(jù)類型。為變量賦值就是給一個定義的變量以指定的數(shù)值。
下面的例子表明了如何使用一個步驟來定義和為變量賦值:
int $temp = 3 ;
float $Temp =222.222;
string $tEmp = “Heya kid.”;
vector $teMp = <<1,2.7,3.2>>;
matrix $temP[2][3]=<<4.5,1,0.2;-13,9911,0.007>>;
當用戶精確的定義矩陣時,用戶必須指明矩陣的行列數(shù)。下面的例子表明如何為整數(shù)、浮點、字符串和矢量數(shù)組賦值和定義它們。例子:數(shù)組的定義和賦值
int $Temp[5]={100, 1000, -70, 2, 9822};
float $TeMp[4]={ 43.3, -10.7, 0, 82.5};
string $TemP[3]={“Lord”,”Flies”,”cool brown fox2.”};
vector $tEmp[2]={<<0,0,0>>,<<0.01,-2,16>>};
如果一個變量被定義,但沒有為其賦值,則Maya 為其分配默認的數(shù)值0 ,而對字符串變量,Maya 為其分配兩個空引號。
------------------------------------------------------
Reserved words (保留字)
保留字是一些在MEL 中帶有固定含義的單詞。這些保留字可用于指定變量類型、邏輯控制,或描述一個數(shù)值。下面就是MEL 中的保留字。break case continue default do
else false float for global
if in int matrix no
off on proc return string
switch true vector while yes
Data type keywords (數(shù)據(jù)類型關鍵字)
int float vetor string matrix
Boolean constant keywords(布爾關鍵字)
yes no on off true false
Flow control keywords(流程控制關鍵字)
if else for while do in
break continue default switch case
其它的關鍵字
global return source catch alias proc
就像變量名稱,保留字是大小寫敏感的。因此,int 是整數(shù)類型,而Int 就不是。實際上,alias 、source和catch 也是保留字。因為它們使用起來,更像命令,它們沒有包含在上面的表中。
關于變量的數(shù)據(jù)類型(Data Types),還得從int講起。
int
int是整數(shù)(integer)的意思。例如:
int $i = 3;
有一種布爾類型的數(shù)據(jù),在mel中也用int表示。布爾數(shù)據(jù)只有兩個值,"是"或"否",mel用"1"或"0"來表示。因此,下面幾句代碼的作用是相同的。
int $bool = 1;
int $bool = yes;
int $bool = true;
int $bool = on;
與之對應的下面幾句代碼的作用也是相同的。
int $bool = 0;
int $bool = no;
int $bool = false;
int $bool = off;
float
float是小數(shù),也就是浮點數(shù)的意思。例如:
float $f = 3.3333;
浮點運算是3d運算的重點,你一定也注意到了Maya中的大部分物體屬性都是浮點數(shù)值。從表示顏色這一點就可以明顯地看出來。比如說紅色,在網頁中或平面軟件中的表示為FF0000(255, 0, 0)(整數(shù)),可在3d軟件中,紅色就成了(1.0000, 0.0000, 0.0000)(浮點數(shù))。
下面該講到字符串(string)了,今天的重點就是文字游戲。
"print"命令的用法:
"print"命令可以輸出任何變量的值。如:
string $s = "你好世界";
print $s;
=======================
結果為:你好世界
string
string是字符串的意思,變量$a是個字符串,它的值為"你好世界"。
字符串的值要用引號引起來。有些情況下你不打引號Maya也會猜出是字符串,要我把這些情況一一列舉可是件很麻煩的事,反正你只須知道是字符串打上引號肯定沒錯誤,而如果你不打引號讓Maya猜謎的話,Maya一旦猜不出就會有錯誤提示信息,你再根據(jù)此信息來修正。
轉義符(escape character)
引號里面的字如果也有引號,就把"變?yōu)閈"。和c語言一樣,\為mel的轉義符(escape character)。例如:
string $s = "你\"好\"世界";
print $s;
=======================
結果為:你"好"世界
除了引號以外,還有一些符號有時也要相應改變。比如把\變?yōu)閈\,把tab空格變?yōu)閈t,把換行符變?yōu)閈n或\r\n等。
數(shù)組(array)
一旦你對字符串有了一定的印象,我就可以講幾個字符處理的mel命令了。
不過在這之前,還必須要提一提數(shù)組(array)的概念。
一群變量放在一起可以組成一個數(shù)組,例如:
int $ia[5]={100, 1000, -70, 2, 9822};
你也可以先弄一個空數(shù)組,然后往里填數(shù)據(jù)。
注意,數(shù)組是從零開始的。!
例如:
string $sa[];
$sa[0] = "你好";
$sa[1] = "世";
$sa[2] = "界";
print $sa;
=======================
結果為:你好
世
界
下面來講幾個字符處理的mel命令,當然這幾個命令的出場順序要根據(jù)我們的實際需要來安排。這幾個命令分別為:
substring
tokenize
size
clear
match
substitute
會了這幾個命令,對于字符處理差不多就夠用了。
在講解之前,先把下面一行代碼在命令行執(zhí)行一下:
string $obj = "pSphere1.translateX";
=======================
結果為:pSphere1.translateX
"substring"命令的用法:
現(xiàn)在得到一個字符串$obj和它的值,可是這個值不是我們想要的。我們想要的只是這個物體的名稱("pSphere1"),而不包括它的屬性(".translateX")。怎樣從$obj中提取物體的名稱呢?方法很多,第一種方法是先指定你要提取的是"pSphere1.translateX"中的第幾個字符到第幾個字符。數(shù)數(shù)看,連猴子也能數(shù)出來,"pSphere1"是"pSphere1.translateX"中的第1個字符到第8個字符。好了,現(xiàn)在用"substring"命令:
substring $obj 1 8;
=======================
結果為:pSphere1
獲取命令的返回值
這個結果是"substring"命令的返回值,這一點我在第一課里曾提到過。要想把命令值的返回存到一個變量里,以便以后使用,有兩種方法:
// 第一種是用"`"的方法,比較常用。
string $objName = `substring $obj 1 8`;
=======================
結果為:pSphere1
// 第二種是用eval的方法,"eval"對于執(zhí)行字符串中的命令很有用。
string $objName = eval("substring $obj 1 8");
=======================
結果為:pSphere1
對于不需要標志的命令,還有第三種方法:
// 這種方法符合學過c語言的人的習慣,用于一些腳本命令很方便。
string $objName = substring($obj, 1, 8);
=======================
結果為:pSphere1
這三種方法的結果是一樣的,具體用哪種方法根據(jù)自己的習慣來決定。
"tokenize"命令的用法:
下面接著來講把"pSphere1"從"pSphere1.translateX"中提取出來的第二種方法,這種方法不用數(shù)數(shù),是一種很實用的方法。方法是從一個字符(".")的位置把字符串截成兩段,把這兩段存到一個字符串數(shù)組中。具體方法如下:
string $buffer[];
tokenize "pSphere1.translateX" "." $buffer;
string $objName = $buffer[0];
=======================
結果為:pSphere1
因為數(shù)組是從0開始的,$buffer[0]是"pSphere1.translateX"的第一段,它的值是"pSphere1",$buffer[1]是第二段,它的值是"translateX"。
"tokenize"是一個很有用的命令,我需要再舉幾個例子把它的用法講明白。
"tokenize"的返回值是把字符串分成的段數(shù),例如用"/"可以把"1/2/3/4/5"分成5份,"tokenize"的返回值就是5。如下:
string $buffer[];
int $numTokens = `tokenize "1/2/3/4/5" "/" $buffer`;
=======================
結果為:5
如果不指定分割字符,"tokenize"會根據(jù)一個默認的空格字符來分割。例如:
string $buffer[];
tokenize "How are you?" $buffer;
print $buffer;
=======================
結果為:How
are
you?
也就是: $buffer[0]=="How"; $buffer[1]=="are"; $buffer[2]=="you?";
"size"命令的用法:
"tokenize"的返回值可以表示字符串被分成的段數(shù),通過衡量這個字符串的大小也可以得知字符串被分成的段數(shù),這就要用到"size"命令:
string $buffer[];
tokenize "1/2/3/4/5" "/" $buffer;
int $numTokens = `size $buffer`;
=======================
結果為:5
"size"命令可以求出一個數(shù)組是由多少個元素組成,也可以求出一個字符串是由多少個字符組成。剛才說道猴子能數(shù)出"pSphere1"是由8個符組成,現(xiàn)在你不用數(shù)也會知道這一點,你知道我的意思當然不是指你就是猴子:
int $size = `size "pSphere1"`;
=======================
結果為:8
要注意的是一個中文字占用兩個字節(jié),"size"為2!
int $size = `size "中文"`;
=======================
結果為:4
"clear"命令的用法:
對于一些"size"很大的數(shù)組,會占用很多內存,你不用時要用"clear"把它清空。清空后它的"size"為0。
int $buffer[5]={1, 2, 3, 4, 5};
clear $buffer;
=======================
結果為:0
"match"命令的用法:
"match"是字符串中的查找功能,返回值是一個字符串。如果找到了,就返回要找的字符串,沒找到,就返回""。例如:
match "this" "this is a test";
=======================
結果為:this
match "that" "this is a test";
=======================
結果為:
"match"命令可以使用通配符,以下是使用規(guī)則:
. 代表任何一個單獨的字符
* 代表0個或多個字符
+ 代表1個或多個字符
^ 代表一行中第一個字符的位置
$ 代表一行中最后一個字符的位置
\ 轉義符(escape character). 把它寫在特殊的字符前面(如 '*')
[...] 代表指定范圍內的任意一個字符
(...) 用于把部分通配符表述組織在一起
例:
match "a*b*" "abbcc";
=======================
結果為:abb
match "a*b*" "bbccc";
=======================
結果為:bb
match "a+b+" "abbcc";
=======================
結果為:abb
match "^the" "the red fox";
=======================
結果為:the
match "fox$" "the red fox";
=======================
結果為:fox
match "[0-9]+" "sceneRender019.iff";
=======================
結果為:019
match "(abc)+" "123abcabc456";
=======================
結果為:abcabc
match("\\^.", "ab^c");
=======================
結果為:^c
"substitute"命令的用法:
"substitute"是字符串中的替換功能,返回值是一個字符串,返回替換后的結果。例如:
string $text = "ok?";
$text = `substitute "?" $text "!"`;
=======================
結果為:ok!
也可以通過此方法把不想要的字符去掉。例如:
string $text = "ok?";
$text = `substitute "?" $text ""`;
=======================
結果為:ok
下面是"substitute"使用通配符的方法:
string $test = "Hello ->there<-";
string $regularExpr = "->.*<-";
string $s1 = `substitute $regularExpr $test "Mel"`;
=======================
結果為:Hello Mel
字符串的聯(lián)合(unite)
最后講一下字符串的聯(lián)合(unite)。
字符串可以做加法,但不能做減法:
string $s1 = "你好";
string $s2 = "世界";
string $text = $s1 + $s2;
=======================
結果為:你好世界
字符串的加法跟數(shù)字的加法不同:
int $i = 1 + 2 + 3;
=======================
結果為:6
string $s = "1" + "2" + "3";
=======================
結果為:123
+=
以下兩種寫法的結果是相同的:
string $s = "你好";
$s = $s + "世界";
=======================
結果為:你好世界
string $s = "你好";
$s += "世界";
=======================
結果為:你好世界
添加注釋
為了增加mel程序的可讀性,你可以在你的mel代碼中添加注釋。注釋可起到解釋、提示或描述腳本的作用,程序執(zhí)行時會跳過這些注釋,執(zhí)行注釋以外的代碼。mel的注釋跟c語言是一樣的。
--------------------------------------------
單行注釋
如果要創(chuàng)建單行的注釋,輸入兩個反斜線(//),然后輸入注釋:
int $locator = 7; //Default locator number is lucky.
--------------------------------------------
多行注釋
如果要創(chuàng)建多行的注釋,輸入一個反斜線加一個星號(/*)。在注釋的結束,輸入一個星號加反斜線(*/)。
用戶可以在一行或多行的某部分中使用這種注釋。
/*This is an example of a
variable-line comment.*/
--------------------------------------------
注意,在表達式中不能使用多行注釋技術,表達式中只能使用單行注釋(//)。
上一課講到了獲取命令的返回值,有了這種方法,就可以繼續(xù)我們的"myFullMoon"了。我們可以把選中的物體名稱存入一個變量,再根據(jù)物體名稱對物體進行處理。
--------------------------------------------
獲取選中物體的名稱
"ls"命令的用法:
ls -sl的意思就是獲得選擇物體的名稱,要想對物體進行操作這是必然的一步。
我來具體演示一下方法,以便大家更好的掌握。
實例5:選擇物體清單
新建一個場景,創(chuàng)建一盞直射燈、一個多邊形球,一個多邊形方塊,一個Nurbs平面,一個Nurbs圓柱,一個細分方塊。如圖。
選中所有物體,然后執(zhí)行"ls -sl"命令:
ls -sl;
=======================
結果為:pSphere1 pCube1 nurbsCylinder1 subdivCube1 directionalLight1 nurbsPlane1
下面編個程序以清單的形式輸出物體名稱:
global proc printObjNameSheet()
{
string $objects[] = `ls -sl`;
int $num = `size $objects`;
string $objNameSheet;
for($i=0; $i<$num; $i++)
{
$objNameSheet += $i+1;
$objNameSheet += ")";
$objNameSheet += $objects[ $i ];
$objNameSheet += "\n";
}
string $output = "\n*******************\n";
$output += "一共選擇了";
$output += $num; // 注意$num不能寫成"$num"
$output += "個物體\n";
$output += "它們分別是:\n";
$output += $objNameSheet;
print $output;
}
printObjNameSheet;
--------------------------------------------
把上面的代碼拉到Shelf上,隨便選擇幾個物體,然后執(zhí)行這些代碼,就會看到mel歷史窗中輸出的物體名稱清單。
由于上一課已經講過了字符處理的方法,相信你上面的大部分代碼都看得懂。不過,"for($i=0; $i<$num; $i++)"這種語法以前沒講過,現(xiàn)在來具體講講。
$i++
$i++的意思就是$i=$i+1或$i+=1。
for語句
for語句是一個典型的循環(huán)語句。我下面具體分析一下。
string $objects[] = `ls -sl`;
int $num = `size $objects`;
如圖,已知我們選中了三個物體,物體名分別存入了$objects數(shù)組中。
跟據(jù)我們所知的,用代數(shù)的方法把所知的值代入變量中,就有了以下結果:
$num == 3;
$objects[0] == nurbsPlane1;
$objects[1] == directionalLight1;
$objects[2] == pSphere1;
--------------------------------------------
string $objNameSheet;
for($i=0; $i<$num; $i++)
{
$objNameSheet += $i+1;
$objNameSheet += ")";
$objNameSheet += $objects&i;;
$objNameSheet += "\n";
}
繼續(xù)用代數(shù)的方法:
//當$i=0時:
$objNameSheet += 0+1;
//這時:$objNameSheet == "1"
$objNameSheet += ")";
//這時:$objNameSheet == "1)"
$objNameSheet += $objects[0];
//這時:$objNameSheet == "1)nurbsPlane1"
$objNameSheet += "\n";
//這時:$objNameSheet == "1)nurbsPlane1\n"
//當$i=1時:
$objNameSheet += 1+1;
//這時:$objNameSheet == "1)nurbsPlane1\n2"
$objNameSheet += ")";
//這時:$objNameSheet == "1)nurbsPlane1\n2)"
$objNameSheet += $objects[1];
//這時:$objNameSheet == "1)nurbsPlane1\n2)directionalLight1"
$objNameSheet += "\n";
//這時:$objNameSheet == "1)nurbsPlane1\n2)directionalLight1\n"
//當$i=2時:
$objNameSheet += 2+1;
//這時:$objNameSheet == "1)nurbsPlane1\n2)directionalLight1\n3"
$objNameSheet += ")";
//這時:$objNameSheet == "1)nurbsPlane1\n2)directionalLight1\n3)"
$objNameSheet += $objects[2];
//這時:$objNameSheet == "1)nurbsPlane1\n2)directionalLight1\n3)pSphere1"
$objNameSheet += "\n";
//這時:$objNameSheet == "1)nurbsPlane1\n2)directionalLight1\n3)pSphere1\n"
//當$i=3時:
// $i = $num 結束循環(huán)
--------------------------------------------
因為mel不能設斷點進行調試分析,我只好用這種笨辦法來分析了,但愿你能看懂。
注意for語句分號的位置,初學者常會在這里出錯,不要把分號寫在那個小括號的后面。大括號后面也不要寫分號。
關于獲取選中物體的名稱,再舉一個例子。例如,你想把所有選中的物體沿x軸移動5:最簡單的方法當然是: move -r -x 5;
不過,為了演示ls -sl的用法,請看下面的代碼:
string $objects[] = `ls -sl`; // 獲取選擇物體名稱
int $num = `size $objects`; // 獲得物體的數(shù)量
for($i=0; $i<$num; $i++)
{
select -r $object;
move -r -x 5;
}
in
還有一種方法可以達到同樣的效果:
string $objects[] = `ls -sl`; // 獲取選擇物體名稱
for($object in $objects)
{
select -r $object;
move -r -x 5;
}
"filterExpand"命令的用法:
不過"ls -sl"是獲得所有選擇物體的名稱,如果你只想對選擇物體中的多邊形物體進行操作,最好的方法是不用"ls",而用"filterExpand"。
// -sm 12是多邊形物體, -sm 10是Nurbs表面,其它的請
// 參看filterExpand命令的幫助文檔(help -doc filterExpand;)。
string $polygons[] = `filterExpand -sm 12`;
for($polygon in $polygons)
{
select -r $polygon;
move -r -x 5;
}
--------------------------------------------
[注]
用"ls"命令也有辦法達到與"filterExpand"類似的效果,為了減輕你們的學習復擔,我就不多講了。用"filterExpand"更簡便一些。
選中剛才那個場景的全部物體,下面的代碼分類輸出選擇物體的名稱:
global proc printObjNameSheet()
{
int $num;
string $polygons[] = `filterExpand -sm 12`;
string $nurbs[] = `filterExpand -sm 10`;
string $polyNNurbs[] = `filterExpand -sm 10 -sm 12`;
string $output = "\n*******************\n";
$output += "一共選擇了";
$num = `size $polygons`;
$output += $num + "個多邊形物體\n它們是:\n";
print $output;
print $polygons;
$output = "\n一共選擇了";
$num = `size $nurbs`;
$output += $num + "個Nurbs物體\n它們是:\n";
print $output;
print $nurbs;
$output = "\n選擇的物體中Nurbs物體和多邊形物體共有";
$num = `size $polyNNurbs`;
$output += $num + "個\n它們是:\n";
print $output;
print $polyNNurbs;
}
printObjNameSheet;
如何判斷選擇的是面,還是點,還是邊呢?如下:
// 多邊形點
string $vertices[] = `filterExpand -ex 1 -sm 31`;
// 多邊形邊
string $edges[] = `filterExpand -ex 1 -sm 32`;
// 多邊形面
string $faces[] = `filterExpand -ex 1 -sm 34`;
// 如果框選很多,要想獲取其中的nurbs和polygon物體,-sm可以出現(xiàn)不止一次。
string $polygons[] = `filterExpand -sm 10 -sm 12`;
選擇一些多邊形物體的面,下面的代碼輸出面的名稱:
global proc printObjNameSheet()
{
int $num;
string $faces[] = `filterExpand -sm 34`;
string $output = "\n*******************\n";
$output += "一共選擇了";
$num = `size $faces`;
$output += $num + "個面\n它們是:\n";
print $output;
print $faces;
}
printObjNameSheet;
"filterExpand -ex/expand"的用法
關于filterExpand -ex/expand的用法,如果懶得看help,看下面的吧:
Object Type / Mask
Handle 0
Nurbs Curves 9
Nurbs Surfaces 10
Nurbs Curves On Surface 11
Polygon 12
Locator XYZ 22
Orientation Locator 23
Locator UV 24
Control Vertices (CVs) 28
Edit Points 30
Polygon Vertices 31
Polygon Edges 32
Polygon Face 34
Polygon UVs 35
Subdivision Mesh Points 36
Subdivision Mesh Edges 37
Subdivision Mesh Faces 38
Curve Parameter Points 39
Curve Knot 40
Surface Parameter Points 41
Surface Knot 42
Surface Range 43
Trim Surface Edge 44
Surface Isoparms 45
Lattice Points 46
Particles 47
Scale Pivots 49
Rotate Pivots 50
Select Handles 51
Subdivision Surface 68
Polygon Vertex Face 70
NURBS Surface Face 72
Subdivision Mesh UVs 73
前面的準備就緒,現(xiàn)在來繼續(xù)myFullMoon的新版。
實例6:myFullMoon3.0
前面提到過升級"myFullMoon"的時候圖標命令依舊,只要改"myFullMoon.mel"文件就行了。
現(xiàn)在打開"myFullMoon.mel"文件,用下面的代碼替換掉原來的代碼:
// myFullMoon3.0
global proc myFullMoon()
{
string $polygons[] = `filterExpand -sm 12`;
// 如果選擇了多個多邊形物體,只對第一個進行操作
string $polygon = $polygons[0];
select -r $polygon;
ConvertSelectionToFaces; // 轉換為選擇面模式
polyExtrudeFacet -ch 1 -kft 1
-pvx -3.926295877 -pvy -1.118658066 -pvz 4.649739981
-translateX -7.5 -ty 0 -tz 0
-rx 0 -ry 0 -rz 0
-scaleX -1 -sy 1 -sz 1
-ran 0 -divisions 1 -off 0 -ltz 0 -ws 0
-ltx 0 -lty 0
-lrx 0 -lry 0 -lrz 0
-lsx 1 -lsy 1 -lsz 1
-ldx 1 -ldy 0 -ldz 0
-w 0
-gx 0 -gy -1 -gz 0
-att 0
-mx 0 -my 0 -mz 0;
polyNormal -normalMode 0 -ch 1;
polySmooth -mth 0 -dv 1 -c 1
-kb 1 -ksb 1 -khe 0 -kt 1
-sl 1 -dpe 1 -ps 0.1 -ro 1 -ch 1;
select -cl ;
}
現(xiàn)在這些代碼還不會生效,直到你關閉再重新打開Maya。要想讓mel文件里的代碼立即生效,需要Source Script。有三種方法可以Source Script:
1. 選擇mel編輯器菜單File->Source Script...,打開"myFullMoon.mel"文件。
2. 把"myFullMoon.mel"文件直接拖放到Maya視圖窗口中。
3. 使用"source"命令。
"source"命令的用法:
通過"source"命令可以執(zhí)行mel文件中的代碼。例如:
source "c:/temp/myFullMoon.mel";
或:
source "c:\\temp\\myFullMoon.mel";
如果你的mel文件是放在Maya的Script目錄(我的文檔\e:\My Documents\maya\4.5(5.0)\scripts),"source"命令可以不指定路徑:
source "myFullMoon.mel";
Source了"myFullMoon.mel"之后,就可以試試了。
選中以前那個半邊臉模型,給它改個名字,再按"myFullMoon"命令的Shelf圖標,可以看到新代碼起作用了。
當然現(xiàn)在又來了新的問題,還記得以前用的模型寬度是7.5吧,每個模型的寬度是不同的,模型寬度也應該是個變量。要想獲得模型寬度,需要用到邊界框(Bounding Box),獲取邊界框是以后要做的事情,我把它放到建模部分講。
前面講了很多東西,你可能還要慢慢消化。不過如果你真的能把前面講的都理解了,相信你的mel已經入門了。從下一課開始,我來講用mel編寫界面的方法。
Maya幫助文檔中把編寫界面的mel命令稱為ELF命令。
"Most ELF commands create and modify UI elements. " [Maya Help docs]
大部分ELF命令創(chuàng)建和修改UI元素。
"ELF","UI"這么多縮寫真的很讓人感到厭煩。
"ELF" - Extended Layer Framework(擴展層框架)
這個詞匯有點多余,我會盡可能少提到它。
"UI" - User Interface(用戶界面)
用戶界面的含義相信大多數(shù)人都知道,用戶界面又稱人機界面,實現(xiàn)用戶與計算機之間得通信,以控制計算機或進行用戶和計算機之間得數(shù)據(jù)傳送得系統(tǒng)部件。具體的說,我們平常使用的窗口、菜單、按鈕等都是用戶界面的元素。
"界面"
我們平常所說的界面都是指“用戶界面”,因為程序員是少數(shù)派,用戶才是上帝,所以用戶們從來不怕直接說界面會被別人誤解為“應用程序設計界面”。我后面也把用戶界面簡稱為界面,希望不要給我糾正。
[注]
"API" - Application Programming Interface
有幾種不同譯法:應用程序界面,應用程序設計界面,應用程序接口。這是一種專門給程序員使用的界面或接口,例如Maya API。
現(xiàn)在來編寫一個窗口。用mel編寫一個窗口就像創(chuàng)建一個球或者方塊那么簡單。
"window"命令 (創(chuàng)建窗口)
在命令行輸入"window"(打字的候要小心,不要讓視窗變成寡婦window-widow)然后回車,一個窗口就創(chuàng)建好了。不要因為沒看到有什么變化而認為我在欺騙你,"window"命令創(chuàng)建的窗口在默認情況下是不可見的,換句話說,"window"命令的"-vis/-visible"的默認參數(shù)值為0(或false),也就是說直接輸入"window"命令和輸入"window -visible false"是等效的。
"showWindow"命令 (顯示窗口)
要想創(chuàng)建可以看得到的窗口,有兩種方法。一種方法是在創(chuàng)建窗口時用"window -visible true",另一種方法是創(chuàng)建窗口后執(zhí)行"showWindow"命令。這時我會推薦你用第二種方法,"showWindow"的好處是既顯示窗口,又把這個窗口推到所有窗口的最前面。
好,現(xiàn)在就在命令行輸入"showWindow"并回車,一個窗口便顯示了出來。
唯一的命名
因為你可以創(chuàng)建很多個窗口,因此如果想完全控制每個窗口,就必須給每個窗口指定一個唯一的命名。執(zhí)行下面兩句命令:
-----------------------------------------
window -t "我的測試窗" myTestWin;
showWindow myTestWin;
-----------------------------------------
這里"myTestWin"就是窗口的名字。
"-t/-title"的用途想必不用我多作解釋了吧?
當一個窗口創(chuàng)建時,會另人想到另一個問題,就是怎么把它銷毀(刪除)。點窗口右上角標題欄上的那個"×"可以關閉窗口,然而關閉窗口卻蘊含著兩層意思。在大多數(shù)情況下,關閉窗口可以銷毀窗口,有少數(shù)情況,關閉窗口以后窗口只是處于不可見狀態(tài),窗口并沒被銷毀,你隨時可以用"showWindow"命令來顯示它。用"-ret/-retain"標志創(chuàng)建的窗口就屬于這種少數(shù)情況。
"deleteUI"命令 (銷毀界面元素)
要想讓徹底銷毀窗口,使用"deleteUI"命令。"deleteUI"可以銷毀任何界面元素,包括布局、控件和菜單。在命令行輸入"deleteUI myTestWin"并回車,"myTestWin"就被銷毀了。
為方便以后對于這個窗口代碼的調用,我們來編寫一個函數(shù)。
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) // 如果myTestWin窗口已經存在了
deleteUI myTestWin; // 就把myTestWin銷毀
window -t "我的測試窗" myTestWin;
showWindow myTestWin;
}
myTest;
"if"語句
這里用到了if語句,我覺得if語句是最容易理解的語句,把"if"直譯成"如果"就可以理解。if后面那個小括號里的代碼表示的只能是一個布爾值,是(1, true)或否(0, false)。當表示否時,可以在前面加一個嘆號,"!"可以直譯成"不"。例如:
if(!`window -ex myTestWin`) // 表示如果myTestWin窗口不存在
也就是:
if(`window -ex myTestWin` == false) // `window -ex myTestWin`的返回值為0
現(xiàn)在只是編寫了一個空的窗口,要是想往窗口中添加一些按鈕或者文本框什么的怎么辦呢?
控件,布局
按鈕或者文本框這些東東統(tǒng)稱控件(control),mel擺放控件的特色是使用布局(layout)。在不同的布局中,控件擺放的方法不同,比如豎布局(columnLayout)中的控件都是一個挨一個豎著擺放的,而橫布局(rowLayout)中的控件都是橫著排列的。
"window"命令之后必須有一個布局,這個布局作為窗口的主布局,其它控件或布局都放置在這個主布局之中。
層級關系
由于布局之中既可以放布局又可以放控件,因此布局與控件,布局與布局之間構成了一種層級關系(父子關系)。例如,布局columnLayout2在布局columnLayout1之中,columnLayout1稱為columnLayout2的父布局(Parents),columnLayout2稱為columnLayout1的子布局(Children)。
"setParent"命令 (指定當前布局)
默認的,剛剛創(chuàng)建的布局為當前布局,新建的布局或控件都建在當前布局中。要想使以前創(chuàng)建的布局成為當前布局,使用setParent命令:"setParent 布局名"。要想使上一級布局(父布局)成為當前布局,使用"setParent .."。
"columnLayout"命令 (豎布局)
對于不在乎界面美觀的人來說,學會一個豎布局差不多就足夠了。
豎布局中的控件都是一個挨一個豎著擺放的,請看下面的代碼:
-------------------------------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`)
deleteUI myTestWin;
window -t "我的測試窗" myTestWin;
columnLayout -adj 1; // 主布局
text -l "圓錐名稱";
// 和控制窗口一樣,要想控制控件,也要給控件一個唯一的命名。
// 這里文本框(textField)控件的名字為"myConeNameFld"
textField -tx "cone" myConeNameFld;
button -l "創(chuàng)建圓錐" -c "cone";
button -l "刪除" -c "delete";
showWindow myTestWin;
}
myTest;
-------------------------------------------------------------
執(zhí)行這段代碼,不要關掉生成的窗口。
"text"命令 (靜態(tài)文本)
"textField"命令 (文本框)
"button"命令 (按鈕)
這里用到三個關于界面的mel命令:"text","textField","button"。
根據(jù)生成的窗口你就可以猜到這三個命令的意思。
"text"為靜態(tài)文本,"textField"為文本框,"button"為按鈕。
"-l/-label"可能是界面命令中最明顯、最容易理解的標志了,意思是界面元素的標簽。
"-tx/-text"是文本框中的文字。
"-c/-command"是要執(zhí)行的mel命令,注意這個命令是一個字符串,一般要用引號引起來。
"-adj"決定是否自動調整布局內控件的寬度。
"-q/-query"(查詢)和"-e/-edit"(編輯)標志
這是兩個非常特殊又非常有用的標志,所有的界面命令都用到這兩個標志。
"-q/-query" 表示查詢的意思,你可以通過它獲得某一標志的數(shù)值。
"-e/-edit" 表示編輯的意思,你可以通過它修改某一標志的數(shù)值。
下面舉例說明它們的用法:
在剛才創(chuàng)建的窗口的文本框中隨便輸入幾個字,在命令行輸入"textField -q -tx myConeNameFld"并回車,可以在輸出窗口中看到文本框中的字。
在命令行輸入"textField -e -tx "change" myConeNameFld"并回車,文本框中的字就變成了"change"。前面說過"deleteUI"可以銷毀任何界面元素,在命令行輸入"deleteUI myConeNameFld"并回車,文本框消失了。
獲取本框中的文字
編寫按鈕命令
要想創(chuàng)建的圓錐根據(jù)本框中的文字來命名,使用下面的代碼:
-----------------------------------------
// 先編寫一個函數(shù)(createCone),然后在按鈕命令中調用這個函數(shù)。
global proc createCone()
{
string $name = `textField -q -tx myConeNameFld`; // 獲取文本框中的文字
cone -name $name; // 用文本框中的文字為名稱創(chuàng)建圓錐
}
global proc myTest()
{
if(`window -ex myTestWin`)
deleteUI myTestWin;
window -t "豎布局(columnLayout)測試窗" myTestWin;
columnLayout -adj 1;
text -l "圓錐名稱";
textField -tx "cone" myConeNameFld;
button -l "創(chuàng)建圓錐" -c "createCone"; // 按鈕命令中調用createCone函數(shù)
button -l "刪除" -c "delete";
showWindow myTestWin;
}
myTest;
-----------------------------------------
改一下文本框中的字,按"創(chuàng)建圓錐"按鈕,這時創(chuàng)建的圓錐是按照文本框中的文字命名的。注意,對物體命名時不能使用標點符號,不能使用大多數(shù)的中文字,具體原因在后面講到如何用mel判斷物體名稱是否合法時再解釋。
下面講幾個常用布局:
"rowColumnLayout"命令 (橫豎布局)
控件橫豎排列的布局:
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "橫豎布局(rowColumnLayout)測試窗" myTestWin;
rowColumnLayout -nc 3 -cw 1 60 -cw 2 80 -cw 3 100; // 橫豎布局
button; button; textField;
button; textField; button;
button;
showWindow myTestWin;
}
myTest;
-----------------------------------------
"-nc/numberOfColumns"指定一共有幾列。
"-cw/columnWidth"指定某一列的寬度。
"-cw 1 60"第一列的寬度為60像素。
"-cw 2 80"第二列的寬度為80像素。以此類推。
"scrollLayout"命令 (滾動布局)
滾動布局中只能放布局,不能放控件。
滾動布局可以給它的子布局加上滾動條。
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "滾動布局(scrollLayout)測試窗" myTestWin;
scrollLayout;
columnLayout;
button;button;button;button;button;
button;button;button;button;button;
showWindow myTestWin;
}
myTest;
"frameLayout"命令 (邊框布局)
邊框布局中只能放布局,不能放控件。
下面的代碼提供幾種不同風格的邊框:
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "邊框布局(frameLayout)測試窗" myTestWin;
columnLayout -adjustableColumn true;
// 第一種:可折疊邊框
// 你熟悉的Attribute Editor和Tool Settings等窗口就是用的這種邊框
frameLayout -label "Collapsable" -collapsable on -borderStyle "etchedIn"; // 可折疊
columnLayout;
text -l "-cl/collapse 0 -折疊";
text -l "-cl/collapse 1 -展開";
setParent ..;
setParent ..;
// 其它幾種標簽在邊框線的位置不同,邊框線的風格不同,如圖。
// 第二種:
frameLayout -label "Buttons" -labelAlign "top" -borderStyle "in";
columnLayout;
button; button;
setParent ..;
setParent ..;
// 第三種:
frameLayout -label "Scroll Bars" -labelAlign "center" -borderStyle "out";
columnLayout;
intSlider; intSlider;
setParent ..;
setParent ..;
// 第四種:
frameLayout -label "Fields" -labelAlign "center" -borderStyle "etchedIn";
columnLayout;
intField; intField;
setParent ..;
setParent ..;
// 第五種:
frameLayout -label "Check Boxes" -labelAlign "bottom" -borderStyle "etchedOut";
columnLayout;
checkBox; checkBox;
setParent ..;
setParent ..;
showWindow myTestWin;
}
myTest;
-----------------------------------------
"-cll/collapsable"邊框是否可折疊。
"-cl/collapse"如果邊框可折疊,現(xiàn)在是否折疊。
"-bs/borderStyle"邊框線的風格,可用值為:"in", "out", "etchedIn", 或 "etchedOut"。
"labelAlign"邊框標簽的對齊方式,可用值為:are "top", "center", 或 "bottom"。
"paneLayout"命令 (窗格布局)
窗格布局一個很明顯的特點就是有分隔線分隔窗格面板,你可以移動分隔線來調整窗格面板的大小。Maya的四視圖就是用了這個布局的,還有mel編輯器也是。
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "窗格布局(paneLayout)測試窗" myTestWin;
paneLayout -configuration "quad";
button;
textScrollList -append "one" -append "two" -append "three";
scrollField;
scrollLayout;
columnLayout;
button; button; button;
showWindow myTestWin;
}
myTest;
-----------------------------------------
"-cn/configuration"pane的組合方式,可用值為:"single", "horizontal2", "vertical2", "horizontal3", "vertical3", "top3", "left3", "bottom3", "right3", "horizontal4", "vertical4", "top4", "left4", "bottom4", "right4", "quad"。
"tabLayout"命令 (標簽布局)
標簽布局中只能放布局,不能放控件。標簽布局中可以放多個并列的子布局,當鼠標點到某一標簽時,就顯示與此標簽對應的子布局。對于控件很多的面板,標簽布局無疑是最好的選擇。
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "標簽布局(tabLayout)測試窗" myTestWin;
string $tabs = `tabLayout`;
string $child1 = `columnLayout -adj 1`; // 標簽1
button; button; button;
setParent ..;
string $child2 = `rowColumnLayout -numberOfColumns 2`; // "標簽2"
button; button; button;
setParent ..;
tabLayout -edit
-tabLabel $child1 "標簽1" -tabLabel $child2 "標簽2"
$tabs;
showWindow myTestWin;
}
myTest;
-----------------------------------------
string $tabs = `tabLayout`;
這句的意思獲取tabLayout命令的返回值,存到變量$tabs里。
前面說過界面元素要有一個唯一的命名,如果你沒有給它命名,Maya會自動給它一個命名。界面命令的返回值就是界面元素的名稱,因此用獲取命令返回值的方法就可以獲得界面元素的名稱。(string $變量 = `界面命令`;)
string $child1 = `columnLayout -adj 1`;
這句也是同樣的道理。
"formLayout"命令 (表單布局)
表單布局是最強大、最復雜、最具有可塑性的布局。這種布局可以隨意把控件放到任何你想要的地方,也可以根據(jù)窗口的縮放即時縮放指定的控件。如果你很注重界面舒適美觀的話,它就是你必然的選擇。
-----------------------------------------
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "表單布局(formLayout)測試窗" myTestWin;
// 創(chuàng)建表單布局和布局中的控件
string $form = `formLayout`;
string $b1 = `button -label "按鈕1"`;
string $b2 = `button -label "按鈕2"`;
string $b3 = `button -label "按鈕3"`;
// 分別描述每個按鈕的上下左右位置
formLayout -edit
// 按鈕1
-attachForm $b1 "top" 5 // 頂部距離表單5個像素
-attachForm $b1 "left" 5 // 左邊距離表單5個像素
-attachForm $b1 "bottom" 5 // 底部距離表單5個像素
-attachPosition $b1 "right" 0 75 // 右邊距離表單的75%
// 按鈕2
-attachForm $b2 "top" 5 // 頂部距離表單5個像素
-attachControl $b2 "left" 5 $b1 // 左邊距離"按鈕1"5個像素
-attachNone $b2 "bottom" // 底部為默認值
-attachForm $b2 "right" 5 // 右邊距離表單5個像素
// 按鈕3
-attachControl $b3 "top" 5 $b2 // 頂部距離"按鈕2"5個像素
-attachControl $b3 "left" 5 $b1 // 左邊距離"按鈕1"5個像素
-attachNone $b3 "bottom" // 底部為默認值
-attachForm $b3 "right" 5 // 右邊距離表單5個像素
$form;
showWindow myTestWin;
}
myTest;
-----------------------------------------
當你拉大或縮小這個窗口時,"按鈕1"的寬度總是占整個窗口的75%。
編寫表單布局一般分為兩步:
1.創(chuàng)建表單布局和布局中的控件。
2.分別描述每個按鈕的上下左右位置。
"-af/attachForm" 距離表單多少個像素。
"-ac/attachControl" 距離控件多少個像素。
"-ap/attachPosition" 距離表單的幾(-numberOfDivisions)分之幾多少個像素。
"-nd/numberOfDivisions" 把表單分成多少份,好用于"-ap/attachPosition"。默認值為100。
關于表單布局,再舉兩個例子:
-----------------------------------------
// 這是一種常用的按鈕擺放方法
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "表單布局1(formLayout)測試窗" myTestWin;
string $form = `formLayout -numberOfDivisions 3`; // 分3份
// "-horizontalScrollBarThickness 0"無橫向滾動條
string $scroll = `scrollLayout -horizontalScrollBarThickness 0`;
columnLayout;
button; button; button;button; button;
button; button; button;button; button;
setParent ..;
setParent ..;
string $sep = `separator`;
string $b1 = `button -label "按鈕1"`;
string $b2 = `button -label "按鈕2"`;
string $b3 = `button -label "按鈕3"`;
formLayout -edit
// 滾動布局
-attachForm $scroll "top" 4 // 頂部距離表單4個像素
-attachForm $scroll "left" 4 // 左邊距離表單4個像素
-attachControl $scroll "bottom" 4 $sep // 底部距離"分隔線"4個像素
-attachForm $scroll "right" 4 // 右邊距離表單4個像素
// 分隔線
-attachNone $sep "top" // 頂部為默認值
-attachForm $sep "left" 2 // 左邊距離表單2個像素
-attachControl $sep "bottom" 4 $b1 // 底部距離"按鈕1"4個像素
-attachForm $sep "right" 2 // 右邊距離表單2個像素
// 按鈕1
-attachNone $b1 "top" // 頂部為默認值
-attachForm $b1 "left" 4 // 左邊距離表單4個像素
-attachForm $b1 "bottom" 4 // 底部距離表單4個像素
-attachPosition $b1 "right" 2 1 // 右邊距離表單的1/3處2個像素
// 按鈕2
-attachNone $b2 "top" // 頂部為默認值
-attachControl $b2 "left" 4 $b1 // 左邊距離"按鈕1"表單4個像素
-attachForm $b2 "bottom" 4 // 底部距離表單4個像素
-attachPosition $b2 "right" 2 2 // 右邊距離表單的2/3處2個像素
// 按鈕3
-attachNone $b3 "top" // 頂部為默認值
-attachControl $b3 "left" 4 $b2 // 左邊距離"按鈕2"4個像素
-attachForm $b3 "bottom" 4 // 底部距離表單4個像素
-attachForm $b3 "right" 4 // 右邊距離表單4個像素
$form;
showWindow myTestWin;
}
myTest;
-----------------------------------------
// 隨意擺放控件的位置
global proc myTest()
{
if(`window -ex myTestWin`) deleteUI myTestWin;
window -t "表單布局2(formLayout)測試窗" myTestWin;
string $form = `formLayout`;
string $b1 = `button -l "按鈕1"`;
string $b2 = `button -l "按鈕2"`;
string $b3 = `button -l "按鈕3"`;
formLayout -e
-af $b1 "top" 1
-af $b1 "left" 5
-af $b2 "top" 19
-af $b2 "left" 132
-af $b3 "top" 46
-af $b3 "left" 40
$form;
showWindow myTestWin;
}
myTest;
"rowLayout"命令 (橫布局)
橫布局與橫豎布局有些相似,只不過控件只能橫著擺而已。
-----------------------------------------
window;
rowLayout -numberOfColumns 3
-columnWidth3 80 75 150
-adjustableColumn 2
-columnAlign 1 "right"
-columnAttach 1 "both" 0
-columnAttach 2 "both" 0
-columnAttach 3 "both" 0;
text;
intField;
intSlider;
showWindow;
-----------------------------------------
"gridLayout"命令 (格子布局)
格子布局與橫豎布局有些相似,受格子的影響,每個的控件大小相同。
-----------------------------------------
window;
gridLayout -numberOfColumns 2 -cellWidthHeight 50 50;
button; button; button; button;
button; button; button;
showWindow;
-----------------------------------------
"menuBarLayout"命令 (菜單條布局)
用于一個窗口放多個菜單條的情況。
-----------------------------------------
string $window = `window`;
columnLayout -adjustableColumn true;
// Create first menu bar layout.
string $menuBarLayout = `menuBarLayout`;
menu -label "File";
menuItem -label "New";
menuItem -label "Open";
menuItem -label "Close";
menu -label "Help" -helpMenu true;
menuItem -label "About...";
columnLayout;
button -label "Add Menu"
-command ("menu -parent " + $menuBarLayout + "; menuItem");
setParent ..;
setParent ..;
separator -height 10 -style "none";
// Create a second menu bar layout.
//
menuBarLayout;
menu -label "Edit";
menuItem -label "Cut";
menuItem -label "Copy";
menuItem -label "Paste";
menu -label "View";
menuItem -label "Fonts...";
menuItem -label "Colors...";
columnLayout;
text -label "Add some controls here.";
setParent ..;
setParent ..;
showWindow $window;
-----------------------------------------
"shelfLayout"命令 (工具架布局)
放置shelf按鈕的布局,支持鼠標中鍵拖放代碼。
-----------------------------------------
window;
tabLayout;
shelfLayout Anim;
setParent ..;
shelfLayout Render;
setParent ..;
shelfLayout Misc;
setParent ..;
showWindow;
-----------------------------------------
"shelfTabLayout"命令 (工具架標簽布局)
-----------------------------------------
可以使工具架上多一個垃圾桶圖標,以便刪除不用的shelf按鈕。
window;
shelfTabLayout
-image "smallTrash.xpm"
-imageVisible true mainShelfTab;
shelfLayout Dynamics;
setParent ..;
shelfLayout Rendering;
setParent ..;
shelfLayout Animation;
setParent ..;
showWindow;
共有的標志(一):
如果你讀過mel命令的幫助文檔,你會發(fā)現(xiàn)所有的界面命令都具有一些共同的標志。比如,"-q"、"-e"就是所有界面命令都具有的標志。我把這些標志介紹一下,這樣無論任何界面(布局或控件)命令,只要用到這些標志時,你們就都知道怎么去用了。
"-q/-query"(查詢)
表示查詢的意思,你可以通過它獲得某一標志的數(shù)值。(前面講過了)
"-e/-edit"(編輯)
表示編輯的意思,你可以通過它修改某一標志的數(shù)值。(前面講過了)
"-ex/exists"(是否存在)
通過"-ex"可以得知一個界面元素是否存在。
例如前面講過的"if(`window -ex myTestWin`)"。
"-dt/defineTemplate" (定義模板)
"-ut/useTemplate" (使用模板)
一般我們沒必要定義模板,不過有幾個Maya已經定義好的模板有時會用到,我以后再講。
"-p/parent" (指定父級界面元素)
指定父級界面元素(布局或菜單),平常我們用"setParent"命令來解決這個問題,所以"-p"很少用到。
"-en/enable" (是否激活)
我們經?吹揭恍┎藛位虬粹o上的字是灰色的,表示它們現(xiàn)在不能用。
使用"-en"就可以讓你的界面元素上的字變灰,使它們不能用。
例如"button -e -en off myButton1"就是讓按鈕myButton1變得不能用。
而"button -e -en on myButton1"就是讓按鈕myButton1恢復活力。
"-w/width" (寬度)
界面元素的寬度為多少像素。
你會發(fā)現(xiàn)使用這個標志時經常不起作用。
比如如果在布局命令中指定了里面控件的寬度,再給布局中這些控件指定寬度就不起作用了。
窗口的寬度只在第一次創(chuàng)建時有用,以后創(chuàng)建會使用預置中的尺寸。預置中的尺寸就是窗口關閉時的大小,這個尺寸記錄在預置文件(windowPrefs.mel)中,可以通過"windowPref"命令去除窗口的預置尺寸,方法是"windowPref -remove 窗口名;"
"-h/height" (高度)
界面元素的高度為多少像素。
用法同"-w/width",例如"button -w 32 -h 32;"。
"-vis/visible" (是否可見)
指定界面元素是否可見。
前面講過"window -visible off"的問題。其它界面元素也可以通過"-visible off"暫時隱藏,想要顯示出來,就用"-visible on"。
"-io/isObscured" (是否看不到)
查詢界面元素是否不可見。你肯定會問"-io"和"-vis"的區(qū)別。"-io"只能用于查詢(-q)模式,般查詢結果正好與"-vis"的查詢結果相反。不過"-io"的不可見包括窗口最小化、父級界面元素隱藏等造成的不可見因素。
"-m/manage" (可控制)
指定界面元素是否可見。跟"-vis"沒什么區(qū)別。
"-ann/annotation" (注釋)
給界面元素注釋。注釋可以在幫助欄顯示,也可以讓鼠標在界面元素上停一會,在彈出式的淡黃底色注釋條看到。
"-bgc/backgroundColor" (底色)
要想編寫彩色窗口就要用到這個標志。
還有一些共有標志留到以后講。
布局到此已經全講完了,接下來講控件。
前面提到過一些控件:
"text"命令 (靜態(tài)文本)
"textField"命令 (文本框)
"button"命令 (按鈕)
"separator"命令 (分隔線)
這些控件雖然已經講過了,但由于十分重要,現(xiàn)在在強調一下,一定要熟練掌握。
另外,先隨便講幾個:
"picture"命令 (圖片)
靜態(tài)圖片。
---------------------------------------------------
picture -w 80 -h 60 -image "sphere.xpm" -tile on;
---------------------------------------------------
"-i/image"指定圖片。
"-tl/tile"指定圖片是否重復排疊顯示。
"iconTextButton"命令 (圖標文本按鈕)
既有字又有圖標的按鈕,你可以只顯示字或圖標。
---------------------------------------------------
string $window = `window`;
columnLayout -adj 1;
iconTextButton -style "textOnly"
-image1 "sphere.xpm" -label "sphere";
iconTextButton -style "iconOnly"
-image1 "spotlight.xpm" -label "spotlight";
iconTextButton -style "iconAndTextHorizontal"
-image1 "cone.xpm" -label "cone";
iconTextButton -style "iconAndTextVertical"
-image1 "cube.xpm" -label "cube";
showWindow $window;
"symbolButton"命令 (符號按鈕)
有圖片的按鈕。
---------------------------------------------------
symbolButton -image "cone.xpm" -c "cone";
---------------------------------------------------
"intSlider"命令 (整數(shù)型滑動條)
---------------------------------------------------
intSlider -min -100 -max 100 -value 0;
---------------------------------------------------
"-min"最小值
"-max"最大值
"-value"當前值
一直這么講下去難免有些枯燥乏味,還是來點實戰(zhàn)輕松一下吧。
下面介紹一下賭牌游戲myFourUp的編寫方法。
------------------------------
實例7:賭牌游戲myFourUp
myFourUp程序很像一個撲克機,它的原版是Stephen D. Gilbert的Visual C++ 6編程藍皮書中的一個實例(FourUp),我認為這個程序用于Mel也同樣很能說明問題,就擅自把它移植了過來,不過我在界面和算法部分都作了較大改動。
說到打賭,myFourUp程序與拉斯韋加斯和亞特蘭大城中賭場工作人員特別喜愛的撲克游戲相似。但myFourUp更簡單,因此你可以集中精力學習程序設計和界面編寫方法。
myFourUp并非技巧游戲。當游戲開始時,給玩者1000美元。每一輪(需花一定賭注,比如2美元)玩者接到4張牌。牌值無關緊要-我們關心的只是花色。如果玩者得到兩對(例如,兩張紅心和兩張方片),將得4美元。如果玩者得到三個單張,將得到6美元。如果發(fā)牌者擺出四張同一花色,將得8美元。
當然,游戲并非使用真錢。如果你想編寫程序以便能撥入玩者的銀行帳戶來取出贏利(或彌補虧空),那是你自己的事。
在我們編寫代碼之前,讓我們先花一點時間來計劃一下程序的外觀以及它將如何工作。最好是用紙和筆畫出該布局,畢竟修訂一個計劃比改變一個完成的程序要容易。
現(xiàn)在可以根據(jù)草圖來編寫代碼,不過編寫代碼之前最好先講一下什么是全局變量。
全局變量(Global Variable)
我們以前用到的變量都是局部變量。局部變量只能在函數(shù)里面用,當函數(shù)運行時局部變量被創(chuàng)建,函數(shù)運行結束后,局部變量就被刪除了。
如果用戶需要在一個函數(shù)中創(chuàng)建的變量,其它的函數(shù)也可用,則用戶可以通過全局變量來實現(xiàn)。如果你創(chuàng)建了一個全局變量,你可以通過函數(shù),也可以通過命令行不斷修改它的值。全局變量將一直保留在內存中,直到你退出Maya時才會被刪除。
在變量聲明前加上"global"表示為全局變量。(在命令行聲明全局變量可以不寫"global")
例如:
global float $counter;
跟椐Maya幫助文檔的說法應當盡量少用全局變量,否則很容易會因為粗心而出錯。比如你聲明了一個全局變量$counter,而Maya中卻碰巧已經有了一個全局變量$counter,此時你對$counter變量值所作的任何修改都將影響以前就存在的全局變量$counter,這將會產生錯誤。
為避免碰巧同名變量的存在,請用較為獨特的名稱來命名全局變量。Maya中的全局變量都是以小寫字母"g"開頭(例如$gMainWindow),你可以用除"g"以外的你喜歡的字母開頭,本教程中用到的全局變量一般都以"mg"開頭。
下面開始我們的程序。程序雖然簡單,但這個是一個完善的游戲程序,對于某些初學者來說可能有些難度,如果你實在看不懂,就索性照抄一遍,借此體驗一個編程的感覺。
在myFourUp程序中,用一個全局變量$mgMoneyRemaining來記錄你擁有的總錢數(shù),當你贏錢時總錢數(shù)會增加,反之則會減少。
----------------------------------
//global proc myFourUp(){以下的代碼寫在括號中}
// 初始化總錢數(shù)為1000美元
global int $mgMoneyRemaining;
$mgMoneyRemaining = 1000;
界面代碼:
首先創(chuàng)建窗口:
----------------------------------
if(`window -ex myFourUpWin`) deleteUI myFourUpWin;
window -t "Four Up"
-widthHeight 293 440
-sizeable false
myFourUpWin;
----------------------------------
"-wh/widthHeight"指定窗口的長度和寬度分別為多少像素。也可用"-w"和"-h"分別來指定。
"-s/sizeable"指定窗口是否可以拉大縮小(變化尺寸)。
為了能夠隨意擺放控件,用一個表單布局作為窗口的主布局。
----------------------------------
string $form = `formLayout -backgroundColor 0.353 0.353 0.259`;
----------------------------------
創(chuàng)建布局$form中的元素:
----------------------------------
// 標題圖片
string $pTitle_f = `picture -image "title_f.bmp" fourUpTitle`;
// 邊框圖片
string $pFrame_l = `picture -image "frame_l.bmp"`;
string $pFrame_r = `picture -image "frame_r.bmp"`;
string $pFrame_b = `picture -image "frame_b.bmp"`;
// 位圖按鈕(圖標文本按鈕)
string $bBtn_1 = `iconTextButton -style "iconOnly"
-backgroundColor 0.353 0.353 0.259
-w 106 -h 41 -image1 "btn_deal.bmp"
-c "dealCards"`;
string $bBtn_2 = `iconTextButton -style "iconOnly"
-backgroundColor 0.353 0.353 0.259
-w 106 -h 41 -image1 "btn_Exit.bmp"
-c "deleteUI myFourUpWin;"`;
// 橫布局
// 包括一個靜態(tài)圖片,一個文本框和一個符號按鈕
string $row = `rowLayout -numberOfColumns 3
-backgroundColor 0.353 0.353 0.259
-columnWidth3 82 60 80`;
picture -image "deposit.bmp";
textField -ed false -tx "$2" placeBetField;
symbolButton -image "max.bmp" -c "placeBetMax";
setParent ..;
// 整數(shù)型滑動條
string $slider = `intSlider
-backgroundColor 0.568 0.588 0.463
-min 2 -max 100 -value 2 -step 20
-dragCommand "placeBet" placeBetSlider`;
// 兩個邊框布局
string $frame_1 = `frameLayout -backgroundColor 0.863 0.729 0.435
-label "你還剩:$1000 "
-labelAlign "bottom" -borderStyle "etchedIn"
-h 65 MoneyRemainingFrame`;
// @@@A 創(chuàng)建布局$frame_1中元素的代碼寫在這里:
setParent ..;
string $frame_2 = `frameLayout -backgroundColor 0.863 0.729 0.435
-label "贏錢規(guī)則:"
-labelAlign "bottom" -borderStyle "etchedIn"
-h 110`;
// @@@B 創(chuàng)建布局$frame_2中元素的代碼寫在這里:
setParent ..;
----------------------------------
指定布局$form中每個界面元素的位置,
注意每張圖片所放置的位置。
----------------------------------
formLayout -e
// frame_l.bmp直接放在布局$form的左上角
-af $pFrame_l "top" 0
-af $pFrame_l "left" 0
// title_f.bmp
-af $pTitle_f "top" 0 // 上邊緊靠布局的上邊
-ac $pTitle_f "left" 0 $pFrame_l // 左邊緊靠frame_l.bmp
// frame_r.bmp
-af $pFrame_r "top" 0 // 上邊緊靠布局的上邊
-ac $pFrame_r "left" 0 $pTitle_f // 左邊緊靠title_f.bmp
// frame_b.bmp
-af $pFrame_b "bottom" 0 // 底邊緊靠布局的底邊
-ac $pFrame_b "left" 0 $pFrame_l // 左邊緊靠frame_l.bmp
-ac $frame_1 "top" 5 $pTitle_f
-ac $frame_1 "left" 5 $pFrame_l
-ac $frame_1 "right" 2 $pFrame_r
-ac $frame_2 "top" 12 $frame_1
-ac $frame_2 "left" 5 $pFrame_l
-ac $frame_2 "right" 2 $pFrame_r
-ac $row "top" 12 $frame_2
-ac $row "left" 5 $pFrame_l
-ac $slider "top" 8 $row
-ac $slider "left" 5 $pFrame_l
-ac $slider "right" 2 $pFrame_r
// btn_deal.bmp
-ac $bBtn_1 "bottom" 0 $pFrame_b // 底邊緊靠frame_b.bmp
-ac $bBtn_1 "left" 0 $pFrame_l // 左邊緊靠frame_l.bmp
// btn_exit.bmp
-ac $bBtn_2 "bottom" 0 $pFrame_b // 底邊緊靠frame_b.bmp
-ac $bBtn_2 "right" 0 $pFrame_r // 右邊緊靠frame_r.bmp
$form;
----------------------------------
顯示窗口:
----------------------------------
showWindow myFourUpWin;
----------------------------------
界面和主要部分編好了,下面讓我們來填補兩個邊框布局。
在前面代碼的"@@@A..."這句注釋之后填寫如下代碼:
----------------------------------
// 布局$form_1中的布局分兩層
string $form_1 = `formLayout`;
// 先寫的為第一層,第一層放置底紋圖片"back_1.bmp"
// "-tl"圖片重疊擺放
string $pBack_1 = `picture -tl 1 -image "back_1.bmp"`;
// 放置四種花色圖片
// spade - 黑桃,club - 梅花,diamond - 方片,heart - 紅心。
string $card_s = `picture -image "card_spade.bmp" cardImg_0`;
string $card_c = `picture -image "card_club.bmp" cardImg_1`;
string $card_d = `picture -image "card_diamond.bmp" cardImg_2`;
string $card_h = `picture -image "card_heart.bmp" cardImg_3`;
// 指定位置
formLayout -e
-af $pBack_1 "top" 0
-af $pBack_1 "bottom" 0
-af $pBack_1 "left" 0
-af $pBack_1 "right" 0
-af $card_s "top" 6
-ap $card_s "left" 8 0
-af $card_c "top" 6
-ap $card_c "left" 8 25
-af $card_d "top" 6
-ap $card_d "left" 8 50
-af $card_h "top" 6
-ap $card_h "left" 8 75
$form_1;
setParent ..;
布局$form_2編寫的方法類似布局$form_1,我就不做解釋了。
在"@@@B..."這句注釋之后填寫如下代碼:
----------------------------------
string $form_2 = `formLayout`;
string $pBack_2 = `picture -tl 1 -image "back_1.bmp"`;
string $pT1 = `text -l " 兩對同花 $4"
-backgroundColor 0.568 0.588 0.463 placeBetText1`;
string $pT2 = `text -l " 三個同花 $6"
-backgroundColor 0.568 0.588 0.463 placeBetText2`;
string $pT3 = `text -l " 全部同花 $8"
-backgroundColor 0.568 0.588 0.463 placeBetText3`;
formLayout -e
-af $pBack_2 "top" 0
-af $pBack_2 "bottom" 0
-af $pBack_2 "left" 0
-af $pBack_2 "right" 0
-ap $pT1 "top" 10 0
-af $pT1 "left" 15
-af $pT1 "right" 15
-ap $pT2 "top" 10 30
-af $pT2 "left" 15
-af $pT2 "right" 15
-ap $pT3 "top" 10 60
-af $pT3 "left" 15
-af $pT3 "right" 15
$form_2;
// 因為$form_2之后不再創(chuàng)建界面元素了,所以可以不寫"setParent ..;"
界面部分編完了,現(xiàn)在來編寫代碼讓這個游戲運轉起來。
在前面的代碼中,有三處需要調用自定義的命令,分別是:
1)位圖按鈕(btn_deal.bmp)。
string $bBtn_1 = `iconTextButton ... -c "dealCards"`;
2)"-dragCommand"拉動滑動條時執(zhí)行的命令。
string $slider = `intSlider ... -dragCommand "placeBet" placeBetSlider`;
3)橫布局中的符號按鈕(max.bmp)。
symbolButton ... -c "placeBetMax";
// 按下"Max"(max.bmp)按鈕執(zhí)行的命令
global proc placeBetMax()
{
// 把滑動條撥到最右邊
intSlider -e -v 100 placeBetSlider;
// 文本框中填入相應的值$100
textField -e -tx "$100" placeBetField;
// 修改贏錢規(guī)則
text -e -l (" 兩對同花 $200") placeBetText1;
text -e -l (" 三個同花 $300") placeBetText2;
text -e -l (" 全部同花 $400") placeBetText3;
}
// 拉動滑動條時執(zhí)行的命令
global proc placeBet()
{
// 獲得滑動條上的數(shù)值
int $deposit = `intSlider -q -v placeBetSlider`;
// 文本框中填入滑動條的數(shù)值
string $text = "$" + $deposit;
textField -e -tx $text placeBetField;
// 修改贏錢規(guī)則
int $m1 = $deposit * 2;
int $m2 = $deposit * 3;
int $m3 = $deposit * 4;
text -e -l (" 兩對同花 $" + $m1) placeBetText1;
text -e -l (" 三個同花 $" + $m2) placeBetText2;
text -e -l (" 全部同花 $" + $m3) placeBetText3;
}
現(xiàn)在來對付關鍵部分,相對比較復雜。
我們可以先來考慮一下,當玩家點擊了"deal"按鈕時,我們希望發(fā)生什么?
1)隨機選擇4張牌作為一付牌。
2)更新每張牌的圖像以指示該付牌的花色。
3)統(tǒng)計每種花色牌的張數(shù)。
4)統(tǒng)計同種花色的牌一共有幾對。
5)從總錢數(shù)扣去賭注。
6)計算輸贏,如果贏錢加入總錢數(shù)中。
7)贏錢后加亮贏錢規(guī)則字樣的底色。
8)修改標題圖片,如果是贏錢顯示"Congratulations",否則顯示"Misfortune"。
8)顯示剩余金額。
----------------------------------
// 位圖按鈕(btn_deal.bmp)命令
global proc dealCards()
{
global int $mgMoneyRemaining;
// 分別用四個變量記錄每種花色牌的張數(shù)
int $num_spade = 0;
int $num_club = 0;
int $num_diamond = 0;
int $num_heart = 0;
// 用$pairs記錄同種花色的牌一共有幾對
int $pairs = 0;
for($i=0; $i<4; $i++)
{
// 隨機選擇4張牌中的一張
int $num = `rand 4`;
if($num == 0) {
// 更新花色牌的圖片
picture -e -image "card_spade.bmp" ("cardImg_"+$i);
// 統(tǒng)計每種花色牌的張數(shù)
$num_spade++; // $num_spade = $num_spade + 1
}
if($num == 1) {
picture -e -image "card_club.bmp" ("cardImg_"+$i);
$num_club++;
}
if($num == 2) {
picture -e -image "card_diamond.bmp" ("cardImg_"+$i);
$num_diamond++;
}
if($num == 3) {
picture -e -image "card_heart.bmp" ("cardImg_"+$i);
$num_heart++;
}
}
// 統(tǒng)計同種花色的牌一共有幾對
if($num_spade == 2)
$pairs++;
if($num_club == 2)
$pairs++;
if($num_diamond == 2)
$pairs++;
if($num_heart == 2)
$pairs++;
//----------------------------------------------
// 從滑動條上得到賭注數(shù)額
$deposit = `intSlider -q -v placeBetSlider`;
int $m1 = $deposit * 2;
int $m2 = $deposit * 3;
int $m3 = $deposit * 4;
// 先用總錢數(shù)減去賭注
$mgMoneyRemaining -= $deposit;
// 調整文字,為的是保證文本框底色的刷新
text -e -l "" -backgroundColor 0.568 0.588 0.463 placeBetText1;
text -e -l "" -backgroundColor 0.568 0.588 0.463 placeBetText2;
text -e -l "" -backgroundColor 0.568 0.588 0.463 placeBetText3;
// 修改標題圖片為"Misfortune"
picture -e -image "title_m.bmp" fourUpTitle;
// 兩對同花
if($pairs == 2) {
// 贏的錢加入總錢數(shù)中
$mgMoneyRemaining += $m1;
// 加亮文本底色,變?yōu)榱咙S色
text -e -backgroundColor 1 1 0.463 placeBetText1;
// 修改標題圖片為"Congratulations"
picture -e -image "title_c.bmp" fourUpTitle;
}
// 三個同花
if( $num_spade == 3 || $num_club == 3 ||
$num_diamond == 3 || $num_heart == 3 ) {
$mgMoneyRemaining += $m2;
text -e -backgroundColor 1 1 0.463 placeBetText2;
picture -e -image "title_c.bmp" fourUpTitle;
}
// 全部同花
if( $num_spade == 4 || $num_club == 4 ||
$num_diamond == 4 || $num_heart == 4 ) {
$mgMoneyRemaining += $m3;
text -e -backgroundColor 1 1 0.463 placeBetText3;
picture -e -image "title_c.bmp" fourUpTitle;
}
// 刷新
text -e -l (" 兩對同花 $" + $m1) placeBetText1;
text -e -l (" 三個同花 $" + $m2) placeBetText2;
text -e -l (" 全部同花 $" + $m3) placeBetText3;
// 顯示剩余金額
frameLayout -e -label ("你還剩:$" + $mgMoneyRemaining) MoneyRemainingFrame;
}
全部代碼編寫完畢,現(xiàn)在你可以體驗賭牌的樂趣了。
我把全部范例代碼和圖片放到附件中,供你們參考。
1515133-myFourUp.rar (56.52k)
"if"語句 (如果)
關于"if"語句以前講的不夠詳細,現(xiàn)在做一點簡要補充。小括號里的雙豎線"||"表示"或"的意思,而"&&"表示"并且"的意思。
"if"后面的小括號里的代碼表示的只能是一個布爾值,1或0。
因此要表示相等要寫雙等號。
"雙等號的用法"
----------------------------------
int $test = 4;
int $bool = $test == 5; // 如果$test等于5,$bool = 1(true)
----------------------------------
這里因為$test = 4,不等于5,所以$bool = 0(false)。
"rand"命令 (隨機)
前面用到"rand"命令,"rand"是隨機的意思,就是從指定的范圍內隨便取出一個數(shù)值。mel里的"rand"同C語言的"rand"有一個很明顯的差別,mel是浮點數(shù)的隨機,C語言是整數(shù)的隨機。
我在myFourUp程序中寫的"int $num = `rand 4`;" 實際上是一種浮點數(shù)轉整數(shù)的方法,還有一種寫法更容易看明白一些:
float $floatNum = `rand 4`;
int $num = (int)$floatNum;
學習 · 提示
相關教程