pvzclass是如何實(shí)現(xiàn)的?pvzclass源代碼初步分析(3) PVZ.h 概覽 & Enums
PVZ.h算是把pvzclass中幾乎所有的對(duì)象(除了后來(lái)加上的events相關(guān))都定義了出來(lái),這也使得它是pvzclass核心文件中最主要的,也是最大的文件。
本篇主要把PVZ.h包含的內(nèi)容概括一下,順帶將Enums.h和Classes文件夾下的東西說(shuō)一說(shuō)。

1.PVZ.h概覽
個(gè)人認(rèn)為,大部分人是不會(huì)喜歡看PVZ.h的代碼的。
因?yàn)橐淮蜷_(kāi)PVZ.h,映入眼簾的將是:

(因?yàn)镻VZ.h含有#pragma region和#pragma endregion這種只有Visual Studio才能使用的編譯命令,本篇使用Visual Studio呈現(xiàn)代碼)
這種看上去就很難分析的代碼很容易給人留下極不友好的印象。
再加上下面的定義使用的是不同于常見(jiàn)代碼的方式,這就足以篩掉大部分人了。
既然先細(xì)致地看會(huì)招致混亂,我們就先粗略得看,對(duì)PVZ.h的內(nèi)容形成一個(gè)大致的認(rèn)識(shí)。
我們將大部分代碼先收起來(lái),就可以看出PVZ.h可以大致分為三部分:

可以看到,PVZ類的定義占用了最多行數(shù)的代碼。
而實(shí)際上,PVZ.h還沒(méi)有包含完整的定義。其中附屬的方法由其他的源代碼文件完成。
這些源代碼大部分都在Classes文件夾下的文件之中,剩余的代碼則包含在PVZ.cpp之中。
接下來(lái)按照順序,先把Enums.h說(shuō)一下。
2. Enums
Enum,本來(lái)是一種特殊變量類型,在C/C++中也有。
這種變量在中文語(yǔ)境下一般稱為“枚舉”類型變量。
顧名思義,Enums.h自然是包括了pvzclass中用到的所有enum類的頭文件。
然而它實(shí)際上長(zhǎng)這樣:

沒(méi)錯(cuò),Enums.h和pvzclass.h一樣,也是一個(gè)頭文件索引。
那么Enums文件夾的作用也很顯然了,就是包含上面提到的各個(gè)文件,以及它們對(duì)應(yīng)的源代碼文件。
所幸的是,這些頭文件都是有規(guī)律的,它們都可以劃分為兩個(gè)部分(以下以ProjectileType.h為例):

enum部分為該頭文件下的枚舉變量。ProjectileType.h存儲(chǔ)的當(dāng)然是Projectile Type(子彈類型)了。
順便說(shuō)一句,其中Puff是孢子子彈(就是小噴菇等的子彈),Kernel是玉米粒,ZombiePea就是豌豆射手僵尸等的子彈。
ToString函數(shù)的目的很簡(jiǎn)單,就是將相應(yīng)的枚舉變量轉(zhuǎn)換為描述性的字符串,以ProjectileType.cpp為例:

借助Enums,我們?cè)趯?xiě)基于pvzclass的代碼時(shí),就沒(méi)有必要背過(guò)PVZ中各種類型在PVZ內(nèi)的存儲(chǔ)情況了。需要的時(shí)候,直接去Enums文件夾對(duì)應(yīng)的頭文件查詢即可。
3.PVZ.h中主要的宏定義
回到PVZ.h。開(kāi)頭的宏定義雖然長(zhǎng)得令人發(fā)指,但是接下來(lái)的代碼幾乎完全基于這些宏。
搞明白宏的定義,對(duì)于理解代碼是有很大幫助的。
我們從頭開(kāi)始:

STRING宏很好理解,就是將str和它的字符數(shù)同時(shí)羅列。
這個(gè)宏在pvzclass的代碼中主要作為參數(shù)出現(xiàn)。
SETARG宏和SETARGFLOAT宏對(duì)于不熟悉“指針”概念的人而言可能不好理解。實(shí)際上它們分別相當(dāng)于如下代碼中的第一、二行:
不過(guò),在pvzclass中,這兩個(gè)宏主要是用于修改匯編指令中的部分參數(shù),而pvzclass中匯編指令都是用byte數(shù)組存儲(chǔ)的。
不過(guò)不用擔(dān)心,這兩個(gè)宏依然可以像往常一樣使用,不過(guò)要注意的是這會(huì)同時(shí)修改數(shù)組中的4個(gè)量。

