跳轉到內容

文明/文明IV/Modding/教程/Python 教程/簡介

來自華夏公益教科書

Python 可以用來在文明 4 中實現令人驚訝的事情,但前提是你必須瞭解它。現在,雖然我可能不是最適合撰寫本教程的人,但我感覺我對如何用它來修改遊戲非常熟悉。請隨時編輯我在這裡寫的任何內容,因為本教程遠未完成。本教程也釋出在 CFC 這裡。我會嘗試更新維基中的任何更新,儘管顯然維基上的格式比較嚴格。

如果你想了解一些關於 python 的知識,我建議你看看 維基百科條目python 網站

簡單介紹一下 python 在文明 4 中的能力

Python 可以

  • 用來建立複雜的指令碼,在觸發器觸發時執行事件。例如,你可以讓它在佔領一個宗教聖城時,所有信仰該宗教的文明都向你宣戰。
  • 用來編輯介面。幾乎所有的遊戲介面都是由 python 動態生成的,可以在 python 中進行編輯。你可以建立新的按鈕,改變文明百科的佈局,改變科技樹的生成方式... 甚至,如果你願意,可以在螢幕角落新增一隻跳舞的小豬 - 伴隨音樂(雖然我不認為有人嘗試過,但我確信這是可行的)。但這也有侷限性 - 例如,滑鼠懸停文字更適合使用 SDK。
  • 用來生成新的地圖型別。我還沒有嘗試過,所以本教程可能不會詳細介紹這方面。
  • 用來編輯 AI... 但我建議你用 SDK 來做,因為這可能節省很多時間!

Python 不能

  • 完全按照你想要的方式使用它。雖然它能做的事情很多,但也有一些事情是做不到的。這真的很煩人,但這是你必須學會接受的。它能做的事情很多,所以不要抱怨。希望隨著 SDK 的釋出,更有經驗的編碼人員將增強 python 修改遊戲運作方式的能力。

還在看嗎?很好!

現在,我建議你先瀏覽一下 GBM 的 python 教程,它簡要概述瞭如何在 python 中程式設計。此外,如果你還沒有,請檢視 python 網站,上面有大量關於 python 的資訊(不出所料)。最後,我建議你看看 由 Jon (Trip) Shafer,Firaxis 編寫的 這個 python 教程,你可能會發現他的教程比我的更好,畢竟,他是在一開始就編寫了其中大部分程式碼的人。

正如你所知,python 中有幾種不同的“東西”型別:整數、浮點數、字串和布林值。你可能也知道“指標”。我剛接觸 python 的時候並不知道,所以我會告訴你一些關於指標的資訊。

指標本質上是一個遊戲實體。它可以是單位、地塊(方塊)或玩家。在預設的 python 程式碼以及許多模組製作者的程式碼中,變數字首為“p”表示它是一個指標。因此,pUnit 將定義遊戲中的單個單位,pPlayer 定義單個玩家。

同樣,如果一個變數以“i”為字首,它很可能是一個整數(int)變數,而“b”則表示布林值(bool)變數。例如,iPlayer 可能是一個玩家的個人 ID 號碼,而不是該玩家的指標。

有時會讓人感到困惑,因為大多數指標都與之緊密相關的整數。這種命名方式有助於避免在像 python 這樣的語言中混淆,在 python 中不需要宣告變數,但它只是一個指導方針,僅僅因為一個變數的命名方式看起來像是某種型別的變數,它並不一定會是這種型別。

我還想在這裡說明一下“型別”。在大多數 xml 條目中,你需要在型別欄位中輸入一些內容。這個欄位是與 python 之間非常重要的聯絡,儘管必須將其轉換為整數才能正常工作。有一個函式你將發現自己會一遍又一遍地使用,所以我認為應該把它寫在這裡。這個函式將找到與型別相對應的整數 - 因為 API 中的大多數函式(很快就會介紹)都需要型別的整數形式,所以它非常有用。例如

gc.getInfoTypeForString("TECH_MYSTICISM")

將返回與科技“神秘主義”相對應的整數,因為它是相關 xml 檔案中的第一個條目,預設情況下為零。

gc.getInfoTypeForString("RELIGION_TAOISM")

將返回整數 6,預設情況下,因為它是列出的第 7 個宗教。

