Unity引擎类游戏MOD制作通用教程
软件系统架构设计师,中国象棋强大师水平,日麻上层巅峰水平。
前言:
此文章是俺之前在3DMGAME论坛发的一个教程帖子,转到知乎上一是留个存档,二是分享给对Unity引擎类游戏MOD制作有兴趣的水友们!
正题:
本教程计划分为五个部分分别讲述,从第二楼起,每楼阐述一个部分。
第一部分:MOD制作前必备的工具软件;
第二部分:DLL文件的反编译与重编译;
第三部分:IL文件的修改;
第四部分:IL汇编语言相关;
第五部分:对MOD制作的一些展望。
第一部分:MOD制作前必备的工具软件
1、Visual Studio 2017(C++、C#语言类开发IDE);
2、dotPeek(个人认为该软件是最好的.NET框架反编译工具,网址:
导出成功后即可到导出目录双击*.sln文件打开VS2017查看源代码了; 2、在任意硬盘分区新建3个目录,例如:E:\Decompile,E:\Decompile\DLL,E:\Decompile\DLL-ildasm,将文件Assembly-CSharp.dll复制到E:\Decompile\DLL目录; 3、打开“VS 2017的开发人员命令提示符 ”,输入命令: 此时在E:\Decompile\DLL-ildasm目录会生成Assembly-CSharp.res和Assembly-CSharp.il两个文件,
此时在E:\Decompile\DLL-ildasm目录会生成文件Assembly-CSharp.dll,该文件就是重编译文件Assembly-CSharp.il后得到的新的类库文件; 5、将步骤4得到的新类库文件Assembly-CSharp.dll复制到游戏目录下的XXXX_Data\Managed目录即完成了MOD制作,此时可以运行游戏查看效果了。 这部分内容是MOD制作的核心,对观看的水友有一定的要求,需要能读懂C#语言和IL汇编语言;考虑到不懂这两种语言的水友,我尽可能简明的举例说明下怎么修改。 下面我以角色负重值修改(100倍负重)为例具体说明下操作过程: 1、双击*.sln文件打开VS2017,在左侧的解决方案资源管理器窗口定位到C#源文件EncumbranceHelper.cs并双击图标,此时在右侧源代码编辑窗口搜索GetHeavy定位到获得角色负重值上限的方法,记住最后一行return语句表示的代码含义,如下图: 2、
3、
下面为方便水友们自己修改,提供我自制的《开拓者:拥王者》40级上限修改补丁的所有修改的相关IL代码片段如下: IL汇编语言是什么?简单来说,IL是微软.NET平台上衍生出的一门中间语言,.NET平台上的各种高级语言(如C#,VB,F#)的编译器会将各自的代码转化为IL;详细介绍推荐给有C#编程基础的水友看下这两篇文章:IL汇编语言介绍。 本人为了修改IL文件时能方便的查询IL指令,自制了一个IL语言帮助命令,可查询和配置管理IL指令和IL语句的帮助信息;该命令使用说明: 1、解压缩game.7z(有需要的水友请到我这个帖子下载:使其能根据指定的IL替换代码片段自动替换文件Assembly-CSharp.il。cd /d "E:\Decompile\DLL"
ildasm Assembly-CSharp.dll /output:..\DLL-ildasm\Assembly-CSharp.il
第三部分:IL文件的修改
*开头的行表示需要替换,+开头的行表示需要新增,右边是查询需要被修改的IL代码片段的关键字
角色等级上限及经验值修改
* } // end of method LevelUpController::CanLevelUp
IL_0034: ldloc.0
IL_0035: ldc.i4.s 40
IL_0036: blt IL_0039
IL_0037: ldc.i4.0
IL_0038: ret
IL_0039: ldloc.0
IL_003a: ldc.i4.s 20
IL_003b: bge IL_004e
IL_003c: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_003d: callvirt instance class Kingmaker.Blueprints.Root.BlueprintRoot Kingmaker.Game::get_BlueprintRoot()
IL_003e: ldfld class Kingmaker.Blueprints.Root.ProgressionRoot Kingmaker.Blueprints.Root.BlueprintRoot::Progression
IL_003f: ldfld class Kingmaker.Blueprints.Classes.BlueprintStatProgression Kingmaker.Blueprints.Root.ProgressionRoot::XPTable
IL_0040: ldloc.0
IL_0041: ldc.i4.1
IL_0042: add
IL_0043: callvirt instance int32 Kingmaker.Blueprints.Classes.BlueprintStatProgression::GetBonus(int32)
IL_0044: ldc.i4.2
IL_0045: div
IL_0046: stloc.1
IL_0047: ldloc.1
IL_0048: ldarg.0
IL_0049: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_004a: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_Experience()
IL_004b: ble IL_006e
IL_004c: ldc.i4.0
IL_004d: ret
IL_004e: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_004f: callvirt instance class Kingmaker.Blueprints.Root.BlueprintRoot Kingmaker.Game::get_BlueprintRoot()
IL_0050: ldfld class Kingmaker.Blueprints.Root.ProgressionRoot Kingmaker.Blueprints.Root.BlueprintRoot::Progression
IL_0051: ldfld class Kingmaker.Blueprints.Classes.BlueprintStatProgression Kingmaker.Blueprints.Root.ProgressionRoot::XPTable
IL_0052: ldc.i4.s 20
IL_0053: callvirt instance int32 Kingmaker.Blueprints.Classes.BlueprintStatProgression::GetBonus(int32)
IL_0054: ldc.i4.2
IL_0055: div
IL_0056: ldloc.0
IL_0057: ldc.i4.s 19
IL_0058: sub
IL_0059: ldc.i4 90000
IL_005a: mul
IL_005b: add
IL_005c: stloc.1
IL_005d: ldloc.1
IL_005e: ldarg.0
IL_005f: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_0060: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_Experience()
IL_0061: ble IL_006e
IL_006c: ldc.i4.0
* } // end of method UnitProgressionData::AddClassLevel
IL_0061: ldloc.0
IL_0062: ldloc.0
IL_0063: ldlen
IL_0064: conv.i4
IL_0065: ldc.i4.1
IL_0066: sub
IL_0067: ldarg.0
IL_0068: call instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_0069: call int32 [mscorlib]System.Math::Min(int32,
int32)
IL_006a: ldelem.i4
IL_006b: ldc.i4.2
IL_006c: div
IL_006d: stloc.1
IL_006e: ldarg.0
IL_006f: call instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_0070: ldc.i4.s 20
IL_0071: ble IL_007b
IL_0072: ldloc.1
IL_0073: ldarg.0
IL_0074: call instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_0075: ldc.i4.s 20
IL_0076: sub
IL_0077: ldc.i4 90000
IL_0078: mul
IL_0079: add
IL_007a: stloc.1
IL_007b: ldarg.0
IL_007c: ldarg.0
IL_007d: call instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_Experience()
IL_007e: ldloc.1
IL_007f: call int32 [mscorlib]System.Math::Max(int32,
int32)
IL_0081: call instance void Kingmaker.UnitLogic.UnitProgressionData::set_Experience(int32)
* } // end of method CharSLevel::FillData
IL_00bd: br IL_0100
IL_00c2: ldarg.0
IL_00c3: ldfld class ['Assembly-CSharp-firstpass']TMPro.TextMeshProUGUI Kingmaker.UI.ServiceWindow.CharacterScreen.CharSLevel::Level
IL_00c8: ldloc.0
IL_00c9: ldfld class Kingmaker.Localization.LocalizedString Kingmaker.Blueprints.Root.Strings.UITextCharSheet::LEVEL
IL_00ce: call string Kingmaker.Localization.LocalizedString::op_Implicit(class Kingmaker.Localization.LocalizedString)
IL_00d3: ldstr " "
IL_00d8: ldarg.1
IL_00d9: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_00de: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_00e3: box [mscorlib]System.Int32
IL_00e8: call string [mscorlib]System.String::Concat(object,
object,
object)
IL_00ed: callvirt instance void ['Assembly-CSharp-firstpass']TMPro.TMP_Text::set_text(string)
IL_00f2: ldarg.0
IL_00f3: ldfld class [UnityEngine.UI]UnityEngine.UI.Image Kingmaker.UI.ServiceWindow.CharacterScreen.CharSLevel::NegativeLevel
IL_00f8: callvirt instance class [UnityEngine.CoreModule]UnityEngine.GameObject [UnityEngine.CoreModule]UnityEngine.Component::get_gameObject()
IL_00fd: ldc.i4.0
IL_00fe: callvirt instance void [UnityEngine.CoreModule]UnityEngine.GameObject::SetActive(bool)
IL_0100: ldarg.1
IL_0101: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_0102: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_0103: ldc.i4.s 20
IL_0104: bge IL_0141
IL_0105: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_0106: callvirt instance class Kingmaker.Blueprints.Root.BlueprintRoot Kingmaker.Game::get_BlueprintRoot()
IL_0107: ldfld class Kingmaker.Blueprints.Root.ProgressionRoot Kingmaker.Blueprints.Root.BlueprintRoot::Progression
IL_0108: ldfld class Kingmaker.Blueprints.Classes.BlueprintStatProgression Kingmaker.Blueprints.Root.ProgressionRoot::XPTable
IL_0109: ldfld int32[] Kingmaker.Blueprints.Classes.BlueprintStatProgression::Bonuses
IL_010a: ldarg.1
IL_010b: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_010c: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_010d: ldc.i4.1
IL_010e: add
IL_010f: ldelem.i4
IL_0110: ldc.i4.2
IL_0111: div
IL_011c: stloc.3
IL_011d: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_011e: callvirt instance class Kingmaker.Blueprints.Root.BlueprintRoot Kingmaker.Game::get_BlueprintRoot()
IL_011f: ldfld class Kingmaker.Blueprints.Root.ProgressionRoot Kingmaker.Blueprints.Root.BlueprintRoot::Progression
IL_0120: ldfld class Kingmaker.Blueprints.Classes.BlueprintStatProgression Kingmaker.Blueprints.Root.ProgressionRoot::XPTable
IL_0121: ldfld int32[] Kingmaker.Blueprints.Classes.BlueprintStatProgression::Bonuses
IL_0122: ldarg.1
IL_0123: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_0124: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_0125: ldelem.i4
IL_0126: ldc.i4.2
IL_0127: div
IL_0128: stloc.s V_4
IL_0140: br IL_0174
IL_0141: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_0142: callvirt instance class Kingmaker.Blueprints.Root.BlueprintRoot Kingmaker.Game::get_BlueprintRoot()
IL_0143: ldfld class Kingmaker.Blueprints.Root.ProgressionRoot Kingmaker.Blueprints.Root.BlueprintRoot::Progression
IL_0144: ldfld class Kingmaker.Blueprints.Classes.BlueprintStatProgression Kingmaker.Blueprints.Root.ProgressionRoot::XPTable
IL_0145: ldfld int32[] Kingmaker.Blueprints.Classes.BlueprintStatProgression::Bonuses
IL_0146: ldc.i4.s 20
IL_0147: ldelem.i4
IL_0148: ldc.i4.2
IL_0149: div
IL_014a: ldarg.1
IL_014b: ldfld class Kingmaker.UnitLogic.UnitProgressionData Kingmaker.UnitLogic.UnitDescriptor::Progression
IL_014c: callvirt instance int32 Kingmaker.UnitLogic.UnitProgressionData::get_CharacterLevel()
IL_014d: ldc.i4.s 19
IL_014e: sub
IL_014f: ldc.i4 90000
IL_0150: mul
IL_0151: add
IL_0152: stloc.3
IL_0153: ldloc.3
IL_0154: ldc.i4 90000
IL_0155: sub
IL_0172: stloc.s V_4
职业等级上限及升级能力点数修改
* } // end of method BlueprintCharacterClass::MeetsPrerequisites
IL_001a: ldc.i4.s 40
IL_001c: blt IL_0023
IL_0021: ldc.i4.0
IL_0022: ret
IL_0023: ldarg.0
IL_0024: ldfld bool Kingmaker.Blueprints.Classes.BlueprintCharacterClass::PrestigeClass
IL_0029: brfalse IL_0038
IL_002e: ldloc.0
IL_002f: ldc.i4.s 40
* } // end of method ProgressionData::GetLevelEntry
IL_0000: ldarg.1
IL_0001: ldc.i4.s 20
IL_0002: rem
IL_0003: starg.s level
IL_0004: ldarg.1
IL_0005: brtrue.s IL_0008
IL_0006: ldc.i4.s 20
IL_0007: starg.s level
IL_0008: newobj instance void Kingmaker.UnitLogic.ProgressionData/'<GetLevelEntry>c__AnonStorey0'::.ctor()
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: ldarg.1
IL_000c: stfld int32 Kingmaker.UnitLogic.ProgressionData/'<GetLevelEntry>c__AnonStorey0'::level
IL_000d: ldarg.0
角色属性点获得修改(每两级获得1点属性点)
* } // end of method LevelUpState::.ctor
IL_00c8: ldc.i4.2
角色升级生命值修改(每级获得生命点数为:角色生命骰+2+体质加点)
*// end of method ApplyClassMechanics::ApplyHitPoints
IL_00d4: ldc.i4.1
角色负重值修改(100倍负重)
* } // end of method EncumbranceHelper::GetHeavy
IL_007a: call float64 [mscorlib]System.Math::Round(float64)
IL_007b: conv.i4
IL_007c: ldc.i4.s 100
IL_007d: mul
IL_007f: conv.i4
角色移动速度修改(移动1.5倍速,战斗1.25倍速)
* } // end of method TimeController::Tick
IL_0063: beq IL_0068
IL_0064: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_0065: callvirt instance valuetype Kingmaker.GameModes.GameModeType Kingmaker.Game::get_CurrentMode()
IL_0066: ldc.i4.7
IL_0067: bne.un IL_00aa
IL_0068: call class Kingmaker.Game Kingmaker.Game::get_Instance()
IL_0069: callvirt instance class Kingmaker.Player Kingmaker.Game::get_Player()
IL_006a: callvirt instance bool Kingmaker.Player::get_IsInCombat()
IL_006b: brtrue IL_0073
IL_006c: ldarg.0
IL_006d: ldarg.0
IL_006e: call instance float32 Kingmaker.Controllers.TimeController::get_DeltaTime()
IL_006f: ldc.r4 1.5
IL_0070: mul
IL_0071: call instance void Kingmaker.Controllers.TimeController::set_DeltaTime(float32)
IL_0072: br IL_0079
IL_0073: ldarg.0
IL_0074: ldarg.0
IL_0075: call instance float32 Kingmaker.Controllers.TimeController::get_DeltaTime()
IL_0076: ldc.r4 1.25
IL_0077: mul
IL_0078: call instance void Kingmaker.Controllers.TimeController::set_DeltaTime(float32)
IL_0079: ldarg.0
IL_007a: ldarg.0
IL_007b: call instance float32 Kingmaker.Controllers.TimeController::get_DeltaTime()
IL_007f: call instance void Kingmaker.Controllers.TimeController::set_GameDeltaTime(float32)
角色大地图移动速度修改(VisualSpeedBase越大移动越快,MechanicsSpeedBase越大游戏时间流逝越慢)
* } // end of method GlobalMapRoot::.ctor
IL_0000: ldarg.0
IL_0001: ldc.r4 0.02
IL_0006: stfld float32 Kingmaker.Blueprints.Root.GlobalMapRoot::VisualSpeedBase
IL_000b: ldarg.0
IL_000c: ldc.r4 12.
IL_0011: stfld float32 Kingmaker.Blueprints.Root.GlobalMapRoot::MechanicsSpeedBase
视野缩放范围修改
* } // end of method CameraZoom::TickZoom
IL_0088: ldc.r4 30
IL_008f: ldc.r4 5
新增隐藏UI开关快捷键 Ctrl + Alt + H
* } // end of method KeyboardAccess::RegisterBuiltinBindings
IL_04d1: ldarg.0
IL_04d2: ldstr "RapidBugReportWindowOpen"
IL_04d3: ldc.i4.s 98
IL_04d4: ldloc.0
IL_04d5: ldc.i4.1
IL_04d6: ldc.i4.1
IL_04d7: ldc.i4.1
IL_04d8: ldc.i4.0
IL_04d9: ldc.i4.0
IL_04da: call instance void Kingmaker.UI.KeyboardAccess::RegisterBinding(string,
valuetype [UnityEngine.CoreModule]UnityEngine.KeyCode,
class [mscorlib]System.Collections.Generic.IEnumerable`1<valuetype Kingmaker.GameModes.GameModeType>,
bool,
bool,
bool,
bool,
valuetype Kingmaker.UI.KeyboardAccess/ModificationSide)
IL_04db: ldarg.0
IL_04dc: ldstr "HideUI"
IL_04dd: ldc.i4.s 104
IL_04de: ldloc.0
IL_04df: ldc.i4.1
IL_04e0: ldc.i4.1
IL_04e1: ldc.i4.0
IL_04e2: ldc.i4.0
IL_04e3: ldc.i4.0
IL_04e4: call instance void Kingmaker.UI.KeyboardAccess::RegisterBinding(string,
valuetype [UnityEngine.CoreModule]UnityEngine.KeyCode,
class [mscorlib]System.Collections.Generic.IEnumerable`1<valuetype Kingmaker.GameModes.GameModeType>,
bool,
bool,
bool,
bool,
valuetype Kingmaker.UI.KeyboardAccess/ModificationSide)
IL_04e5: ldarg.0
IL_04e6: call void Kingmaker.PubSubSystem.EventBus::Subscribe(object)
IL_04ea: ret
+ .field public class Kingmaker.GameStatistic Statistic
.field public class [UnityEngine.CoreModule]UnityEngine.GameObject GameObject
+ } // end of method Game::.cctor
.method private hidebysig instance void
'<Initialize>m__13'() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// 代码大小 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [UnityEngine.CoreModule]UnityEngine.GameObject Kingmaker.Game::GameObject
IL_0002: brtrue IL_000a
IL_0003: ldarg.0
IL_0004: ldarg.0
IL_0005: ldfld class Kingmaker.UI.UIAccess Kingmaker.Game::UI
IL_0006: callvirt instance class Kingmaker.Assets.UI.MainCanvas Kingmaker.UI.UIAccess::get_MainCanvas()
IL_0007: callvirt instance class [UnityEngine.UIModule]UnityEngine.CanvasGroup Kingmaker.Assets.UI.MainCanvas::get_CanvasGroup()
IL_0008: callvirt instance class [UnityEngine.CoreModule]UnityEngine.GameObject [UnityEngine.CoreModule]UnityEngine.Component::get_gameObject()
IL_0009: stfld class [UnityEngine.CoreModule]UnityEngine.GameObject Kingmaker.Game::GameObject
IL_000a: ldarg.0
IL_000b: ldfld class [UnityEngine.CoreModule]UnityEngine.GameObject Kingmaker.Game::GameObject
IL_000c: ldarg.0
IL_000d: ldfld class [UnityEngine.CoreModule]UnityEngine.GameObject Kingmaker.Game::GameObject
IL_000e: callvirt instance bool [UnityEngine.CoreModule]UnityEngine.GameObject::get_activeSelf()
IL_000f: ldc.i4.0
IL_0010: ceq
IL_0011: callvirt instance void [UnityEngine.CoreModule]UnityEngine.GameObject::SetActive(bool)
IL_0012: ret
} // end of method Game::'<Initialize>m__13'
* } // end of method Game::Initialize
IL_0037: ldarg.0
IL_0038: ldfld class Kingmaker.UI.KeyboardAccess Kingmaker.Game::Keyboard
IL_0039: call void Kingmaker.Utility.Screenshot::Initialize(class Kingmaker.UI.KeyboardAccess)
IL_003a: ldarg.0
IL_003b: ldfld class AuraSupport Kingmaker.Game::AuraSupport
IL_003c: callvirt instance void AuraSupport::Initialize()
IL_003d: ldarg.0
IL_003e: ldfld class Kingmaker.UI.KeyboardAccess Kingmaker.Game::Keyboard
IL_003f: ldstr "HideUI"
IL_0040: ldarg.0
IL_0041: ldftn instance void Kingmaker.Game::'<Initialize>m__13'()
IL_0042: newobj instance void [mscorlib]System.Action::.ctor(object,
native int)
IL_0048: callvirt instance void Kingmaker.UI.KeyboardAccess::Bind(string,
class [mscorlib]System.Action)
修改施法造成的友军伤害减半
* } // end of method AbilityAcceptBurnOnCast::OnCast
IL_0019: ldfld int32 Kingmaker.UnitLogic.Class.Kineticist.AbilityAcceptBurnOnCast::BurnValue
IL_001a: ldc.i4.2
IL_001b: div
IL_001e: ldarg.1
* } // end of method KineticistAbilityBurnCost::GetTotal
IL_0095: brfalse IL_009c
IL_009a: ldc.i4.1
IL_009b: br IL_009d
IL_009c: ldc.i4.0
IL_009d: mul
IL_009e: add
IL_009f: ldc.i4.2
IL_00a0: div
IL_00a3: ret
第四部分:IL语言相关
俺在3DMGAME论坛发的原帖链接: