MaxScript學(xué)習(xí)筆記目錄
大家好,我是阿趙
之前寫了一些MaxScript的學(xué)習(xí)筆記,里面實(shí)現(xiàn)的功能不算很復(fù)雜,所以都是使用了偏向于面向過程的方式去編寫的。
我本人其實(shí)是比較習(xí)慣用面向?qū)ο蟮姆绞饺ゾ帉懘a。關(guān)于面向過程和面向?qū)ο笾g的優(yōu)缺點(diǎn)對比,各位如果不是很熟悉的話,有空可以去自行查詢了解一下。按我自己的理解簡單概括一下:
1、面向過程運(yùn)行的效率高,但如果代碼邏輯復(fù)雜的時(shí)候,修改和維護(hù)的難度會比較大
2、面向?qū)ο笮阅茌^差,內(nèi)存也占用得比較多,但它易于管理和維護(hù),擴(kuò)展性好
在編寫MaxScript的時(shí)候,可以用Struct結(jié)構(gòu)體來部分實(shí)現(xiàn)面向?qū)ο?,下面來介紹一下。
一、Struct的基礎(chǔ)使用
1、例子:
在說理論之前,先看一個(gè)簡單的例子:
(
local TestPerson
local PrintPerson
struct person (name,age,sex,height = 175)
fn TestPerson =
(
local Tom = person()
Tom.name = "Tom"
Tom.age = 23
Tom.sex = "male"
Tom.height = 180
PrintPerson Tom
local Bill = person name:"Bill" age:19 sex:"male"
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = "";
content +=("name:"+ obj.name+"\n")
content +=("age:"+ (obj.age as string) + "\n")
content += ("sex:"+obj.sex+ "\n")
content += ("height:"+(obj.height as string)+"\n")
print content
return "ok"
)
TestPerson()
)
運(yùn)行代碼,可以看到打印輸出:
"name:Tom
age:23
sex:male
height:180
"
"name:Bill
age:19
sex:male
height:175
"
"ok"
2、Struct的創(chuàng)建和屬性使用
從上面的例子可以看出,我定義了一個(gè)叫做person的結(jié)構(gòu)體。這個(gè)person結(jié)構(gòu)體里面有以下幾個(gè)屬性:name、age、sex、height,其中height是有默認(rèn)值175的
從單詞的意思上就可以知道,這是一個(gè)人物信息的結(jié)構(gòu)體,它包含了名字、年齡、性別、身高這4個(gè)屬性。
接下來使用person結(jié)構(gòu)體來創(chuàng)建對象。
從上面的例子里面可以看出,有2種方式可以創(chuàng)建對象
1.創(chuàng)建一個(gè)空的person
local Tom = person()
Tom.name = "Tom"
Tom.age = 23
Tom.sex = "male"
Tom.height = 180
用struct名稱加括號,可以創(chuàng)建出這個(gè)struct的對象。
然后可以在對象后面接”.屬性名稱”,來給對象身上的屬性賦值。
2.創(chuàng)建的時(shí)候指定參數(shù)
local Bill = person name:"Bill" age:19 sex:"male"
在創(chuàng)建的時(shí)候,也可以直接用屬性的”名稱:值”的方式直接賦值給屬性。
3、Struct內(nèi)部定義方法
除了簡單的指定一些變量屬性,Struct還可以寫函數(shù)在里面,然后從外部調(diào)用函數(shù)。
把上面的例子稍作修改,變成這樣:
(
local TestPerson
local PrintPerson
struct person (
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
local content = "";
content +=("name:"+ this.name+"\n")
content +=("age:"+ (this.age as string) + "\n")
content += ("sex:"+this.sex+ "\n")
content += ("height:"+(this.height as string)+"\n")
)
)
fn TestPerson =
(
local Tom = person()
Tom.name = "Tom"
Tom.age = 23
Tom.sex = "male"
Tom.height = 180
PrintPerson Tom
local Bill = person name:"Bill" age:19 sex:"male"
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = obj.GetPersonInfoString()
print content
return "ok"
)
TestPerson()
)
運(yùn)行腳本,會發(fā)現(xiàn)結(jié)果和之前是一樣的。
這里我在person這個(gè)結(jié)構(gòu)體里面定義了一個(gè)叫做GetPersonInfoString的函數(shù),然后把自身的信息往外返回,在打印信息的時(shí)候,只需要向person對象調(diào)用GetPersonInfoString函數(shù),就得到了需要打印的結(jié)果了。
4、注意事項(xiàng)
1.struct內(nèi)部的變量和函數(shù)的分隔
從上面的例子可以看出,struct的變量和函數(shù),都必須用逗號分割。特別是函數(shù),很容易漏掉。不過在最后一個(gè)變量或者函數(shù)后,就不能再加逗號了。
2.struct的變量和方法命名
(1)struct內(nèi)部調(diào)用外部變量
來看一段例子:
(
local TestPerson
local PrintPerson
local testVal = 1
struct person (
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
testVal = testVal +1
local content = testVal as string
return content
)
)
fn TestPerson =
(
local Tom = person()
Tom.name = "Tom"
Tom.age = 23
Tom.sex = "male"
Tom.height = 180
PrintPerson Tom
local Bill = person name:"Bill" age:19 sex:"male"
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = obj.GetPersonInfoString()
print content
return "ok"
)
TestPerson()
)
這段例子是從上面的例子改的,需要注意的地方是,我在struct外部定義了一個(gè)局部變量testVal = 1,然后在struct內(nèi)部使用這個(gè)testVal并對其進(jìn)行賦值。
運(yùn)行腳本,會發(fā)現(xiàn)打印如下:
"2"
"3"
"ok"
可以看出,在struct內(nèi)部是可以調(diào)用外部的變量的。
(2)struct內(nèi)部變量和外部變量重名
再對這個(gè)例子進(jìn)行小修改:
(
local TestPerson
local PrintPerson
local height = 1
struct person (
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
height = height +1
local content = height as string
return content
)
)
fn TestPerson =
(
local Tom = person()
Tom.name = "Tom"
Tom.age = 23
Tom.sex = "male"
Tom.height = 180
PrintPerson Tom
local Bill = person name:"Bill" age:19 sex:"male"
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = obj.GetPersonInfoString()
print content
return "ok"
)
TestPerson()
print ("height:"+(height as string))
)
這次我把testVal改名了,改成和Struct里面的height變量重名了。
運(yùn)行腳本,可以看到:
"181"
"176"
"height:1"
從這里我們可以看出來,如果Struct的變量和外部重名了,在使用的時(shí)候,會使用內(nèi)部的變量。
3.內(nèi)部參數(shù)問題
再對例子做小修改:
(
local TestPerson
local PrintPerson
local height = 1
struct person (
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
height = height +1
local content = height as string
return content
),
fn SetHeight height =
(
height = height
)
)
fn TestPerson =
(
local Bill = person name:"Bill" age:19 sex:"male"
Bill.SetHeight 200
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = obj.GetPersonInfoString()
print content
return "ok"
)
TestPerson()
print ("height:"+(height as string))
)
這次,我加了一個(gè)SetHeight 函數(shù)在struct里面,傳進(jìn)去一個(gè)height的變量,然后很莫名其妙的height = height,因?yàn)閔eight這個(gè)名字出現(xiàn)在了3個(gè)地方,首先是struct外部的變量,然后是struct本身的變量,最后是函數(shù)的傳入變量,那么這個(gè)height究竟是代表了哪個(gè)?
看看打印結(jié)果
"176"
"height:1"
可以看出,struct內(nèi)部的height變量并沒有收到height = height的影響,struct外部的height變量也沒有受到height = height的影響,這里賦值的只是函數(shù)傳進(jìn)去的height參數(shù)。
再進(jìn)行一下修改:
(
local TestPerson
local PrintPerson
local height = 1
struct person (
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
height = height +1
local content = height as string
return content
),
fn SetHeight height =
(
this.height = height
)
)
fn TestPerson =
(
local Bill = person name:"Bill" age:19 sex:"male"
Bill.SetHeight 200
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = obj.GetPersonInfoString()
print content
return "ok"
)
TestPerson()
print ("height:"+(height as string))
)
這次把SetHeight 函數(shù)的內(nèi)容改成了
this.height = height
這次的輸出結(jié)果是:
"201"
"height:1"
可以看出,struct的height被賦值了。
4.公共和私有變量函數(shù)
對上面的例子再做修改:
(
local TestPerson
local PrintPerson
struct person (
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
return (this.GetWeightStr())
),
fn SetWeight val =
(
this.weight = val
),
private weight = 1,
private fn GetWeightStr =
(
local content = this.weight as string
return content
)
)
fn TestPerson =
(
local Bill = person name:"Bill" age:19 sex:"male"
Bill.SetWeight 100
PrintPerson Bill
)
fn PrintPerson obj =
(
local content = obj.GetPersonInfoString()
print content
return "ok"
)
TestPerson()
)
可以看到在struct里面,多了一個(gè)私有的變量weight,還有一個(gè)私有的函數(shù)GetWeightStr。
這樣寫是允許的,這個(gè)weight變量和GetWeightStr方法,是只有struct內(nèi)部才能使用,如果在外部調(diào)用,就會報(bào)錯(cuò)。
還有一點(diǎn)值得注意的是,在struct里面,可以用一個(gè)public或者private定義一列連續(xù)的變量或者函數(shù)
于是我們可以把struct內(nèi)部的代碼改成這樣:
struct person (
public
name,
age,
sex,
height = 175,
fn GetPersonInfoString =
(
return (this.GetWeightStr())
),
fn SetWeight val =
(
this.weight = val
),
private
weight = 1,
fn GetWeightStr =
(
local content = this.weight as string
return content
)
)
在public下面的一段變量和函數(shù),都是公共的,在private下面的一段變量和函數(shù),都是私有的。
public和private沒有嚴(yán)格的順序和數(shù)量,可以先private后public也行,或者先private再public再private都可以。
5.總結(jié)
說了這么多廢話,對于本身熟悉其他語言編程的朋友來說,肯定覺得很無聊。其實(shí)我是為了照顧本身對編程不是特別熟悉的朋友,上面的實(shí)驗(yàn)得出了一個(gè)結(jié)論,struct里面的變量定義,和一般的腳本語言沒區(qū)別,作用域是優(yōu)先當(dāng)前結(jié)構(gòu)本身,然后才是往上一級。然后如果要指定struct本身,可以使用this來指定。
然后關(guān)于public和private,也是和一般腳本區(qū)別不大,它可以通過一個(gè)public或者private標(biāo)記一系列連著的變量和函數(shù),
二、關(guān)于面向?qū)ο蟮乃伎?/h2>
通過上面的例子,我們似乎看到了面向?qū)ο蟮囊唤z希望,對于復(fù)雜的邏輯,我們可以通過Struct結(jié)構(gòu)定義類似于類(Class)的結(jié)構(gòu),然后把數(shù)據(jù)都封裝在結(jié)構(gòu)體的對象里面,然后定義方法,把處理的邏輯都寫在結(jié)構(gòu)體里面。最后,在外部調(diào)用時(shí),只需要構(gòu)建對象,并且傳入數(shù)據(jù),剩下的邏輯都在對象內(nèi)部處理。
不過Struct不是Class,它的功能比較有限。比如如果按照嚴(yán)格的概念定義,面向?qū)ο髴?yīng)該包含封裝、繼承、多態(tài)。
從上面的例子看,Struct實(shí)現(xiàn)封裝是沒問題的,以為他有public和private的定義。
繼承和多態(tài),struct并沒有直接現(xiàn)成的方法可以做到。如果非要說能實(shí)現(xiàn)繼承,也可以通過定義一個(gè)方法,在創(chuàng)建子類的時(shí)候,把父類傳進(jìn)去,然后復(fù)制所有父類變量和方法的定義,最后在子類實(shí)現(xiàn)重寫,覆蓋父類的方法,也能勉強(qiáng)能實(shí)現(xiàn)。但我個(gè)人感覺,這樣子做,變成了是為了實(shí)現(xiàn)面向?qū)ο蠖鴮?shí)現(xiàn),好像有點(diǎn)跑偏了。
從我個(gè)人的理解,面向?qū)ο蟮哪康氖菫榱俗尨a變得條例清晰,便于管理。對象內(nèi)部的問題對象自己解決,外部只負(fù)責(zé)調(diào)用和得到結(jié)果?;谶@個(gè)理念,我覺得不一定非要實(shí)現(xiàn)繼承和多態(tài),只要在編寫代碼的時(shí)候能比較清晰的劃分業(yè)務(wù)范圍,就可以使用面向?qū)ο蟮姆绞饺憁axscript的腳本了。
三、相對完整的應(yīng)用例子
這里寫了一個(gè)獲取一個(gè)biped骨骼所有骨骼的信息的腳本,通過這個(gè)腳本,可以看看較為具體的寫法。
1、完整代碼:
(
--function
local CheckOneObj
local PrintBoneInfo
local OnPickFun
local AddBoneInfoToDict
local GetBoneInfoByName
--var
local TestPickUI
local boneNameList;
local boneDict
struct TransformInfo
(
pos,
rotation,
scale,
fn SetData obj =
(
this.pos = obj.transform.pos
this.rotation = obj.transform.rotation
this.scale = obj.transform.scale
),
fn GetPrintString =
(
local content = ""
content += "pos:"+(this.pos as string)+"\n"
content += "rotation:"+(this.rotation as string+"\n")
content +="scale:"+(this.scale as string+"\n")
return content
)
)
struct BoneInfo
(
public
name,
transform,
children,
parent,
fn SetData obj =
(
this.name = obj.name
this.transform = TransformInfo()
this.transform.SetData obj
this.SetParent obj
this.SetChildren obj
),
fn GetInfoString =
(
local content = this.GetNameString() + this.GetTransformString()+this.GetParentString()+this.GetChildrenString()
return content
),
private
fn SetParent obj =
(
if obj.parent == undefined then
(
this.parent = undefined
)
else
(
this.parent = obj.parent.name
)
),
fn SetChildren obj =
(
local childrenList = obj.children
if childrenList != undefined and childrenList.count >0 then
(
this.children = #()
for i in 1 to childrenList.count do
(
append this.children childrenList[i].name
)
)
else
(
this.children = undefined
)
),
fn GetNameString =
(
local content = "----------\n";
if this.name == undefined then
(
content += "name:null\n"
)
else
(
content += "name:"+this.name+"\n";
)
return content
),
fn GetTransformString =
(
local content = "";
if this.transform != undefined then
(
content = "transform:\n";
content += this.transform.GetPrintString()
)
else
(
content = "transform:null\n"
)
return content;
),
fn GetParentString =
(
local content = "";
if this.parent == undefined then
(
content = "parent:null\n"
)
else
(
content = "parent:"+this.parent+"\n";
)
),
fn GetChildrenString =
(
local content = "";
if this.children == undefined or this.children.count == 0 then
(
content = "children:null\n"
)
else
(
content = "children:"
for i in 1 to this.children.count do
(
content += this.children[i]
if i<this.children.count then
content +=","
)
content +="\n"
)
return content
)
)
fn AddBoneInfoToDict info =
(
if boneNameList == undefined then
boneNameList = #()
if boneDict == undefined then
boneDict = #()
local index = findItem boneNameList info.name
if index <=0 then
(
append boneNameList info.name
append boneDict info
)
else
(
boneDict[index] = info
)
)
fn GetBoneInfoByName val =
(
if boneNameList == undefined or boneNameList.count == 0 then
return undefined
local index = findItem boneNameList val
if index <=0 then
(
return undefined
)
else
(
return boneDict[index]
)
)
fn CheckOneObj obj =
(
local info = BoneInfo()
info.SetData obj
AddBoneInfoToDict info
local childrenList = obj.children
if childrenList != undefined and childrenList.count >0 then
(
for i in 1 to childrenList.count do
(
CheckOneObj childrenList[i];
)
)
)
fn PrintBoneInfo =
(
if boneDict != undefined and boneDict.count >0 then
(
for i in 1 to boneDict.count do
(
print(boneDict[i].GetInfoString())
)
)
)
fn OnPickFun =
(
if $ == undefined then
return 0
boneNameList = #()
boneDict = #()
CheckOneObj $
PrintBoneInfo()
)
rollout TestPickUI "Untitled" width:199 height:177
(
button 'btn1' "pick" pos:[51,48] width:110 height:31 align:#left
on btn1 pressed do
(
OnPickFun()
)
)
createDialog TestPickUI
)
2、執(zhí)行腳本的結(jié)果
運(yùn)行腳本,會看到只有一個(gè)按鈕:
創(chuàng)建一個(gè)biped骨骼
選擇biped的根節(jié)點(diǎn),然后點(diǎn)擊pick按鈕
會發(fā)現(xiàn)輸出了很多打印。以橫線分割,每一段是一根骨骼的信息。
3、代碼說明:
1.struct的說明
這個(gè)腳本里面定義了2個(gè)結(jié)構(gòu)體,分別是TransformInfo和BoneInfo。其中TransformInfo作為BoneInfo里面的一個(gè)變量,記錄了骨骼的Transform信息。
BoneInfo除了Transform信息,還有名字、子節(jié)點(diǎn)、父節(jié)點(diǎn)的信息。
在使用方面,都是直接新建對應(yīng)的對象,然后用SetData函數(shù)把物體對象傳進(jìn)去,然后在對象內(nèi)部進(jìn)行的數(shù)據(jù)分析和記錄。
在最后,調(diào)用了BoneInfo的GetInfoString函數(shù),獲取物體的各種參數(shù)。而每一種屬性的參數(shù),都是有獨(dú)立的方法去組建打印的字符串。如果想修改其中一種信息,可以只修改對應(yīng)的方法。
2.數(shù)據(jù)存儲
腳本里面有寫函數(shù),在這個(gè)例子里面是沒有用到的,我是想順便展示一下怎樣去做這個(gè)事情。文章來源:http://www.zghlxwxcb.cn/news/detail-419886.html
fn AddBoneInfoToDict info =
(
if boneNameList == undefined then
boneNameList = #()
if boneDict == undefined then
boneDict = #()
local index = findItem boneNameList info.name
if index <=0 then
(
append boneNameList info.name
append boneDict info
)
else
(
boneDict[index] = info
)
)
fn GetBoneInfoByName val =
(
if boneNameList == undefined or boneNameList.count == 0 then
return undefined
local index = findItem boneNameList val
if index <=0 then
(
return undefined
)
else
(
return boneDict[index]
)
)
本來事情很簡單,如果有dictionary或者哈希表這類的數(shù)據(jù)類型,直接用key和value來存儲是很簡單的事情。但MaxScript并沒有這樣的類型,所以我就用其他方法實(shí)現(xiàn)了。
這里是建了了2個(gè)數(shù)組,一個(gè)是存儲骨骼的名字boneNameList,一個(gè)是存儲名字對應(yīng)的對象boneDict,保證兩個(gè)數(shù)組的下標(biāo)是一樣的,然后通過函數(shù)AddBoneInfoToDict添加數(shù)據(jù),通過函數(shù)GetBoneInfoByName來獲取數(shù)據(jù),獲取的時(shí)候,先通過名字判斷是否在boneNameList數(shù)組里存在,如果存在,返回了下標(biāo),就通過下標(biāo)去boneDict數(shù)組拿對象。文章來源地址http://www.zghlxwxcb.cn/news/detail-419886.html
到了這里,關(guān)于阿趙的MaxScript學(xué)習(xí)筆記分享十四《Struct結(jié)構(gòu)體的使用和面向?qū)ο蟮乃伎肌返奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!