跳转到内容

文明/文明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文件中添加变量。 在这里获取

华夏公益教科书