如果你現在還沒有理解最後一點,不要擔心 - 希望在閱讀下一節時會變得清晰。

文明 4 API 是一個列表,其中包含所有特定於文明 4 並與實際遊戲互動的函式。API 有兩個版本。第一個版本由 Locutus 建立,可以在 這裡找到,第二個版本由 GBM 建立,可以在 這裡找到。Locutus 的 API 已經更新到 1.60 補丁,所以我建議你使用那個版本。

現在,在過去的一個月裡,我在論壇上回答有關 python 的問題時,發現許多人難以閱讀 API。理解函式的作用可能非常困難,但 API 確實提供了一些提示,如果你願意閱讀的話。

我將使用 Locutus 的 API 作為示例,因為我覺得它的介面更好,所以 開啟它

首先,我們有類。它們列出了所有可以對特定實體或指標執行的函式。例如,CyUnit 列出了所有可以作用於單位實體(遊戲中的單位)的函式。類在左上角的框架中列出。你會注意到,一些類旁邊有一個“+”號。這些類用於直接從物件的 xml 檔案中獲取特定資訊。

現在我們有型別。它們是從 xml 檔案中提取的不同型別。顯示的整數是預設值,雖然你可以使用它們,並且你的程式碼很可能能夠正常工作,但我建議你不要這樣做,因為如果未來補丁改變了 xml 的順序,你的程式碼就會失效。相反,我建議使用 gc.getInfoTypeForString(""),如上面所示,因為這更相容補丁。

最後我們有函式。每個類都有自己的一組函式,這些函式只能對該類使用。如果你試圖從一個類中獲取函式,並將其用於另一個類,它將無法工作。函式是 API 的主要部分,是你想要了解的東西。

現在,有很多不同的函式。大多數函式在遊戲中不“做”任何事情,而是從你的指標中獲取一個值,你可以在等式中使用這個值。**如果你看一下函式的左側,它會顯示這個函式返回什麼。** 例如,如果你使用了

BOOL CyPlayer.canChangeReligion()

它**不會**設定玩家能夠改變宗教,而是會返回一個 BOOL,一個真或假值,取決於玩家是否能夠改變宗教。明白我的意思了嗎?它不“做”任何事情?

函式可以返回以下型別的值:

  • Bool - 真或假,1 或 0
  • Int - 整數值(可以為負數)(例如 256)
  • Float - 浮點數(例如 1.3423)
  • String - 字串("The cat sat on the mat")
  • Turple - 列表([1, 4, 2, 8, 2])
  • Void - 見下文

"做"事情的函式返回一個 "VOID" - 意思是它們是設定而不是獲取資料。這些函式是最重要的,因為沒有它們你就不能做太多事情,不幸的是它們不足以做你想做的一切。抱歉,但這就是現實。

所以,你知道一個函式返回什麼,但這並不是你需要知道的全部。你還需要知道要給它什麼資訊才能返回這些資料。有些函式不需要任何輸入。

CyUnit.canMove()

例如,除了要檢查哪個單位可以移動之外(單位指標應該在 CyUnit 的位置),它不需要知道任何其他資訊。然而,大多數函式並不像這樣簡單,它們需要一個輸入來產生一個輸出。

以這個函式為例:

CyUnit.setHasPromotion(PromotionType eIndex, BOOL bNewValue)

這個函式接受兩個引數(輸入):你想給予單位的晉升型別,以及一個布林值,表示是否要獲取或刪除晉升。舉個例子:

pUnit.setHasPromotion(gc.getInfoTypeForString("PROMOTION_COMBAT1"), 1)

會給予指標 pUnit 晉升 combat 1。請注意,在這種情況下給出晉升的整數值很重要,因為當函式請求型別時,它實際上想要一個整數值……別問!幸運的是,如果你在函式中使用了錯誤的引數,Python 在嘗試執行該函式時會告訴你。關於這部分內容,將在除錯部分進行更詳細的介紹!

CyGlobalContext

[edit | edit source]

在檔案中通常縮寫為 gc,它可能是最有用的類,因為它包含所有與文明 4 相關的通用函式。GlobalContext 的獨特之處在於它不需要用指標呼叫,而是一個獨立的類,用於獲取資訊。

它有許多有用的用途:

  • 正如我之前所說,你可以用它從型別中獲取整數值。例如:
