本文已参与「新人创作礼」活动,一起开启掘金创作之路。
写在前面
- 修改游戏的做法可能不合适,仅供个人娱乐、学习、测试代码安全之用
- 仅讨论修改方法,不提供修改成品文件
背景
关于游戏内容
- 飞行射击游戏。没玩过的话建议先玩前作 Sky Force Anniversary
- 玩法上,需要大量重复刷星章,来升级武器、购买 Power-ups 等
- 本人为了缩短刷星时间,尝试修改
关于游戏的相关调研
- 由较早版本的 Unity 制作
- Backend 使用 Mono,未使用 IL2CPP,Assembly-CSharp.dll 未混淆
- 使用含 Mono 插件的 Cheat Engine 7.2 修改失败,估计是内部使用了类似 Anti-Cheat Toolkit 的扩展
- 已经有前人(peizhaochen)制作了7项修改器,但似乎不能直接修改星章,市面上又几乎没有其他可用的修改器
关于环境
- Windows 10 专业版 20H2
- .Net Reflector 10.1 Standard
- 安装 Reflexil v2.4
- 从 Steam 购买的正版 Sky Force Reloaded
修改方法
直接上结论,具体过程经历了多次实践和失败,见后文。
- 用 .Net Reflector 打开游戏的 Assembly-CSharp.dll。
- 打开空命名空间(-)下的
Player.EvCollectedStar(BonusStar) : Void
方法,得到代码,并作如下修改:
protected virtual void EvCollectedStar(BonusStar coStar)
{
int nCollectedStars = 0;
if (coStar.Type == eBonusType.Star)
{
this.m_fCollectedStars += TSingleton<CardsManager>.singleton.IsStarDoublerActive ? 2f : 1f;
nCollectedStars = Mathf.FloorToInt(this.m_fCollectedStars);
this.m_fCollectedStars -= nCollectedStars;
//SaveGame.CurrentNumCollectedStarsNotSafe += nCollectedStars; // 原句,注释掉
SaveGame.CurrentNumCollectedStarsNotSafe += nCollectedStars * 1000; // 修改为,令存档中的数值为实际收获的 1000 倍
}
else if (coStar.Type == eBonusType.Star2x)
{
this.m_fCollectedStars += TSingleton<CardsManager>.singleton.IsStarDoublerActive ? 10f : 5f;
nCollectedStars = Mathf.FloorToInt(this.m_fCollectedStars);
this.m_fCollectedStars -= nCollectedStars;
//SaveGame.CurrentNumCollectedStarsNotSafe += nCollectedStars; // 原句,注释掉
SaveGame.CurrentNumCollectedStarsNotSafe += nCollectedStars * 1000; // 同上
}
this.LaunchStarCollectedEvent(coStar, nCollectedStars, SaveGame.CurrentNumCollectedStars);
}
- 使用 Reflexil 重写此方法,然后替换原 dll 即可。注意:
- 关于 Reflexil 的基本使用方法,见此处;
- 点击 Compile 后可能报错“不存在或未定义相关方法”等,这时双击错误,编译窗口会跳转到出错的方法或定义,将其注释掉即可(这是作者在编译窗口留下的原话),或删掉也可以,没出错的不要动;
- 点击 Compile 后报错
CS1073
,是因为 Assembly-CSharp.dll 引用的 mscorlib.dll(与 Assembly-CSharp.dll 位于同一目录)的版本与 UnityEngine 命名空间内引用的 mscorlib.dll(版本 2.0.0.0)的不一致。这是很少见的情况。解决方法是,在点击 Compile 前,将 mscorlib.dll(与 Assembly-CSharp.dll 位于同一目录)重命名(如“mscorlib.dll0”)再点击 Compile,通过后撤销重命名就可以了。这是作者提供的方法,详见此处。
- 修改效果:打开游戏任意一关任意难度,至少收集到 1 个星章(否则不会触发
Player.EvCollectedStar(BonusStar) : Void
方法),然后想办法自杀或退出当前关卡,当然,正常通关也是可以的。战绩界面之前的星章界面,提示的是真正收集到的星章数;但当返回到选关界面,就会发现实际在存档中保存的是 1000 倍的星章数。 - 反复刷取,或将倍率改得更大,可以得到较为满意的星章,用它就可以快速升级武器装备。
- 若希望游戏结束后的星章界面也显示修改后的数值,而不要显示真实值,则找到
Stage.EvCollectedStar(BonusStar) : Void
方法,作如下修改:
private void EvCollectedStar(BonusStar coStar, int nEarnedStars, int nTotalStars)
{
if (coStar.Type == eBonusType.Star)
{
this._Stats_StarsCollected++;
}
else if (coStar.Type == eBonusType.Star2x)
{
this._Stats_StarsCollected += 5;
}
//this._Stats_StarsEarned += nEarnedStars; // 原句,注释掉
this._Stats_StarsEarned += nEarnedStars * 1000; // 修改为,令显示的数值为实际收获的 1000 倍,但不会保存到存档
if (!TSingleton<CardsManager>.singleton.IsMoreStarsFromEnemiesActive)
{
this.AddPoints(coStar.Points);
}
else
{
this.AddPoints(coStar.Points / 2);
}
}
如何找到修改处
具体查找过程,经历了多次失败。太具体的这里也暂时不写出来了,只总结一下大致方法和流程。
- 首先检查代码的混淆情况和结构,发现未混淆,代码结构清晰,命名良好,这就很方便阅读和修改了;
- 以星章为切入点,通过阅读代码结构确定其名称,是 Star,而不是 Point/Bonus 之类的近义词;
- 搜索 Star,在搜索结果中,首先阅读方法名和定义名,找到感觉比较接近的,再详细阅读其方法的实现。感觉可能是目标的,就标记 Bookmark,或进一步 Analyze,查看它调用了谁以及谁调用了它;
- 慢慢挖掘,直到找到一个方法,你发现它能够基本解释 Star 变化的原因,同时,这个方法和调用它的方法在逻辑上能够自洽,并且便于修改。那么,这个方法就可以作为修改目标了;
- 修改它,放到程序里试运行,看是否达到目标。当然,这个方法可能并不能带来预期的效果,这时候就要考虑是否该方法的触发条件满足了,或者 Star 这个切入点根本就是错误的?这就需要反复尝试,直到成功,对其他的修改对象也是如此。