入門教程內容結構
本教程內容分爲五個部分,用幾個具體的例子講解大多數的腳本操作和語法知識。
一、MAXScript簡介
1、說明什麼是MAXScript以及它有什麼功能,能給用戶帶來什麼好處。
2、訪問腳本,認識腳本訪問的界面。
二、基本操作: 創建和修改簡單的對象,給對象賦材質,從而引出下面的概念
構造器:創建對象的語法稱爲構造器;
路徑名;
數據類型:它們是數字,字符串,數組。
對象和類(層級樹);
屬性和方法;
變換;
函數和方法的聯繫;
通用屬性和動態屬性;
顯示屬性的函數;
一些常用的方法;
三、創建和操作具有複雜關係的對象,從而引出下面的概念
變量,變量和屬性的關係;
對象引用:引用另外一個對象的屬性值作爲自己的值;
數組和集合等:數組,數組和變量的關係;
運算符和表達式;
表達式:求值的規則。
流程控制:選擇和循環,條件選擇。
製作簡單的動畫:"at time",動畫控制器。
四、換一角度——從大家熟悉的操作流程來學習腳本
這是對前面知識的總結,前面是對語法基本概念的學習,現在是從大家操作流程的角度來學習,一縱一橫,讓我們對腳本有個相對全面的瞭解,理論和實際相互聯繫。有了前面的基礎纔好學下面的內容。
第一步:創建物體
一些常用物體的創建方法 。
第二步:選擇物體
select
路徑名,
變量名引用
條件選擇
用類和集合選擇物體
組Group:是一個虛擬對象,不是集合。
第三步:變換物體
變換:postion,move,scale,rotate,變換座標系和變換中心
第四步:修改器
第五步:材質和貼圖
第六步:製作動畫
五、基本的語法知識
現在來學語法恐怕不會那麼頭痛了吧?
-----------------------------------------------------------------------------------------------------------------------------------------
一、 MAXScript簡介
1、MAXScript
MAXScript語言是爲了擴展3ds MAX 功能而專門設計的一種腳本語言,是面向對象編程語言中的一種。用它創建的場景物體和材質與在3ds MAX界面中創建的場景物體和材質完全對應。可以生成自動關鍵幀的動畫模式,並可以通過層級路徑名來訪問場景中的物體。有記錄在3ds MAX界面中的交互操作過程的能力,在使用界面操作的同時可以使用MAXScript來自由創作。
可以實現3D Studio MAX的全部用途,如建模、動畫、材質、渲染等。
MAXScript可以運用各種數學工具來完成高級複雜的程序設計任務。可以對含有大量對象的集合進行操作。例如在複雜的場景中選擇物體,可以把大量物體放置在精確的位置上,例如在山或路邊放置一些樹木,使用MAXScript操作起來是非常的方便。
它也能將一些功能定義爲界面元素,例如工具欄按鈕、菜單、浮動窗口,程序面板捲簾窗。
可以建行批處理操作提高工作效率。例如建立一次可以渲染多個場景文件的腳本程序。
可以自定義輸入輸出工具,可以定義修改器,渲染效果插件等。
2、訪問腳本
2.1、認識MAXScript界面:包括MAXScript捲簾窗,腳本監聽器窗口,腳本編輯窗口。
2.1.1、MAXScript捲簾窗
單擊命令面板中的按鈕,打開應用程序面板,再單擊MAXScript按鈕,在應用程序面板出現MAXScript捲簾窗,如圖1-1所示。
圖1-1
2.1.2、腳本監聽器窗口
腳本監聽器又叫腳本跟蹤器,單擊MAXScript捲簾窗中的Open Listener按鈕,也可選擇菜單欄中的 MAXScript/MAXScript Listener 命令,快捷鍵F11。如圖1-2所示。
圖1-2
也可右擊用戶界面左下方的Mini-Listener(迷你監聽器)打開腳本監聽器窗口,如圖1-3所示。
圖1-3
腳本監聽器窗口是一個命令編輯和執行窗口,它由兩部分組成,上半部分爲宏記錄窗口,下半部分爲腳本運行結果輸出窗口。如圖1-4所示。
圖 1-4
運行腳本時,腳本監聽器窗口會輸出腳本的運行結果,運行正確就顯示爲藍色,運行錯誤就顯示爲紅色,希望你的腳本不要出現紅色啊。在窗口中可以編寫新的腳本語言或對已有的腳本語言進行修改,用戶輸入的腳本顯示爲黑色。
選擇主菜單中的宏記錄Macro Recorder/Enable命令,如果此時進行操作,在窗口中淡紅色部分將記錄所進行的操作,這就是宏記錄,選擇菜單欄中的File/Save as 命令可以將記錄保存爲一個腳本。
2.1.3、腳本編輯窗口
單擊MAXScript捲簾窗中的New Listener按鈕,或者選擇菜單中的 MAXScript/New Scrip 命令打開腳本編輯窗口,如圖1-5所示。
圖1-5
腳本編輯窗口是一個文本編輯窗口,你能在3ds MAX內部打開、創建或者編輯擴展名爲"*.ms"、"*.txt"和"*.dat"格式的文本文件。調試文件時大鍵盤中的回車鍵與在記事本中的用法一樣的,用來換行,當鼠標光標出現在某一行時,敲一下小鍵盤區的回車鍵,就可以執行本行程序。如果選擇多行程序,敲下小鍵盤區的回車鍵,可以執行選擇被選擇的程序。如圖1-6所示。
圖1-6
按住鼠標左鍵不放,把選擇的一行或者多行程序拖到3DSMAX工具欄裏,將生成一個宏腳本按鈕,單擊此按鈕,就可以運行這個宏腳本。
在腳本編輯窗口或腳本監聽器窗口裏調用edit()函數,可以打開腳本編輯窗口,語法是:edit "腳本文件名稱" 。 例如打開 effect.ms 文件:輸入 edit " effect.ms ",運行就可以打開這個腳本文件。
二、基本操作:創建和改變對象
1、創建簡單的對象
傳說天地未創建之時,是爲無極,及有太極,則宇宙處於渾沌狀態,就象雞蛋一樣盤旋着,在空間上無大無小無內無外,在時間上追溯到遠古時代也無法知道其起源,所以把這種狀態稱爲盤古,後來又不知那個好事者把盤古這種狀態變成了一個傳說——“盤古開天地”,結果“盤古”變成了一個宇宙創始人。老子給它起了個玄之又玄的名字——道。盤古一斧頭就把這混混沌沌蛋給劈開了,結果太極判爲天地,一氣分爲陰陽,中國就有了太極八卦和陰陽學說。自從盤古開闢天地之後,則萬物具備,輕清者上升爲天,重濁者下降爲地。在天成象,在地成形,仰觀天有日月星辰,俯察地有人魚鳥獸,花草樹木,種類繁多,數量龐大,不可計數。人類爲了征服大自然,掌握這些事物,就給它們進行了分類,把相同的對象歸類到具有相似特徵的類之中,類與類之間如果還有相似的地方,那麼就把這些歸納到更高層次的類中,例如把植物類和動物類歸納到生物類。面向對象編程中類和對象的概念就是試圖對上面這些比較符合人類認識規律的概念進行描述。(呵呵 , 說的還挺玄乎~~)
好吧,現在就來看看在 MAXScript 中誰是宇宙萬物的創造者——“盤古”。
選擇菜單中的 MAXScript/New Scrip 命令打開腳本編輯窗口,如果想調試程序,跟蹤程序執行結果,也可以把腳本監聽器窗口打開,輸入
box length:100 width:100 height:2
sphere radius:10 segments:16
按Ctrl+E鍵或者選擇腳本編輯窗口菜單中的File/Evaluate All 命令執行程序,還可以用鼠標全部選定,敲小鍵盤上的回車鍵執行程序(以後都按此方法,不再說明)。
結果創建了一個盒子和一個球體,如圖2-1所示。現在單擊工具欄按名字選擇對象按鈕,在彈出的對話框中會出現這個盒子的名字“Box01”和球體的名字“Sphere01”,這是默認的名字。圖2-1
box length:100 width:100 height:2 這一行代碼就是一個box構造器,它創建了一個長度爲100、寬度爲100、高度爲2的box。搞了半天,原來構造器就是“盤古”啊,偉哉,萬物之始!
什麼是構造器?在MAXScript幫助中常常看到Constructor這個單詞,中文意思就是構造器。但是幫助中只是說明構造創建對象的語法,但並沒有專門的內容來說明構造器這個概念。我把它歸納如下:在MAXScript中創建對象的語法就叫作構造器,其實質就是調用函數來創建對象,所以可以把它看作是一個函數調用 。也可以說是用一個抽象的類產生一個具體的對象。這個函數的名稱就是基類的名稱,所謂基類就是不能再分的類。我們創建對象都是從基類開始的。Box就是個基類,它的上一級就是幾何類,再上一級就是節點。
如果學過JAVA,那麼知道創建一個box對象的語法是:
Box Box01=new box() ;
呵呵,學習MAXScript可舒服了,只需輸入box()就可以構造一個box了,面且大小寫,分號都可以不用注意。
當我們手工單擊創建面板中的Box按鈕,然後在視窗中完成一個長度爲100、寬度爲100、高度爲2的Box時,在程序內部就調用了Box( )函數,就是把我們在界面中的操作轉換成了box length:100 width:100 height:2 這行內部代碼。
構造器由兩部分組成,基類名和創建參數。整個構造器就是一個函數調用,函數名 box就是基類的名字。創建參數由參數名後跟一個 冒號 : 和參數值組成,如圖2-2所示。
圖2-2
如果調用函數沒有輸入任何參數,那麼必須在類名後加上一對英文 ( )括號,例如box(),將創建缺省參數的盒子。知道構造器原理後就知道怎麼創建其它的對象了, 例如創建球體,輸入
sphere radius:10 就可以了。
注:由於對象和類、層級關係等詳細概念的內容比較複雜,將另立一文講述。請看>>
2、改變對象
創建了盒子和球體對象之後,我們想知道剛纔的長度、寬度等參數是否正確,應該怎麼辦呢?那就要訪問對象的屬性。諸如長度、寬度、名字、線框顏色半徑等等對象所具有的特性統統稱爲對象的屬性(Properties)。
訪問對象屬性的語法是由對象的路徑名(PathName)後跟小數點.和屬性名組成,路徑名由美元符號$後跟對象在場景中的名稱組成,$表示當前場景。例如訪問box01的長度,輸入 $Box01.length ,在腳本跟蹤器窗口中返回100.0,訪問Sphere01的半徑,輸入 $Sphere01.radius 返回 10.0 。改變Sphere01的線框顏色,輸入 $sphere01.wirecolor=blue 或者輸入:$sphere01.wirecolor= (color 0 0 255) 都一樣。
那麼要改變屬性呢?只需給這些屬性賦值, 輸入$Box01.length=20,box01的長度變成20。我們以前數學裏的等號在MAXScript中只能當作賦值符號用了。值的數據類型有幾種,現在簡單介紹幾個典型的不同數據類型,它們是數字,字符串,數組。
數字:MAXScript區分兩個數目類型:integers(整型)和floats(浮點型)。整型就是整數,例如0、1、802。浮點型就是小數,如期而至0.2、25.02、0.68。當MAXScript執行數字操作時返回和運算參數相同的類型。例如4+5返回9、而4.0+5.0返回9.0。
字符串:字符串是一個常量,需用引號標識。輸入"Hello",MAXScript返回"Hello"。如果輸入時沒有加引號則返回undefined未定義。改變Sphere01的名字,輸入$Sphere01.name="球體01",現在打開修改器面板,可以看到如圖2-3所示的樣子。
再把名字改回來,輸入:
$球體01.name="sphere01"
注意雙引號不能忘掉輸入。
數組:數組是一些有序的元素的集合。每個元素可以是任何類型的值,並且所有的元素都可以單獨的訪問。一個數組可以有兩種表現形式,第一種是:#(),這是一個空數組,數組定義必須用一個數目符號#和一對括號( )組成,括號裏面是數組的元素名,元素可以爲一到多個,多個元素之間用逗號分開,例如,#(1,“hello”,3.14159)。想要初始化數組中的元素的時候數組有適當的形式,每一個元素的值可以是數值,一個表達式(例如、30*2),或者是一個字符串(例如,“hello”),元素的名稱是不能有同名,而且元素的數目沒有限制。關於數組的具體操作,以後的例子中會涉及,這裏只作個簡單的介紹。輸入命令 #(100,"hello",pi) ,MAXScript返回 #(100, "hello", 3.14159)。
那麼我們怎麼可以獲取更多的創建參數呢?方法有兩種:
其一是在創建面板中找到對應的創建參數名,創建好的對象也可以在修改器面板中找到創建參數。如圖2-4A所示。
其二,根據屬性來,因爲創建參數實質就是根據對象的屬性而定的,沒有這個屬性就不可能有這個創建參數。顯示對象的屬性的函數是 showProperties( ),輸入 showProperties $box01 ,結果在腳本監聽器窗口中返回以小數點開頭屬性名稱,冒號後面是值的類型,現在不要管它。如圖2-4B所示。三者之間的關係
細心的讀者會發現,像name、wirecolor這些屬性怎麼沒有顯示出來呢?這就涉及到節點的通用屬性,可以觀看另文撰寫的文章“面向對象編程中的對象和類”中的“類的繼承”內容,現在不要管它,隨着學習後面的文章,就會知道的。
3、變換對象
改變位置:想把那個球體放在Box01上面,先看看這兩個物體的座標吧。輸入 $Box01.pos 返回 [0,0,0] ,輸入 $Sphere01.pos 也返回 [0,0,0] 。Box01的高度是2,Sphere01 的半徑是10,要向Z軸移動球體 12 個單位才能達到目的。輸入 move $Sphere01 [0,0,12], 結果球的底部剛好在盒子的上面。聰明的讀者也可能會輸入 $Sphere.pos=[0,0,12] ,結果也達到了目的。那麼move和pos有什麼不同呢?我們試着分別把$Sphere01.pos= [0,0,12] 和 move $Sphere01 [0,0,12] 分別連續輸入三遍,結果區別就出來了。pos是絕對座標,move是相對座標。
[0,0,12] 是什麼?它是個三點值,在這裏,方括號中的三個值分別是X軸、Y軸、Z 軸座標值。
縮放尺寸:據說地球以前是圓的,後來人一多就給踩扁了,變成現在的橢圓形了(哈哈!!)。現在看看我們創建的球是不是也能踩扁它。輸入$Box01.scale看看結果,返回了 [1,1,1],再輸入 $Box01.scale=[1,1,0.5] ,結果Box01沿Z軸方向被壓扁了一半,重複輸入幾次,不會再有變化了。輸入 $Box01.scale=[1,1,1] ,恢復原狀,現在輸入scale $Box01= [0.5,0.5,1],它沿XY軸方向縮小了一半,繼續輸入就更小了。 $Box01.scale是絕對的,
scale $Box01 [0.5,0.5,1]是相對的。輸入$Box01.scale=[1,1,1] 恢復到原來的狀態。數值1表示原始大小即100%,2表示200%,0.5表示50%,其它如此推算。方括號中的三個值分別對應於X軸、Y軸、Z 軸。
旋轉角度:那個盒子比球大,卻被球壓迫在底下,很不服氣,發出 $Box01.rotation=(eulerangles 90 0 0) 命令,來了個側翻身,(eulerangles 90 0 0) 這是歐拉角度值,eulerangles 表示歐拉角度,後面三個值分別表示X軸、Y軸、Z 軸。上面稱動和縮放都有兩種操作方式,一種是爲屬性賦值而變換,另外一種是接受一個動作而變換,那麼角度也不例外,輸入 rotate $Box01 (eulerangles 90 0 0),結果又把這個盒子給擺平了。也能寫成比較好記的方式如
rotate $Box01 90 x_axis 旋轉X軸90度,rotate $Box01 90 y_axis 旋轉Y軸90度, rotate $Box01 90 z_axis 旋轉Z軸90度。
像上面 move、 scale、rotate 等都是函數(Function),能夠完成對象的特定功能,在MAXScript中被稱爲對象的方法(Methods)。函數和方法實質是一樣的,屬於對象特有的函數都稱爲方法。
凡是通過改變對象屬性來變換的都是絕對值,通過對象的方法來變換的都是相對值。
4、複製對象
這個世界有辛辛苦苦的實幹家,那麼就有快快活活的投機者。現在我們也來個偷懶的做法,嘿嘿,不要認爲是模仿啊。輸入 copy $sphere01 pos:[30,30,12] name:"球體2":,結果在盒子的斜角處產生了一個新的名字爲"球體2"球體。
copy也是對象的方法,它調用了另外一個函數作爲參數,這個函數就是前面提到的構造器。把被調用的函數稱爲位置參數(Positional Arguments),把被調用的參數稱爲關鍵詞參數(Keyword Arguments)。因爲位置參數只能放在固定的位置上,所以稱爲位置參數,關鍵詞參數是成對出現的,寫法就像 “關鍵詞:值”,和上面提到的創建參數一樣,沒什麼區別。概念是真多,把人給搞暈了,其實搞通了不過是那麼回事。關聯和引用複製就是把copy分別替換爲instance、reference。
5、其它方法:
select 選擇對象、deselect 取消選擇、delete 刪除對象、hide 隱藏對象,unhide 取消隱藏、
freeze 凍結、 unfreeze 取消凍結。
示例:
select $Box01--選擇Box01
deselect $Box01--取消選擇Box01
delete $* --刪除場景中所有對象
6、賦予材質
自從有了你,世界更美麗。給盒子鋪上幾塊地磚,輸入:
$box01.material=standardMaterial diffusemap:(tiles()) showInViewport:true
$box01.material是Box01的材質屬性,材質也是一個對象,所以標準材質的構造器是
standardMaterial diffusemap:(tiles()) showInViewport:true
standardMaterial是標準材質類名稱,關鍵詞參數 diffusemap:(tiles()) 是材質的 diffusemap 貼圖,其值tiles()是平鋪貼圖類型,showInViewport:true 表示是否在視窗中顯示。其值是true或者false,true(真)表示顯示,false(假)表示不顯示。
這個材質並沒有在材質編輯器24個球中出現,但點擊獲取材質按鈕在場景選項中可以找到它。如果想把材質編輯器插槽中的材質球中的一個賦給Sphere01,輸入
$sphere01.material=meditmaterials[1] --[1]表示第一個,[2]表示第二個
$sphere01.material.diffuse=yellow --設置diffuse的顏色爲黃色
$sphere01.material.specularLevel=20 --設置高光級別爲20
竅門:參數太多不易掌握,可選擇主菜單中的宏記錄Macro Recorder/Enable命令,然後手工在界面中操作,再觀看宏記錄,此時不禁心中竊笑,哈哈,MAXScript你想難倒我,沒門!
關於材質方面的知識太多,不是一時所能掌握的,在此只作過初步瞭解吧。
7、修改對象
addmodifier $box01 (bend())
$box01.widthsegs=10m
$box01.bend.benddir=0
$box01.bend.bendaxis=0
$box01.bend.angle=-180
像對象的修改器、空間變形等屬性,並非對象所固有的,而是後來通過另外的對象附加上去的屬性,MAXScript中把它稱爲動態屬性(dynamic properties)
8、定義自己的腳本
上面輸入的腳本可以保存下來,以備以後再使用。選擇腳本編輯器菜單中的 File/Save 命令,保存到指定的文件夾中,等以後要用時選擇File/Open 命令可以進行編輯,也可以選擇File/Run命令運行腳本。
三、創建和操作具有複雜關係的對象
常量;變量,變量和屬性的關係;運算符和表達式;表達式:求值的規則,輸入的每一行代碼都是表達式,因爲它總是返回一個值。
對象引用:有的書叫雙詞模式(引用另外一個對象的屬性值作爲自己的值。);
數組和集合等:數組(一個系列的數據組合),數組和變量的關係;
流程控制:選擇和循環,條件選擇。
聞着春天裏散發出來的花香味,忍不住去欣賞那些美麗的花朵,它們被一些長得井然有序的綠葉襯托着;穿過長着雜亂小草的林間小道,驚動了樹上正在戲耍的鳥兒,它們飛向兩旁看似有序又無序的青松翠竹林中,瞬間不見了蹤影;走在馬路上,兩旁定有一排排梧桐或揚柳,如果你的眼力好的話,發現樹的皮膚上有好多忙忙碌碌的螞蟻;白天工作了一天,夜晚來到一個幽靜的小河邊,擡頭望着滿天繁星,低頭看着水中的倒影,心情好多了,不禁思潮起伏。
不管是四時花草樹木,八方飛鳥走獸,無論是靜止着的、運動着的,哪怕是無序的事物,都存在有一定的內在關係。那麼到底是怎麼樣一個內在關係呢?要有具體的內容啊!
最簡單最常見的是對稱關係,例如昆蟲的翅膀,羊頭上的角;還有直線關係,例如路邊兩邊的樹;最沒規律的關係是那些運動着的和雜亂無章的事物,例如飛動着的鳥兒,忙碌着的螞蟻,水中冒出的氣泡,就把它們稱爲隨機關係吧;既沒有規律又很雜亂的關係是噪音關係,蝴蝶翩翩起舞的軌跡,雜亂的野草的分佈狀態;最美麗的關係是那些按一定的曲線而存在的事物,例如波瀾起伏的水面,它是正弦波或者正弦波的疊加關係,小朋友向天空方向扔出的石塊的運動軌跡,就是一條拋物線;還有起伏不平的崇山峻嶺、狐狸搖動着的尾巴,曲折前進的蛇類等等無不存在着某種關係。
既然能找出這些事物的內在關係,能否用MAXScript來再現這種關係呢?答案就是一個字:能!
1、常量和變量
要實現複雜的程序,首先要掌握數據的表現方法,在腳本中用常量和變量來表現。現實生活中有些事物是相對固定的,有些則是經常變化的。就拿人來說吧,人的性別是不變的,年齡是每過一年就增加一歲;拿我們用的電腦硬盤來說,總容量是不變的,但可用磁盤空間經常是變化的。在MAXScript中,把那些不能改變的量稱爲常量(Literal Constants,字面常量,簡稱常量),可以變化的量稱爲變量(Variables)。
例如15,“Hello”等,從字面上一看就知道這些值是不能改變的,所以把常量又叫做字面常量,用age來年齡,而這個age是在變化的,所以age是變量。那麼別人問李四的age是多少,去年問的時候李四也許回答是21歲,今年有人問李四,當然就是回答是22歲了。如果他回答age,那別人就不知道他的年齡了,他必須說出具體的數字出來。age是變量名,22歲是變量值。那麼在MAXScript中該怎麼表示?那就是給變量賦值,等於號是賦值符號,輸入 age=22 ,腳本監聽器中返回22,又如李四的銀行存款 , 建行的是20000元,輸入 money1=20000 , 工行的是36000,輸入 money2=36000,請問用money表示李四這兩個銀行存款的總和,程序代碼應該怎麼寫?沒學過編程的人也許可能輸入money=56000,呵呵,這個也沒有什麼錯,不過輸入money= money1+ money2,結果如何?一分錢也少不了,照樣返回56000,程序自己完成了加法計算,減輕了我們大腦的負擔。
變量就像一個容器,可以存儲不同類型的數據。數目,字符串,數組,對象都可以存放在變量中。
現在就來詳細解說變量。在程序運行期間,系統會在內存中爲程序分配一塊內存單元,用來存儲各種不同類型的數據。而想訪問這個內存單元,就要使用一個標識符來標識它,就像你到單位去找一個人一樣,先要告訴這個人的名字。這個標識符就是變量名,內存單元中裝載的數據就是變量值。程序可以通過變量名來讀取數據。把數據放到內存中就是爲變量賦值,=是賦值符號。我們把上面的寫在一塊,如下:
money1=20000
money2=36000
money= money1+ money2
當程序運行第一句代碼時,分配一塊內存,用money1作爲這塊內存的變量名,然後把2000裝入這塊內存中作爲變量的值,第二句同上,運行到第三句時,分配一塊內存,取名爲money,程序取出money1和 money2中的數據讓它們相加 ,把相加的結果56000裝入名爲money的內存單元中。
現在回憶一下以前是如何改變對象屬性的。例如前面提到改變box01的長度,語法是 $Box01.length=20,這和給變量賦值的語法是一樣的。從這裏可以看出,對象的屬性實質上也是一個變量,只因爲是對象特有的變量,就稱爲屬性了,訪問變量直接用變量名,訪問屬性要在屬性名前加上對象名,中間用一個小數點隔開,稱爲點語法,這就是變量和屬性的關係。既然可以把一個變量分配給另外一個變量,就可以把一個對象的屬性分配給一個變量,也可以把一個變量分配給一個對象的屬性,還可以把一個對象的屬性分配給另外一個對象的屬性,有的人把它稱爲雙詞模式。有的人會想,現在到了這一步,索性來個一不做二不休,把整個對象分配給一個變量,以後訪問時更加方便,這也是允許的。舉例如下:
height=50
myBox=box length:20 width:20 height:height
zPos=mybox.height+10
mySphere=sphere radius:10 pos:[0,0,zPos]
第二句中height:height是把變量height分配給這個box的height屬性,第三句是算出Z軸的座標,準備分配給後面要產生的球體的Z軸座標,第四句pos:[0,0,zPos] 是把變量zPos的值分配給球體的Z軸。
注:鑑於要有一定基礎,變量的作用範圍(即全局變量和局部變量)和增量放在第五部分了。
2、數組和集合
數組(array),是一系列有序數據的集合,每一個數據就是一個元素,它有兩種表現形式:
#( 表達式 1 , 表達式2 …… )
#( ) --空數組
例如把常量15,16.2,80這幾個數據放在一起,就可以用數組存放:
#(15,16.2,80)
把變量放到數組中:
x=10
y=20
z=30
#(x,y,z)--返回#(10, 20, 30)
把數組分配給一個變量 :
arr=#(x,y,z)--返回#(10, 20, 30)
訪問數組元素:
arr[2]--第二個元素,返回20
改變數組元素的值:
arr[1]=236
arr--再次訪問數組時,其內部元素的值已經改變了,返回#(236, 20, 30)
選擇集(selectionSet)
這個對應於手工在場景中命名的選擇集,也可以用腳本命名選擇集。
先創建幾個對象,輸入:
b01=box pos:[-50,-50,0]
b02=box pos:[50,-50,0]
sp01=sphere pos:[0,0,0]
sp02=sphere pos:[50,50,0]
sp03=sphere pos:[50,50,0]
下面的代碼創建一個名字爲"盒子集"的選擇集,它包含有剛纔創建的兩個box:
selectionSets["盒子集"]=$box*
下面的代碼創建一個名字爲"球體集"的選擇集,它包含有剛纔創建的兩個sphere:
selectionSets["球體集"]=$sphere*
在工具欄中點擊命名選擇集按鈕,可以看到剛纔創建的選擇集,輸入selectionSets["盒子集"][1],訪問第一個元素,結果返回:
$Box:Box01 @ [-50.000000,-50.000000,0.000000]
輸入move selectionSets["球體集"] [0,20,0],整個“球體集”向Y軸移動了20。
集合(collections)
在使用MAXScript 時有許多值都是一系列的集合,很明顯的有數組、 通配符路徑名選擇集,內置對象集。集合不用你特意去指定,它是系統自動完成的。例如我們現在用下面的代碼創建一些對象:
box()
box pos:[60,0,0]
box pos:[-60,0,0]
sphere pos:[0,70,0]
omnilight pos:[0,-100,150]
circle()
既然是自動完成的,那我們就直接訪問了。訪問場景中的對象可以使用路徑名,前面講過用路徑名,訪問單個對象用美元符號$加上對象的名字就可以了,但如果要把剛纔創建的三個box一起向Y軸移動100,應該怎麼辦呢?使用帶有通配符的路徑名:
$box*.pos.y=100
$box* 表示場景中所有以box開頭的對象,*表示任意個字符。
如果把那三個box和一個球體一起操作呢?它們都是內置幾何體對象集:
select geometry --選擇所有的幾何 體
geometry.pos=[0,0,0]-把所有的幾何體移動到時原點
選擇所有的對象,下面提供有兩種方式:
select $* --以路徑名的方式選擇所有的對象
select objects --以內置對象集的方式選擇所有的對象
下面把內置對象集列出,以供參考:
對象集 (ObjectSet)
對象集是描述主場景對象種類的。
objects -- 全部對象
geometry -- 標準的 3ds Max 分類(categorie)
lights--燈光
cameras--攝像機
helpers--幫助物體
shapes--二維形狀
systems--系統對象
spacewarps--空間扭曲
selection -- 當前選擇的對象
集合的屬性
接着上面的內容進行操作。
輸入selectionSets["盒子集"].center 返回"盒子集"中心 [0,-50,12.5]
輸入selectionSets["球體集"].count 返回"球體集"總數3
輸入selectionSets["盒子集"].max 返回"盒子集"最大範圍[62.5,-37.5,25]
輸入selectionSets["盒子集"].min返回"盒子集"最小範圍[-62.5,-62.5,0]
這四個屬性適用於對象集(ObjectSet),路徑名(PathName)、選擇集(selectionSet)等集合
變量、數組和集合之間的關係
從數組、對象集、路徑名、選擇集等之間的定義可以知道,它們都是存放的數據,這些數據類型可以是數目,字符串,也可以是對象,這和變量是一樣的用途,這麼說來,它們的實質是變量的集合,因此把它們統稱爲集合(collections)。集合中的每一個元素都是一個變量,所以訪問集合中的元素可以像訪問數組中的元素一樣來訪問,對於數組中的每一個元素可以像對待變量一樣。輸入:
Box()
Sphere()
然後選擇它們,再輸入:
selection[1]
監聽器窗口中會返回第一個元素的路徑名和位置。
3、表達式
要得到想要的值,就要對數據進行計算,計算規則不同,其結果也就不一樣。例如2和3兩個數據,按減法規則來計算,結果是-1,按加法來計算,結果是5。像這種按某種計算規則來求值的公式,MAXScript把它稱爲表達式(expressions,簡寫爲expr),表達式是一種求值的規則。從語法的角度來看,用操作符把操作對象連接起來的式子稱爲表達式,有的編程語言也叫語句(statements) ,意思一樣。我們以前在學校裏學的加減乘除四則運算也可以看作是最簡單的表達式了,也就是說我們沒有編程之前就會使用表達式了。前面提到的常量,變量,數組,構造器,函數以及輸入的任何一句代碼,都是表達式,因爲總會返回一個值。 MAXScript有大多數編程語言常見的表達式,例如數學表達式(Math Expressions),比較表達式(comparison expressions ),邏輯表達式(Logical expressions ),以及MAXScript特有的關聯表達式(Context expressions ),後面的教程會涉及。下面是有效的表達式:
x=1
y=2*3+5
print y --輸出y的值到屏幕上
上面的代碼可以寫成一塊,這就是塊表達式(block-expression):
(
x=1
y=2*3+5
print y
)
還可以把這三行代碼寫成一行,用分號;把它們分開:
( x=1; y=2*3+5; print y )
幾個常用的表達式
比較表達式(Comparison Expressions)
比較表達式總是返回兩個布爾值中的一個,true 和 flase ,true(真)表示條件成立,flase(假)表示條件不成立。這個表達式特別有用,很多情況下都會用到,常常和 if ……then ……結合使用。比較表達式在小學數學中就有了,就是那些大於,等於,小於號連接起來的式子,有一點要注意,那就是等於號要用兩個=表示,一個是表示賦值符號。示例如下:
10= =10 --條件成立,返回 true
11= =10 --條件不能成立,返回 flase
11!= 10 --條件成立,返回 true
11>= 10 --條件成立,返回 true
11<= 10 --條件成立,返回 true
x=8
y=36
x>y--條件不能成立,返回 flase
邏輯表達式(Logical Expressions)
邏輯表達式有or、and 和 not 三種,同比較表達式一樣返回一個布爾值 true 和 flase之一,它是對比較表達式的進一步組合計算,也就是說對true和flase的重新組合,得到一個新的布爾值。
or(或),只要兩者有一個成立,那麼整個都成立,例如在中國發現了外星人或者在美國發現了外星人,只要有誰發現了一個,那麼就說明這個宇宙上還有另外的生命。示例:
x=8
y=36
x= =8 or y= =8 --有一個成立,返回 true
and(與),兩者要同時成立,才能整個成立,否則都不能成立。真是有福同享,有難同當的患難之交啊,一損俱損,一榮俱榮,關係密切。從中也可以看出些人生道理出來,那就是兩個人合夥做事一定要同心協力,任何一個懷有異心,就辦不成事。示例:
x=8
y=36
x= =8 and y= =8 --有一個成立,另一個不成立,返回 flase
x= =8 and y= =36 --兩者同時成立,返回 true
not(非),對立派,總是唱反調,你要上山,它就要下水,你說是對的,它偏要說是錯的。示例:
a=13
a>10 -- 返回 true
not a>10 -- 因爲a>10 是true,所以not a>10返回 flase
4、流程控制
通過以前的學習,我們知道使用構造器來創建單個的對象,現在就來大量創建對象,輕鬆地完成手工要費很大的力氣才能完成的事。
for 循環
輸入 :
for i in 1 to 4 do box pos:[i*50,0,0]
看看下面的flash動畫可以形象的理解for循環。
for循環動畫
if 條件
x=20
y=65
if x>y then print x else print "x沒有y大"
--第三句的中文意思是:如果 x 大於 y 那麼 打印 x 否則 打印 "x沒有y大" ,結果返回 "x沒有y大"。繼續輸入:
x=70
if x>y then print x else print "x沒有y大"
--結果返回70
綜合使用
fc=freecamera pos:[-100,0,0]
rotate fc (EulerAngles 90 0 -90)
move fc [-100,0,0]
for i in 1 to 6 do sphere pos:[i*30,0,0] segments:16 radius:10
輸入上面代碼,生成場景對象備用
現在根據物體離攝影機的距離調整物體的分段數,攝影機在X軸-100位置上,離它最近的一個球體在X軸30的位置上。測量兩個對象之間距離的函數是distance,輸入:distance fc $sphere01 返回130.0,說明攝影機和左邊的第一個球體之間的距離是130。輸入:
for i in $sphere* do
if distance fc i>160 then i.segs=4
距離攝影機大於160的球體分段數精簡到4,結果如圖3-4所示 。
圖3-4
輸入:
for i in $sphere* do (
if distance fc i>180 then i.segs=8
if distance fc i>240 then i.segs=4
)
注意,代碼塊要用( )包起來,不然程序只執行第一個if語句。這段代碼使距離攝影機大於180的球體分段數精簡到8,大於240的球體分段數精簡到4,結果如圖3-5所示 。
圖3-5
4、動畫原理
設置兩個不同的關鍵幀就可以動畫了。輸入下面的代碼創建球體沒X軸的運動的動畫:
s=Sphere pos:[-50,0,0] radius:10
animate on
(
at time 0 s.pos.x=-50
at time 100 s.pos.x=50
)
四、換一角度——從大家熟悉的操作流程來學習腳本
這是對前面知識的總結,前面是對語法基本概念的學習,現在是從大家操作流程的角度來學習,一縱一橫,讓我們對腳本有個相對全面的瞭解,理論和實際相互聯繫。有了前面的基礎纔好學下面的內容。
第一步:創建物體
創建目標攝影機
方法見示例欄目中《地面和樹-混合材質的用法》一文
第二步:選擇物體
這裏說的是廣義上的選擇,即操作指定的對象。以前要改變場景中的對象,首先要選擇這個對象。在MAXScript中可以在對象沒有被選中的情況下改變對象。這要綜合運用前面講到的路徑名,選擇集,內置對象集,流程控件等等。
遍歷選擇集
先隨便創建幾個對象,然後選擇它們,例如輸入:
box()
box()
sphere()
cylinder()
sphere()
結果如圖4-1所示。
圖4-1
先用手工把它們全部選擇,此時這些被選擇的對象被稱爲選擇(selection),這是對象集之一。輸入下面的代碼進行遍歷選擇的第一個對象,把它們的名字顯示出來:
for i in selection do print i.name
返回結果如下:
"Box02"
"Cylinder01"
"Box01"
"Sphere02"
"Sphere01"
輸入 selection.count,結果返回5,說明選擇的總數是5個。
輸入 selection[1],結果返回$Box:Box02 @ [0.000000,0.000000,0.000000],說明第一個元素是Box02。
這和前面的for循環有什麼區別?前面的是for i in 1 to 4 do box pos:[i*50,0,0] , 變量i的範圍在1到4,這裏是對象集合selection,集合已經存在變量的範圍了,這個變量 i 就是那些對象本身,遍歷的次數就是選擇的總數5,當第一次循環時,程序就找到選擇集中的第一個元素Box02,相當於 selection[1],其它以此類推。下面的代碼進行更復雜的操作,把它們沿X軸間隔爲40個單位進行分開:
for i in 1 to selection.count do selection.pos.x=selection.pos.x+i*40
這裏的i是數目,selection是元素,是元素的索引,根據 i 的值指向對應的元素,在這裏是指向那五個對象之一。
根據指定條件選擇
繼續使用上面創建的對象。
示例1:根據對象是否具備某種屬性來選擇
for i in geometry where hasProperty i "radius" do i.pos=10
在創建的幾何體中把具有半徑屬性的物體選擇出來,並改變半徑大小。結果那兩個球體和一個圓柱體的半徑變成10。where是表示指定的條件, hasProperty i "radius" 是條件,用來判斷對象 i 是否具有 radius 屬性,返回true或者false。
示例2:根據對象是否符合指定類來選擇
for i in Geometry where classOf i = = box do i.pos = i.pos+[0,30,0]
兩個box在原來位置向Y軸移動30,結果如圖4-3所示。
圖4-3
示例3:根據對象的屬性值是否符合指定值來選擇
for i in Geometry where i.pos.y ==0 do i.pos = i.pos+[0,30,0]
Y軸座標爲0的對象都向Y軸移動了30,結果如圖4-4所示。
圖4-4
第三步:變換物體
1、變換方法:postion,move,scale,rotate,前面已經講過。
2、變換座標系和變換中心
變換座標系統:coordsys關聯表達式
[ in ] coordsys world 使用世界空間座標系統
[ in ] coordsys local 使用對象自身的座標系統
[ in ] coordsys parent 使用對象的父座標系統
[ in ] coordsys grid 使用網格座標系統
[ in ] coordsys screen 使用屏幕座標系統
[ in ] coordsys <node> 使用指定的節點對象座標系統,相當於在界面中以拾取的對象作爲座標系統
變換中心: about關聯表達式
about selection 以選擇集爲變換中心
about pivot 以對象的基準點爲變換中心
about coordsys 以當前使用的座標系統的原點爲變換中心
about <node> 以拾取的節點對象的基準點爲變換中心
示例一:基本功
先創建兩個對象作爲示例物體:
cy=cylinder()
b=box length:10 width:50 pos:[100,0,0]
然後分別執行下面的代碼:
rotate b (EulerAngles 0 0 90)
--box旋轉了自身90度
in coordsys world rotate b (EulerAngles 0 0 90)
--box旋轉了自身90度,和上面相同,這是因爲變換中心默認是使用對象自身的基準點。
in coordsys world about coordsys rotate b (EulerAngles 0 0 45)
--圍繞世界座標系旋轉了45度,變換中心是世界座標系中心
in coordsys cy about coordsys rotate b (EulerAngles 0 0 90)
--變換座標系是圓柱體cy,b以圓柱體cy座標系原點爲變換中心旋轉了90度
示例二:小應用--一張桌子四條凳
zhuozi=cylinder radius:80 height:60 wirecolor:white
for i in 1 to 4 do (
dengzi=box length:50 height:40 pos:[100,0,0] wirecolor:white
in coordsys zhuozi about coordsys rotate dengzi (EulerAngles 0 0 (90*i))
)
五、基本的語法知識 1、註釋 註釋在程序執行時會跳過,不會執行註釋部分代碼,它有助於理解代碼的作用,也可以用來說明作者、程序版本等信息。 單行註釋,用兩個--(減號)開頭,後面跟註釋內容。例如: --下面是創建一個box的代碼 box() 多行註釋,以 /* 開頭,以 */ 結尾,中間放入註釋內容。例如: /* 程序名稱:入門教程 作者:mediastime 時間:2005年1月7日 */ a=10 b=9 --下面是if 表達式,用途很大,必須花心思把它掌握 if a>b then print a else print b 2、代碼佈局 縮進,選擇要縮進的代碼,使用Tab鍵可以使它向右縮進,同時按住Shift+Tab鍵可使它向右縮進。 換行,每一行代碼用回車鍵結束,如果把多行代碼寫在一行,之間要用分號隔開。例如: x=1;y=2;z=x+y 如果是一行很長的代碼,是不能用回車鍵的分行的,可用右斜槓連接兩行代碼爲一行,如下: box length:100 width:100 height:20 wirecolor:(color 0 125 222) lengthsegs:10 widthsegs:10 / name:"地面" pos:[0,0,0] 3、變量的範圍 分爲全局變量和局部變量 在程序運行過程中一直起作用的變量稱爲全局變量,只在某一代碼塊內有效的變量稱爲局部變量。 聲明全局變量 global i=10 聲明局部變量 local i=10 示例: global i=6 if i==6 do ( local j=20 j=i+j print j ) 然後輸入 print i 返回 6 ,輸入 print j 返回 undefined,這說明j是局部變量,只在if代碼塊中有效。 一般情況下,聲明可以省略,程序自動判斷是全局變量還是局部變量。 4、增量 在每一次循環過程中變量自己增加一定的量。 i=i+2 i=i-2 i=i*2 i=i/2 可以簡寫成 i+=2 i-=2 i*=2 i/=2 示例: j=0 for i in 1 to 4 do ( j=j+2 print j ) 結果返回: 0 2 4 6 8 OK OK 5、數目值 整數(Integer):例如1,-8 浮點數(Float):實數,例如1.25,60.33 注意點:12與12.0的區別,例如輸入12/100,結果返回0,輸入12.0/100,結果返回0.12,輸入10/100.0,結果也返回0.12,12/100全是整數,所以返回結果也是整數。 整數和浮點數之間可以轉換,輸入12 as float,結果返回 12.0,輸入12.36 as integer,結果返回12。 -- |
/*
這是伏羲先天八卦,乾1兌2離3震4巽5坎6艮7坤8
據易經:天地定位,山澤通氣,雷風相薄相,水火不相射
*/
--第一步
--創建數組爲後面雕刻八卦用
arr8=#(#(0,0,0),#(0,0,1),#(0,1,0),#(0,1,1),#(1,1,1),#(1,1,0),#(1,0,1),#(1,0,0))
--創建八卦的文本
atext=#("乾","兌","離","震","坤","艮","坎","巽")
--創建一個倒角修改器
bev=bevel Level_1_Outline:1.4 Level_1_Height:2 Use_Level_2:1 Level_2_Outline:0 Level_2_Height:30/
use_Level_3:1 Level_3_Outline:-1.8 Level_3_Height:2
for i in 1 to 8 do (
--把八卦文本分佈到八個對應的位置上
atex=text font:"隸書" text:atext prefix:"txt" pos:[0,300,0]
in coordsys world about coordsys rotate atex (eulerangles 0 0 (45*(i-1)))
--創建八卦的卦爻
for j in 1 to 3 do (
r=180+j*30
--創建八卦的卦爻
badd=box pos:[0,r,0] length:16 width:120 height:30 lengthSegs:5 widthSegs:10 heightSegs:4 /
prefix:"badd" wirecolor:white
in coordsys world about coordsys rotate badd (eulerangles 0 0 (45*(i-1)))
--創建給八卦的卦爻雕刻多餘部分的雕刻刀。
if arr8[j] !=0 then(
bsub=box pos:[0,r,-2] length:22 width:30 height:34 lengthSegs:5 widthSegs:10 heightSegs:4/
prefix:"bsub" wirecolor:white
in coordsys world about coordsys rotate bsub (eulerangles 0 0 (45*(i-1)))
)
)
)
--第四步
--合併卦爻並刪除多餘的部分
for i in $badd* do (if i.name != "badd01" then( $badd01+i;delete i))
--合併雕刻刀並刪除多餘的部分
for i in $bsub* do (if i.name != "bsub01" then( $bsub01+i;delete i))
--爲八卦文本加上倒角修改器,並分配材質
for i in $txt* do (
addmodifier i bev
i.material=currentMaterialLibrary["Metal_Dark_Gold"]
)
--雕刻卦爻並分配材質,然後光滑處理
$badd01-$bsub01
delete $bsub01
$badd01.material=currentMaterialLibrary["Metal_Dark_Gold"]
addmodifier $badd01 (meshsmooth())
--結束
--
--創建地面--
ground=plane length:300 width:300 lengthsegs:10 widthsegs:10
modi=Noisemodifier strength:[0,0,31]
addmodifier grass modi
--創建樹--
tree01=box length:0 width:15 height:20 pos:[50,-45,-0.5] material:meditmaterials[1]
copy tree01 pos:[58,4,-0.5]
copy tree01 pos:[58,62,-0.5]
copy tree01 pos:[20,62,-0.5]
copy tree01 pos:[12,4,-0.5]
copy tree01 pos:[10,-45,-0.5]
--創建燈光--
omni1=omnilight castShadows:on raytracedShadows:on pos:[50,-108,110]
omni2=omnilight pos:[15,25,195]
--創建目標攝影機並設置攝影機視圖--
ca=TargetCamera pos:[26,-96,17]
--攝影機的目標
tobj=targetobject pos:[26,-45,19]
--把目標關聯到攝影機
ca.target=tobj
--把當前視圖設置爲目標攝影機視圖
viewport.setCamera ca
--設置材質--
fnamedir=getdir(#maxroot)+"maps/"--貼圖目錄
fnameenv=fnamedir+"Skies/CLOUD2.JPG"--環境貼圖路徑
environmentMap = Bitmaptexture fileName:fnameenv--設置環境貼圖
fnamegrass01=Bitmaptexture filename:(fnamedir+"Ground/GRASS2.JPG")--混合貼圖1的紋理貼圖路徑
fnamegrass02=Bitmaptexture filename:(fnamedir+"Ground/GRYDIRT2.JPG")--混合貼圖2的紋理貼圖路徑
maskmap=Noise()--混合貼圖的遮罩使用噪音貼圖
m=mix map1:fnamegrass01 map2:fnamegrass02 mask:maskmap--創建一個混合貼圖,賦給m
meditmaterials[2].diffusemap=m--材質編輯第二個插槽的difuse貼圖設置成混合貼圖
ground.material=meditmaterials[2]--地面材質使用材質編輯中第二個插槽中的材質