gc.getInfoTypeForString("TECH_MYSTICISM")

會獲取 Mysticism 的整數值。

  • 你可以用它從整數 ID 獲取指標。例如:[code]gc.getPlayer(0)[/code] 會返回 ID 為零的玩家的 pPlayer 指標。
  • 你可以用它從 xml 檔案中獲取資訊。例如:
gc.getPromotionInfo("PROMOTION_COMBAT1")

會返回 combat 1 晉升的指標,然後可以使用 CvPromotionInfo 獲取有關該特定晉升的資訊。


  • 一個常見的用法是獲取活動玩家指標,它會返回正在進行回合的玩家的指標。
gc.getActivePlayer()
  • 最後一個主要用途是獲取特定事物的例項數量。例如:
gc.getNumPromotionInfos()

會返回遊戲中可用的晉升數量。如果你想遍歷所有晉升並檢查單位是否擁有這些晉升,並在此基礎上啟用一個新函式,這將非常有用。

雖然 CyGlobalContext 有其他用途,但它們太多,無法在這裡一一列舉。我已經嘗試列出我認為最重要的那些。

事件

[edit | edit source]

事件是由於某些觸發器而發生的事情。觸發器數量是固定的,這在一定程度上限制了模組開發者,但這些觸發器可以滿足大多數指令碼的需求。如果你開啟 CvEventManager.py,事件觸發器從第 193 行執行到第 747 行(補丁 1.52)。有近 60 個不同的觸發器可供使用,從每幀執行一次的 onUpdate(可能每秒執行大約 30 次)到只執行一次的 onGameStart,在遊戲開始時執行。

大多數觸發器都與引數相關聯。這些引數儲存在 argsList 中。在大多數事件標題下,這些引數都有定義。它們通常很容易理解。

可能最簡單的新增事件觸發器的方法就是將事件新增到事件管理器中。對於小型模組來說,這可以奏效,但會引發一些相容性問題。更好的方法是建立自己的 CvCustomEventManager 檔案。我推薦 Dr Elmer Jiggle 的事件管理器(此處)。一開始可能有點難理解,但如果你能成功使用它,就非常值得。關於它的實際應用示例,你可以檢視 TheLopez 的任何一個 ModComp(檢視此論壇)。

需要記住的事項:

  • onEndPlayerTurn 觸發器不會在玩家回合結束時發生,而是在玩家回合開始結束時發生,在所有城市建造完畢等操作之後。如果你想在玩家回合結束時執行一個事件,你可能需要編寫一些程式碼,以便在下一個玩家回合開始時執行它。onBeginGameTurn 和 onEndGameTurn 似乎也是如此,儘管它們都在遊戲回合結束時發生,而不是像玩家回合那樣在開始時發生。感謝 Kael 指出這一點。

示例:

  • 例如,如果你想要在每個遊戲回合開始時(換句話說,在第一個玩家回合開始時)在每個玩家的螢幕上顯示一條訊息,你可以用以下程式碼替換事件管理器中的程式碼:
def onBeginGameTurn(self, argsList):
	'Called at the beginning of the end of each turn'
	iGameTurn = argsList[0]
	CvTopCivs.CvTopCivs().turnChecker(iGameTurn)

使用以下程式碼:

def onBeginGameTurn(self, argsList):
	'Called at the beginning of the end of each turn'
	iGameTurn = argsList[0]
	CyInterface.addImmediateMessage("You have just started a new turn", "") # Adds the message "You have just started a new turn" with no sound attached.
 	CvTopCivs.CvTopCivs().turnChecker(iGameTurn)

注意:在製作正式模組時,你不應該像這樣簡單地將內容新增到事件管理器中,因為它會導致相容性問題。請檢視上面連結的自定義事件管理器。

介面

[edit | edit source]

正如我在簡介中提到的,幾乎所有的 GUI 都是動態建立的,無論是在你進入相關螢幕時,還是在遊戲開始時。幾乎所有內容都可以修改。修改介面的檔案位於 .../Assets/Python/Screens 中。


需要記住的事項:

  • 在介面中放置專案時,你輸入的 x 和 y 座標將對應於專案的左上角。
  • 請記住,解析度可能會發生變化。通常最好根據解析度來改變介面的顯示方式。要查詢 X 和 Y 解析度,你可以使用以下程式碼:
