You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Il2cpp游戏本地化Mod:不修改游戏类实现JSON扩展读取

IL2CPP游戏本地化Mod扩展字段解决方案

问题背景

正在开发一款IL2CPP游戏的本地化Mod,原实现流程为:读取JSON文件 → 反序列化为游戏原生GameClassList → 将数据存入游戏内置私有字典private Dictionary<string, GameClass> __instance._dic。现需在不修改游戏原生GameClass的前提下,为JSON添加自定义字段并让Mod读取使用。

尝试通过继承GameClass/GameClassList创建扩展类(NicknameData/NicknameDataList),但出现以下问题:

  • 游戏无法识别自定义类
  • 本地编译无错误,但协作端出现CustomClass无法转换为GameClass的类型转换异常

原有尝试代码

初始实现代码

var jsonString = File.ReadAllText("Myfile.json");
GameClassList Localization = JsonSerializer.Deserialize<GameClassList>(jsonString);
foreach (var applyChange in Localization.assets)
{
    __instance._dic[applyChange.name] = applyChange;
}

自定义子类代码

[Serializable]
public class NicknameData : GameClass
{
    public string? runame;
    public string? ruNickName;

    public NicknameData() {}
    public static GameClass HELP(NicknameData data)
    {
        var PLEASE = JsonSerializer.Serialize(data);
        GameClass assetData = JsonSerializer.Deserialize<GameClass>(PLEASE);
        return assetData;
    }
}

[Serializable]
public class NicknameDataList : GameClassList
{
    public new Il2CppSystem.Collections.Generic.List<NicknameData> assetData;

    public NicknameDataList() { }
}

Harmony补丁代码

public static string nicknameJSON = File.ReadAllText("JSON_FILE.json");
public static NicknameDataList nicknameDataList = JsonSerializer.Deserialize<NicknameDataList>(nicknameJSON);

[HarmonyPatch(typeof(StoryData), nameof(StoryData.Init))]
[HarmonyPostfix]
private static void StoryDataInit(StoryData __instance)
{
    GameClassList blyahaMuha = nicknameDataList;
    
    foreach (var nicknameData in blyahaMuha.assetData)
    {
        __instance._dic[nicknameData.name] = (GameClass)NicknameData.HELP((NicknameData)nicknameData);
    }
}

问题根源

  1. IL2CPP类型隔离机制:游戏原生GameClass是IL2CPP编译后的类型,Mod自定义的继承类位于独立Assembly中,跨Assembly的类型转换会因元数据不匹配导致失败。
  2. 继承结构破坏:NicknameDataList用new关键字覆盖assetData字段,导致基类GameClassList的对应字段未被填充,类型向上转换时丢失数据。
  3. 冗余序列化操作:先序列化自定义类再反序列化为GameClass的操作不仅低效,还会直接丢弃自定义字段,同时引入序列化兼容性问题。

可行解决方案

方案一:自定义数据映射(推荐)

核心思路:不修改原生类结构,通过全局字典将GameClass实例与自定义字段数据关联,避免类型转换问题。

  1. 定义自定义数据类
[Serializable]
public class CustomLocalizationEntry
{
    public string name; // 与GameClass的name字段对应,作为关联键
    public string? runame;
    public string? ruNickName;
}

[Serializable]
public class CustomLocalizationList
{
    public List<CustomLocalizationEntry> assets;
}
  1. 读取并关联数据
// 读取JSON内容
var jsonContent = File.ReadAllText("Myfile.json");

// 反序列化为原生类型,存入游戏字典
var gameDataList = JsonSerializer.Deserialize<GameClassList>(jsonContent);
foreach (var asset in gameDataList.assets)
{
    __instance._dic[asset.name] = asset;
}

// 反序列化为自定义类型,建立全局映射
var customDataList = JsonSerializer.Deserialize<CustomLocalizationList>(jsonContent);
var customDataMap = new Dictionary<string, CustomLocalizationEntry>();
foreach (var entry in customDataList.assets)
{
    customDataMap[entry.name] = entry;
}
  1. 使用自定义字段
// 示例:获取目标GameClass对应的自定义昵称
if (customDataMap.TryGetValue(targetGameClass.name, out var customEntry))
{
    string localizedNick = customEntry.ruNickName ?? targetGameClass.name;
    // 后续业务逻辑
}

方案二:反射注入字段(适合需修改原生类行为场景)

如果需要让GameClass实例直接持有自定义字段,可通过反射在IL2CPP环境下注入数据(需确保反射工具兼容IL2CPP,如Il2CppInterop)。

  1. 读取自定义数据
var jsonContent = File.ReadAllText("JSON_FILE.json");
var nicknameEntries = JsonSerializer.Deserialize<List<CustomLocalizationEntry>>(jsonContent);
  1. Harmony补丁中注入数据
[HarmonyPatch(typeof(StoryData), nameof(StoryData.Init))]
[HarmonyPostfix]
private static void StoryDataInit(StoryData __instance)
{
    var jsonContent = File.ReadAllText("JSON_FILE.json");
    var nicknameEntries = JsonSerializer.Deserialize<List<CustomLocalizationEntry>>(jsonContent);

    foreach (var entry in nicknameEntries)
    {
        if (__instance._dic.TryGetValue(entry.name, out var gameClass))
        {
            // 使用Il2Cpp反射获取或创建字段
            var gameClassType = gameClass.GetType();
            var targetField = gameClassType.GetField("ruNickName", 
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            if (targetField != null)
            {
                targetField.SetValue(gameClass, entry.ruNickName);
            }
            else
            {
                // 若原生类无对应字段, fallback到全局映射方案
                customDataMap[entry.name] = entry;
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Knightey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:15:00