跳转到内容

文明/文明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代码中,以及在许多modder的代码中,一个变量如果以“p”开头,则表明它是一个指针。因此,pUnit将定义游戏中单个单位,pPlayer将定义单个玩家。

同样地,如果一个变量以“i”开头,它很可能是一个整数(int)变量,而“b”则是一个布尔值(bool)变量。例如,iPlayer很可能是一个玩家的个人ID号,而不是玩家的指针。

这有时会让人困惑,因为大多数指针都有与之密切相关的整数。这种命名方法有助于避免在像python这样的语言中出现混淆,因为变量不需要声明,但是这只是一个指导原则,仅仅因为一个变量的命名方式就像它是某种类型的变量,它并不一定就是那种类型。

我还想在这里说明一下“类型”。在大多数xml条目中,你需要在“类型”字段中输入一些内容。这个字段是与python的非常重要的连接,但必须转换为整数才能正常工作。有一个函数你会发现自己一遍又一遍地使用它,所以我认为应该在这里提到它。这个函数会找到对应于类型的整数 - 因为API(即将到来)中的大多数函数都需要类型的整数形式,所以它非常有用。例如

gc.getInfoTypeForString("TECH_MYSTICISM")

将返回对应于“神秘主义”科技的整数,因为它是相关xml文件中的第一个条目,因此默认值为0。

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 - 字符串(“猫坐在垫子上”)
  • 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 战斗 1 晋升。请注意,在这种情况下,使用晋升的整数值很重要,因为当函数要求类型时,它实际上想要一个整数……别问为什么!幸运的是,如果你在函数中输入了错误的参数,Python 会在尝试运行函数时告诉你。更多内容将在调试部分介绍!

CyGlobalContext

[edit | edit source]

在文件中通常缩写为 gc,这可能是最有用的类,因为它包含了所有与文明 4 相关的通用函数。GlobalContext 的独特之处在于它不需要用指针调用,而是一个独立的类,用于获取信息。

它有许多有用的用途

  • 正如我之前所说,你可以用它从类型获取整数。例如,
gc.getInfoTypeForString("TECH_MYSTICISM")

将获取神秘主义的整数值。

  • 你可以用它从整数 ID 获取指针。例如,
gc.getPlayer(0)

将返回 ID 为零的玩家的 pPlayer 指针。

  • 你可以用它从 xml 文件获取信息。例如,
gc.getPromotionInfo("PROMOTION_COMBAT1")

将返回战斗 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 的任何 ModComps(可以在 这个论坛中找到)。

需要注意的事项

  • 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 都是动态创建的,要么是在你进入相关屏幕时创建,要么是在游戏开始时创建。几乎所有 GUI 都可以进行模组化。模组化界面的文件位于 .../Assets/Python/Screens 中。


需要注意的事项

  • 在界面中放置项目时,你输入的 x 和 y 坐标将对应于项目的左上角。
  • 请记住,分辨率可能会发生变化。通常情况下,根据分辨率更改界面的显示方式可能是个好主意。要查找 X 和 Y 分辨率,可以使用以下代码:
CyGInterfaceScreen.getXResolution() or CyGInterfaceScreen.getYResolution()
  • 如果一个项目需要一个名称,该名称必须是该项目独有的,否则就会发生奇怪的事情。
  • techchooser 脚本的编写方式使得对其进行模组化非常困难。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]

通常在日志中,错误会附带一个回溯信息。这个回溯信息列出了受错误影响的所有文件,以及错误发生的行号。最后一行通常是您最感兴趣的,因为这是错误发生的地方。有时您需要回溯几步,因为错误可能不在最后一行,但通常都在最后一行!

语法错误

[edit | edit source]

这些主要是输入错误,例如漏掉了 ":",空格错误,或者括号不匹配。游戏通常会在您加载游戏时发现这些错误。错误日志会用一个小的 ^ 符号来指示语法错误的位置。

参数错误

[edit | edit source]

如果您尝试在整型值上使用为指针设计的函数,您将获得一个类型错误。转到指定行,检查您使用的函数是否有效。通常它会说明它期望的函数类型以及您提供的函数类型。注意:有些类在函数中需要 (),有些不需要。如果您遇到类型错误,这可能是原因。

