我们继续讨论插件 Mutator 类,先从源代码的角度来看
该类文件所在路径为:
KillingFloor \ Engine \ Classes
这里没有列出 KF1 的具体安装目录,只需要在游戏目录找到 Engine 文件夹。
Mutator.uc 文件有 300多行代码(包括空行),里面以函数居多 只有少量属性。
这里将列出三个比较特殊的函数,很容易对游戏进行修改:
-
ModifyPlayer:修改玩家,在玩家进入游戏时 对其进行修改。
-
Mutate:插件消息,接收玩家发送命令消息 可再做处理。
-
CheckReplacement:检查替换,一般可处理任何进入游戏的对象,如 怪物。
这里只是列出简单的几个函数,实际上 Mutator 类还有很多修改游戏的方式。
插件类有一个 ModifyPlayer 函数,当你继承了插件类之后 就可以重写该函数。
如果不懂什么叫函数重写,请从编程基础中的“类的继承”主题开始了解。
以下代码将在玩家进入游戏时给予一把武器
并且是在每一个玩家出生时执行该函数:
function ModifyPlayer(Pawn Other)
{
Other.GiveWeapon("KFMOD.SCARMK17AssaultRifle");
Super.ModifyPlayer(Other);
}
当该函数调用时,玩家棋子 将作为参数传进来,然后就可以对其修改。
这里调用了 玩家棋子的给予武器函数,拿到了一把 SCAR,
然后 Super 访问超类函数,因为父类的同名函数 有些代码还要执行,从而实现多个插件同时运行的兼容性。
提示:由于C++有多继承的功能,所以没有 Super关键字;而虚幻脚本是单向继承。
Super.ModifyPlayer() 首先会尝试访问父类的 ModifyPlayer() 函数,如果该父类没有实现该函数 就再尝试访问 父类的父类 ModifyPlayer() 函数,
如果没有就一直向上找,找到为止。
插件类有一个函数 Mutate,单词还是与 Mutator 有一点区别。
作用是接收用户命令参数,然后判断修改;实际上看代码更直观一点
function Mutate(string MutateString, PlayerController Sender)
{
if (Caps(MutateString) == "HYPER")
Sender.Pawn.Health = 999;
Super.Mutate(MutateString, Sender);
}
在玩家用控制台输入 mutate <字符串参数> 回车时 会调用该函数,
并将 <字符串参数> 作为第一个参数传入,第二参数即执行命令的玩家(发送命令的玩家控制器)
函数体中,将“参数字符串”转成大写 再进行比较,比较成功就给“玩家的棋子”设置血量为 999
最后再访问 超类同名函数,将参数按原顺序传递。
小提示:多插件是以链式引用的关系存储,这是《数据结构》中的一些概念。
游戏启动了一连串的插件时,程序会设置一个插件对象中的某个属性 指向了下一个插件对象,即链式引用。
检查替换 理论上是在任何 Actor 物件生成时都调用,但为了留出节省性能的空间
只有 Actor 属性 bGameRelevant=true 时才会检查该 Actor物件,属性意思为“游戏相关”;
与游戏相关性不大的物件 就没必要检查了,省点性能开支。
以下代码在怪物进入游戏时 修改其血量:
function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
if (KFMonster(Other) != None)
{
KFMonster(Other).Health = 1;
}
return Super.CheckReplacement(Other, bSuperRelevant);
}
Actor 物件类几乎是场景中任何对象的顶级基类,所以替换怪物时 要时刻判断着。
第一个参数 即当前对象,可以用类型转换 做判断,转换“怪物类”成功时执行相应代码,
如果转换失败就变成了 None 空对象。
第二个参数有点奇怪,至于做什么事情 要找到具体被调用处的上下文;由于代码中没有用到第二参数
并且分析代码要找一段时间,这里暂不考虑第二个参数的作用。
最后还要 return 一下,因为该函数有返回值,实际上永远返回 true 。
将代码写在自己的插件中,可以看到效果:
1、默认获得 Scar MK17
2、波浪键 ~ 控制台命令输入:HYPER,能获得 999 血量
3、所有怪物都只有1点血,即一碰就倒