CyGInterfaceScreen.getXResolution() or CyGInterfaceScreen.getYResolution()
  • 如果專案需要一個名稱,該名稱必須是唯一的,否則會發生奇怪的事情。
  • techchooser 指令碼的編寫方式可能使它難以修改。它不是在每次載入螢幕時生成的,而是在遊戲開始時生成的,然後只有顏色變化在遊戲中進行。為了阻止這種情況在遊戲中發生,並確保 techchooser 在遊戲執行時顯示你對它的修改,我建議你註釋掉第 72 行(screen.setPersistent(True))。這將使 techchooser 在開啟時不斷重新生成,儘管這可能會導致你的電腦速度略微下降,但它應該執行得更好。但有時,由於 techchooser 模組的生成方式,你可能需要重啟遊戲才能看到任何更改。

除錯

[edit | edit source]

我希望有人能檢查一下這部分內容,因為我不確定它的準確性,而且我無法測試它,因為我將在未來幾個月內離開我的電腦。

當指令碼無法正常工作時,每個人都必須進行除錯,因為很容易犯一個小錯誤,導致整個程式碼毫無意義。除錯的第一步是開啟遊戲中的除錯選項。為此,請開啟 Civilization4.ini(請務必備份),並將以下條目更改為以下值:

HidePythonExceptions = 0
ShowPythonDebugMsgs = 1
LoggingEnabled = 1
MessageLog = 1

這應該啟用遊戲中的 Python 彈出視窗,並將所有錯誤和訊息列印到你的 My Documents/My Games/Civilization 4/Logs 目錄中。除錯時要查詢的三個重要日誌檔案是 PythonDbg、PythonErr 和 PythonErr2。

常見錯誤訊息及其原因

[edit | edit source]

在日誌中,錯誤通常會有一個回溯資訊。這個回溯資訊列出了所有受錯誤影響的檔案以及錯誤發生的程式碼行。通常你最關心的是最後一行,因為它是錯誤發生的地方。有時你需要回溯幾步,因為錯誤可能不在最後一行,但通常情況下,它就在那裡!

語法錯誤

[編輯 | 編輯原始碼]

這些主要是打字錯誤 - 漏掉的 ":", 不正確的空格或錯誤的括號。遊戲通常會在你載入它時發現這些錯誤。錯誤日誌應該會用一個小小的 ^ 符號顯示語法錯誤發生的確切位置。

引數錯誤

[編輯 | 編輯原始碼]

如果你試圖在一個整數型別值上使用一個為指標設計的函式,你將會得到一個型別錯誤。轉到指定的程式碼行並檢查你正在使用的函式是否有效。通常它會說明它期望的是什麼型別的函式,以及你提供了什麼。注意:有些類在函式中需要 (),有些不需要。如果你遇到型別錯誤,這可能是原因。

型別錯誤

[編輯 | 編輯原始碼]

這些錯誤發生在當你傳遞給函式的引數過多或過少時。你必須確保你傳遞給函式的引數數量與它需要的引數數量匹配,否則在嘗試執行函式時它會丟擲錯誤。

名稱錯誤

[編輯 | 編輯原始碼]

這意味著你試圖使用遊戲不知道的東西。例如,如果你在沒有先說明 b 是什麼的情況下鍵入 a = b,那麼計算機就無法將一個值設定為 a。請記住 a=b 與 b=a 並不相同。另外,確保你沒有在需要 " =" 的地方使用 " == ",反之亦然。

有時你會遇到 "Argument referenced before assignment" 錯誤。我不知道這是否與名稱錯誤相關聯,或者是否需要它自己的類別,但基本上它就像一個名稱錯誤,只是你稍後在程式碼中定義了 "a"。

"列表索引超出範圍"

[編輯 | 編輯原始碼]

當你在一個不存在的列表、元組或字典中引用索引時,就會發生這種情況。這個問題可能很難解決,你可能需要使用本節下一部分中描述的方法來檢視究竟發生了什麼錯誤。

這就是我現在能想到的所有錯誤訊息。我確信遺漏了一兩條 - 如果你發現了任何,請隨時提出,我會在這裡新增它們。正如我在開頭所說,我對這些訊息的準確性並不完全確定,但它們應該沒問題。