类型错误

[edit | edit source]

当您传递给函数的参数过多或过少时,就会发生这些错误。您需要确保传递给函数的参数数量与它需要的参数数量一致,否则函数在运行时会抛出错误。

名称错误

[edit | edit source]

这意味着您尝试使用游戏无法识别的内容。例如,如果您在没有先声明 b 的情况下输入 a = b,计算机就无法将值赋给 a。请记住 a=b 和 b=a 的含义不同。此外,请确保您没有将 "==" 用在需要 "=" 的地方,反之亦然。

有时您会遇到 "Argument referenced before assignment" 错误。我不知道这是否属于名称错误,或者是否需要单独的分类,但基本上它类似于名称错误,只是您在代码中稍后定义了 "a"。

"List index out of range"

[edit | edit source]

当您尝试引用列表、元组或字典中不存在的索引时,就会发生这种情况。这个错误可能很难解决,您可能需要使用本节下一部分中描述的方法来查看具体问题所在。

目前,我已经能想到的所有错误消息就这些了。我确定我遗漏了一两个错误,如果您发现任何错误,请随时提出,我会将其添加到这里。正如我在开头所说,我不确定这些信息的准确性,但应该没问题。

代码行为异常

[edit | edit source]

您编写了代码,它没有错误,可以正常运行... 但它在游戏中没有产生您期望的结果。这可能有多种原因。

首先,如果您认为一段代码应该对游戏产生影响,请通过查看 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。这可以为您提供关于代码内部运行情况的详细信息,并且希望通过这些信息,您能够看到代码中存在的问题。

其他

[edit | edit source]

此页面包含有关 Civ 4 Python 的其他内容,这些内容在其他帖子中没有介绍,而且乍一看并不明显。其中一些内容是粉丝自制的,创建了一些实现非常复杂功能的快捷方式。

PyHelpers.py

[edit | edit source]

大多数 Python 文件都会导入此文件。它基本上为一些更复杂的函数添加了一些快捷方式。如果您在 API 中找不到您想要的内容,那么此文件可能会帮到您,尽管它只创建了快捷方式,仍然遵循 API。

CvGameUtils.py

[edit | edit source]

此文件用于决定某些事情,例如可以/不能建造什么。它可以用来中断一些基本的游戏功能。通常每个函数都会返回 "False",但如果您在某些特定条件下让它返回 "True",例如,就可以从可用单位列表中移除该单位。例如,您可以使用它让拥有奴隶制公民权的文明能够建造奴隶单位。我建议您亲自查看一下,看看您在其中拥有多少权限。

脚本数据

[edit | edit source]

如果您想添加有关游戏世界某些部分的额外信息,您需要脚本数据。例如,如果您想让单位在每次战斗时消耗 "弹药",然后必须返回到有兵工厂的城市进行补给,或者您想让地块在没有道路的情况下被过多单位通行后变得泥泞,您需要脚本数据。

如果您想尝试使用脚本数据,我建议您使用 Stone-D 出色的 SD-Toolkit。此工具包允许您将数据片段附加到游戏的各个部分。每个部分都必须使用唯一的 mod 名称标识(为了兼容性)。

注意:Teg_Navanis 在 CFC 上的这篇帖子 中发布了该工具包的改进版本。这个版本比默认版本更快,并且修复了默认版本中的一些小问题。

操作按钮

[edit | edit source]

talchas 发布了一个名为 Action Button's Utility Mod 的模组,它是一个模板,允许你添加按钮到游戏界面,这些按钮在被按下时会执行自定义函数。AI 不会知道这个模组的存在。

方块选择

[编辑 | 编辑源代码]

这个模组同样由 talchas 创建,它是一个模板,你可以通过适当的修改来让炮兵的运作方式与文明3中的炮兵一致,无需任何变通方案。但是,AI 的缺陷仍然存在。

.ini 文件修改

[编辑 | 编辑源代码]

虽然我还没有亲身尝试,但是 Dr Elmer Jiggle 创建了一个模组,允许你在模组的 .ini 文件中添加变量。 点击这里获取

华夏公益教科书