mel語初解之一-基礎和界面篇(2)
"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ù)值。從表示顏色這一點就可以明顯地看出來。比如說紅色,在網(wǎng)頁中或平面軟件中的表示為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ù)此信息來修正。
轉(zhuǎn)義符(escape character)
引號里面的字如果也有引號,就把"變?yōu)閈"。和c語言一樣,\為mel的轉(zhuǎn)義符(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ù)組,會占用很多內(nèi)存,你不用時要用"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個或多個字符
^ 代表一行中第一個字符的位置
$ 代表一行中最后一個字符的位置
\ 轉(zhuǎn)義符(escape character). 把它寫在特殊的字符前面(如 '*')
[...] 代表指定范圍內(nèi)的任意一個字符
(...) 用于把部分通配符表述組織在一起
例:
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歷史窗中輸出的物體名稱清單。
由于上一課已經(jīng)講過了字符處理的方法,相信你上面的大部分代碼都看得懂。不過,"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不能設斷點進行調(diào)試分析,我只好用這種笨辦法來分析了,但愿你能看懂。
注意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;
學習 · 提示
相關教程