程式碼行為異常

[編輯 | 編輯原始碼]

所以你已經寫好了你的程式碼。它全部執行,沒有錯誤...... 但它並沒有在遊戲中產生你期望的結果。這可能有幾個原因。

首先,如果你認為一段程式碼應該對遊戲產生影響,請透過檢查 API 來確保它確實有影響,看看它是否是 VOID 函式。在剛開始學習時,用其他函式,以為它們會做一些事情,但實際上它們只是檢索資訊,這是一個很常見的錯誤。

其次,如果一切都看起來沒問題,你需要開始在程式碼中新增除錯資訊。這部分內容帶你回到 Python 程式設計的最初階段,那時你告訴你的計算機列印 "hello world"。你需要做的是讓計算機列印一條訊息,告訴你在程式碼中的某個特定點某個特定值是什麼,然後將它與你認為它應該是什麼進行比較。例如,以下程式碼將在 a=b 時在除錯日誌中列印 "a=b",否則列印 a 和 b 的值。

if a == b:
	print "a=b"
else:
	print a
	print b

這在除錯日誌中可能很難理解(通常你會有很多很多數字),所以你可以新增更高階的訊息。

if a == b:
	print "a = b, a and b are %d, and %d"%(a,b))
else:
	print("a = %d"%(a))
	print("b = %d"%(b))

其中 %d 將引用 " " 外面 % 後面的數字。如果你想在 " " 中放置一個字串,你必須使用 %s。這可以讓你詳細瞭解你的程式碼內部究竟發生了什麼,希望從這些資訊中你可以看到程式碼出了什麼問題。

此頁面包含其他關於 Civ 4 Python 的內容,這些內容在其他文章中沒有涉及,並且在初次接觸時並不明顯。其中一些內容是粉絲製作的 - 建立快捷方式來完成一些非常複雜的事情。

PyHelpers.py

[編輯 | 編輯原始碼]

大多數 Python 檔案都會匯入此檔案。基本上它為一些更復雜的函式添加了一些快捷方式。如果你在 API 中找不到你想要的,那麼這個檔案可能會幫助你 - 儘管它只是建立了一些快捷方式,但仍然遵循 API。

CvGameUtils.py

[編輯 | 編輯原始碼]

此檔案用於決定某些事情,例如什麼可以建造/什麼不能建造。它可以用於中斷一些基本的遊戲功能。通常每個函式都返回 "False",但如果你想在某些條件下讓它返回 "True",那麼例如,這個單位就可以從可用單位列表中移除。你可以用它來讓擁有奴隸制公民權的文明可以建造奴隸單位,例如。我建議你自己嘗試一下,看看你擁有多少權力。

指令碼資料

[編輯 | 編輯原始碼]

如果你想為遊戲世界的某些部分新增額外的資訊,你需要指令碼資料。例如,如果你想要一個單位每次戰鬥時消耗 "彈藥",然後必須返回到擁有兵工廠的城市進行補給,或者你想要讓地塊在沒有道路的情況下有太多單位經過時變得泥濘,你需要指令碼資料。

如果你想玩弄指令碼資料,我建議你使用 Stone-D 出色的 SD-Toolkit。這個工具包允許你將資料片段附加到遊戲的各個部分。每個部分都必須用一個唯一的 mod 名(用於相容性)進行標識。

注意:Teg_Navanis 在 CFC 上的這篇文章中釋出了該工具包的改進版本。這個版本比預設版本更快,並修復了預設版本的一些小問題。

動作按鈕

[編輯 | 編輯原始碼]

talchas 釋出了一個 動作按鈕實用程式模組,它是一個模板,用於向 GUI 新增按鈕,以便在按下時執行自定義功能。AI 將不會知道它。

方格選擇

[編輯 | 編輯原始碼]

再次由 talchas 這個mod 是一個模板。透過適當的修改,這可以用來使炮兵完全像文明3的炮兵一樣工作,沒有補丁。再次,AI的缺陷出現了。

.ini 檔案修改

[編輯 | 編輯原始碼]

雖然我自己還沒有嘗試過,但 Dr Elmer Jiggle 弄了一個mod,它允許你在mod的.ini檔案中新增變數。 在這裡獲取

華夏公益教科書