PAGE_SIZE宏……跳過(guò)。
PVZ_BASE宏的作用是獲取PVZ中絕大部分變量的基址,即MemoryAddressList中第一行表示的基址。
當(dāng)然,你不知道什么是基址也無(wú)妨因?yàn)槟阋话阌貌坏?/span>。
(這里出現(xiàn)了Memory類的函數(shù),以后會(huì)講)
PVZBASEADDRESS宏的作用,則是獲取PVZ關(guān)卡中相關(guān)變量的基址。
如果你不熟悉這個(gè)東西,至少需要知道一點(diǎn):沒(méi)有進(jìn)入關(guān)卡時(shí)這個(gè)宏對(duì)應(yīng)的值為0,在關(guān)卡內(nèi)時(shí)則相反,不會(huì)為0。

(接下來(lái)的十個(gè)宏代碼很長(zhǎng),全部截下來(lái)會(huì)被壓縮到失真,建議讀者自己打開(kāi)文件查看相應(yīng)位置的完整代碼)
READONLY_PROPERTY宏和WRITEONLY_PROPERTY宏還算好理解,它們分別是PROPERTY宏的只讀和只寫(xiě)版本。
而PROPERTY宏對(duì)于不很了解C++宏定義的人可以說(shuō)是非常復(fù)雜的。
不過(guò),我們可以結(jié)合具體的例子來(lái)看:

這是一個(gè)double類型的變量。嘗試讀取這個(gè)變量時(shí),返回的是__get_MusicVolume(即getmethod)的返回值;而嘗試對(duì)這個(gè)變量賦值時(shí),這個(gè)值也會(huì)作為_(kāi)_set_MusicVolume(即setmethod)的參數(shù)。
__get_MusicVolume()和__set_MusicVolume()都在PVZ.cpp中有所定義。
說(shuō)白了,通過(guò)PROPERTY宏定義的變量在賦值/取值的形式上仍與一般變量一樣,但實(shí)際賦值/取值的函數(shù)與一般變量不同,需要另行定義。

類似于PROPERTY宏,PROPERTY_BINDING宏也具有只讀和只寫(xiě)版本。
我們比對(duì)以下PROPERTY宏和PROPERTY_BINDING宏的定義:

可以看出,兩者除了是否直接定義了getmethod()和setmethod()以外,可以說(shuō)沒(méi)有別的差別!
PROPERTY_BINDING宏的作用也和PROPERTY宏基本一致,不同的是,PROPERTY_BINDING宏將相應(yīng)的函數(shù)直接完成了定義。
因此,PROPERTY_BINDING宏也可以看成是PROPERTY宏的簡(jiǎn)化版本。
不過(guò)也是因此,接下來(lái)的宏才會(huì)長(zhǎng)得超過(guò)200字符:

T_PROPERTY宏具有整數(shù)版本和只讀版本,是PVZ.h中定義變量的重要的宏。
實(shí)際上,T_PROPERTY宏只是PROPERTY_BINDING宏的一個(gè)應(yīng)用:

T_PROPERTY宏的四個(gè)參數(shù)分別為變量的類型、變量的名稱、getmethod名稱、setmethod名稱和偏移量。
雖然getmethod和setmethod幾乎用不到,但還是得在定義時(shí)寫(xiě)出。
整數(shù)版本和只讀版本類似,只是相應(yīng)地調(diào)整了變量類型,或是換用了只讀版本的宏。
4.PVZ類
PVZ類雖然占用了最多函數(shù)的代碼,但是收起大部分代碼后,可以看到PVZ主要包含四個(gè)部分,即Memory類、屬性、附屬類和直屬方法:

構(gòu)造函數(shù)和析構(gòu)函數(shù)的定義在PVZ.cpp中,這里先跳過(guò)。
Memory類中定義了讀取、寫(xiě)入PVZ主程序的變量需要的函數(shù)和變量。
附屬類中定義的各種類,實(shí)際上都是PVZ的關(guān)卡中直接或間接出現(xiàn)的各種對(duì)象。植物對(duì)象、僵尸對(duì)象,等等,都在附屬類中有所對(duì)應(yīng)。
而properties中定義的,則是附屬類中沒(méi)有包含的其他PVZ內(nèi)部變量。
直屬方法中包含的,則是一些常用函數(shù)和獲取PVZ內(nèi)部對(duì)象使用的函數(shù)。
以后的篇章將對(duì)上述內(nèi)容詳細(xì)解讀。

按照順序,下一篇將從Memory類開(kāi)始解讀。
前面丟下的Asmfuntion.h也會(huì)一并補(bǔ)上。