COG模组开发文档

目录

一、Utils - 工具与实用方法

1. 资源与路径相关

1.1 获取 Sprite 资源

ResourceUtils.LoadSpriteFromAssembly("资源位置", 像素大小)

  • 描述: 从程序集的嵌入资源中加载一张图片并返回为 Sprite 对象
  • 参数:
    • 资源位置: 资源文件名(例如 "Example.png"),无需完整路径
    • 像素大小: Sprite 的像素大小,默认为 100f
  • 返回值: Sprite,可直接赋给 SpriteRenderer
  • 命名空间: COG.Utils

1.2 获取 Among Us 游戏目录

PathUtils.GetAmongUsPath(string additionPath = "")

  • 描述: 通过查找正在运行的 "Among Us" 进程,获取其游戏根目录的绝对路径。可选地拼接一个子路径
  • 参数:
    • additionPath: 需要追加到根目录后的子路径(例如 "BepInEx\plugins")
  • 返回值: 游戏目录的完整路径字符串,如果未找到进程则返回 null
  • 命名空间: COG.Utils

1.3 从嵌入资源复制文件到磁盘

PathUtils.CopyResourcesToDisk(string targetDirectory, string embeddedDirectory, bool overwrite = true)

  • 描述: 将程序集中指定嵌入资源目录下的所有文件,递归地复制到磁盘上的目标文件夹中
  • 参数:
    • targetDirectory: 磁盘上的目标文件夹路径
    • embeddedDirectory: 程序集内嵌入资源的相对目录名(例如 "Images" 对应 COG.Resources.Images)
    • overwrite: 是否覆盖已存在的文件
  • 命名空间: COG.Utils

2. 网络与更新相关

2.1 下载文件 (协程)

AdvancedExampleCoroutine("文件 url", "文件绝对路径")

  • 描述: 一个 IEnumerator 协程方法,用于从指定 URL 下载文件并保存到本地绝对路径
  • 参数:
    • 文件 url: 要下载文件的网络地址
    • 文件绝对路径: 文件在本地磁盘上保存的完整路径(包含文件名)
  • 使用案例:
public static IEnumerator DownloadYaml()
{
    yield return AdvancedExampleCoroutine(
        "https://xtreme.net.cn/upload/YamlDotNet.dll",
        @$"{PathUtils.GetAmongUsPath()}BepInExcore"
    );
}

2.2 检查模组更新

ModUpdater.FetchUpdate()

  • 描述: (当前实现被注释掉)尝试从 GitHub API 获取模组的最新版本信息和更新日志,并存储在 ModUpdater.LatestVersion 和 ModUpdater.LatestDescription 中
  • 命名空间: COG.Utils

2.3 执行模组更新

ModUpdater.DoUpdate()

  • 描述: 从预设的服务器下载最新版本的 COG.dll,并通过一个 VBScript 脚本在游戏关闭后替换旧文件
  • 命名空间: COG.Utils

2.4 获取网页内容

WebUtils.GetWeb(string url)

  • 描述: 同步地向指定 URL 发送 GET 请求,并返回网页的文本内容
  • 参数:
    • url: 目标网页地址
  • 返回值: 网页的源代码字符串,失败时返回空字符串
  • 命名空间: COG.Utils

3. 日志与调试相关

3.1 带调用栈的日志记录器

类: StackTraceLogger

  • 描述: 一个增强的日志记录器,能自动在日志消息前附加 [类名::方法名] 的前缀,便于追踪日志来源。支持禁用特定类型或方法的日志输出
  • 主要方法:
    • LogInfo, LogError, LogWarning 等:记录不同级别的日志
    • DisableSource(Type toDisable): 禁用某个类的所有日志
    • EnableSource(Type toEnable): 启用某个类的日志
  • 命名空间: COG.Utils

3.2 对象转储 (Dump)

LoggerUtils.Dump(this T obj)

  • 描述: 一个扩展方法,用于快速将对象的内容打印到日志中,并返回该对象本身,方便链式调用
  • 命名空间: COG.Utils

4. 游戏逻辑与玩家相关

4.1 获取 Among Us 游戏选项

GameUtils.GetGameOptions()

  • 描述: 获取当前游戏的 NormalGameOptionsV10 实例,可用于读取或修改游戏设置(如击杀距离、内鬼数量等)
  • 返回值: NormalGameOptionsV10
  • 命名空间: COG.Utils

4.2 获取当前启用的内鬼数量

GameUtils.GetImpostorsNumber()

  • 描述: 根据当前房间的玩家总数和游戏设置,计算出实际生效的内鬼数量
  • 返回值: int
  • 命名空间: COG.Utils

4.3 在游戏中发送消息

GameUtils.SendGameMessage(string text)

  • 描述: 在屏幕顶部发送一条类似断开连接的红色提示消息
  • 参数:
    • text: 要发送的消息内容
  • 命名空间: COG.Utils

4.4 获取所有玩家

PlayerUtils.GetAllPlayers()

  • 描述: 获取当前游戏中所有玩家的 PlayerControl 列表
  • 返回值: List<PlayerControl>
  • 命名空间: COG.Utils

4.5 获取所有存活玩家

PlayerUtils.GetAllAlivePlayers()

  • 描述: 获取当前游戏中所有存活玩家的 PlayerControl 列表
  • 返回值: List<PlayerControl>
  • 命名空间: COG.Utils

4.6 杀死玩家(不生成尸体)

PlayerUtils.RpcKillWithoutDeadBody(this PlayerControl killer, PlayerControl target, ...)

  • 描述: 通过 RPC 调用杀死一名玩家,但不会在游戏中生成尸体(DeadBody)
  • 命名空间: COG.Utils

4.7 复活玩家

PlayerUtils.RpcRevive(this PlayerControl player)

  • 描述: 通过 RPC 调用复活一名已死亡的玩家
  • 命名空间: COG.Utils

4.8 设置玩家自定义职业

PlayerUtils.RpcSetCustomRole(this PlayerControl pc) where T : CustomRole

  • 描述: 通过 RPC 调用,将一名玩家的职业设置为指定的自定义职业 T
  • 命名空间: COG.Utils

5. UI 与视觉效果相关

5.1 创建浮动文本

类: FloatingText

  • 描述: 在屏幕指定位置创建一个可自定义的浮动文本(类似于 PingTracker)
  • 主要方法:
    • 构造函数: new FloatingText(...) 初始化配置
    • Create(string text): 创建并显示文本
    • Destroy(): 销毁文本对象
  • 命名空间: COG.Utils

5.2 显示浮动文本 (协程)

IEnumeratorUtils.CoShowText(string text, Vector3 vector, Color color, float duration, int fontsize, TextAlignmentOptions alignment)

  • 描述: 一个协程,用于在指定世界坐标位置显示一个彩色浮动文本,并在 duration 秒后自动销毁
  • 命名空间: COG.Utils

5.3 为 GameObject 创建子对象

GameObjectUtils.CreateObject(string objName, Transform parent, Vector3 localPosition, int? layer = null)

  • 描述: 在指定父对象下创建一个新的 GameObject,并设置其名称、局部位置和层级
  • 返回值: 新创建的 GameObject
  • 命名空间: COG.Utils

5.4 安全销毁对象

GameObjectUtils.TryDestroy(this Object? obj)

  • 描述: 一个扩展方法,在销毁 Unity 对象前先检查其是否为 null,避免报错
  • 命名空间: COG.Utils

6. 工具与辅助方法

6.1 颜色工具

类: ColorUtils

  • 描述: 提供颜色相关的实用方法
  • 主要方法:
    • ToColorString(this Color color, string str): 将字符串包裹在 Rich Text 颜色标签中
    • AsColor(string color): 将十六进制颜色字符串(如 "#FF0000")转换为 Color 对象
    • GradientColorText(...): 生成带有渐变色彩的 Rich Text 字符串
  • 命名空间: COG.Utils

6.2 集合工具

类: CollectionUtils

  • 描述: 为集合(尤其是 List)提供扩展方法
  • 主要方法:
    • Disarrange(this IEnumerable list): 打乱列表顺序
    • Pop(this List list): 弹出并移除列表的第一个元素
    • ForEach(this IEnumerable collection, Action action): 为集合中的每个元素执行操作
    • AsString(...): 将集合作为字符串输出,便于调试
  • 命名空间: COG.Utils

6.3 字符串工具

类: StringUtils

  • 描述: 提供字符串处理的扩展方法
  • 主要方法:
    • GetSHA1Hash(this string input): 计算字符串的 SHA1 哈希值
    • CustomFormat(this string text, params object[] args): 使用 % 作为占位符进行字符串格式化
    • EncodeToBase64 / DecodeAsBase64: 进行 Base64 编码和解码
  • 命名空间: COG.Utils

6.4 Yaml 解析器

类: Yaml

  • 描述: 一个用于加载、解析和操作 Yaml 文件/字符串的工具类
  • 主要方法:
    • LoadFromFile(string path): 从文件加载 Yaml
    • LoadFromString(string text): 从字符串加载 Yaml
    • GetInt / GetBool / GetString / GetStringList(string location): 根据路径(如 "player.health")获取对应的值
    • WriteTo(string path): 将修改后的 Yaml 内容写回文件
  • 命名空间: COG.Utils

二、重要方法 - 核心系统功能

1. 配置系统 (Config System)

1.1 配置基类

ConfigBase

  • 描述: 所有配置类的基类,用于加载和管理配置文件
  • 主要方法:
    • LoadConfigs(bool replace = false):
      • 功能: 加载配置文件。如果文件不存在或需要覆盖,则创建新文件;否则从文件加载配置
      • 参数:
        • replace: 是否覆盖现有配置文件(默认为 false)
      • 流程:
        1. 创建数据目录 DataDirectoryName(默认为 COG_DATA)
        2. 如果文件存在且 replace 或 AutoReplace 为 true,则备份原文件
        3. 如果文件不存在或需要覆盖,则写入默认配置文本
        4. 否则从文件加载配置文本
        5. 解析 YAML 内容到 YamlReader
  • 命名空间: COG.Config

1.2 配置类

SettingsConfig

  • 描述: 系统设置配置类,继承自 ConfigBase
  • 主要属性:
    • EnablePluginSystem: 是否启用插件系统(从配置文件中读取)
    • TimeZone: 时区设置(从配置文件中读取)
    • Culture: 语言文化设置(从配置文件中读取)
    • Strict: 严格模式(从配置文件中读取)
  • 初始化:
    • static SettingsConfig(): 静态构造函数,创建单例实例
    • SettingsConfig(): 构造函数,加载 settings.yml 配置文件
  • 命名空间: COG.Config.Impl

LanguageConfig

  • 描述: 语言配置类,继承自 ConfigBase,处理多语言支持
  • 主要属性:
    • 大量字符串属性(如 MakePublicMessage, Yes, No, Cancel, Confirm 等),用于存储不同语言的翻译文本
  • 主要方法:
    • GetString(string location):
      • 功能: 从语言配置中获取指定路径的翻译字符串
      • 参数:
        • location: 翻译字符串的路径(如 "lobby.make-public-message")
      • 返回值: 翻译后的字符串,如果找不到则返回原始路径
    • SetTranslations():
      • 功能: 从配置文件加载所有翻译字符串
      • 流程:
        1. 使用 GetString 方法获取每个翻译字符串
        2. 将获取的字符串赋值给相应的属性
        3. 处理异常(如果加载失败,则显示错误消息并创建新实例)
  • 注意: 使用LanguageConfig时,请使用LanguageConfig.Instance获取LanguageConfig实例
  • 命名空间: COG.Config.Impl

2. 自定义选项系统 (Custom Options System)

2.1 选项值规则

IValueRule

  • 描述: 值规则接口,用于定义选项的合法值、默认值和验证逻辑
  • 主要属性:
    • DefaultSelection: 默认选择的索引
    • Selections: 选项的合法值数组
  • 命名空间: COG.UI.CustomOption.ValueRules

StringOptionValueRule

  • 描述: 字符串选项值规则实现
  • 主要方法:
    • IsValid(string item):
      • 功能: 验证字符串是否在合法值列表中
      • 返回值: 如果字符串有效则返回 true,否则返回 false
  • 命名空间: COG.UI.CustomOption.ValueRules.Impl

IntOptionValueRule

  • 描述: 整数选项值规则实现
  • 主要方法:
    • IsValid(int obj):
      • 功能: 验证整数是否在合法范围内
      • 返回值: 如果整数有效则返回 true,否则返回 false
    • Validate(ref int obj):
      • 功能: 将值调整到合法范围内
      • 参数: obj - 需要验证的值
  • 命名空间: COG.UI.CustomOption.ValueRules.Impl

FloatOptionValueRule

  • 描述: 浮点数选项值规则实现
  • 主要方法:
    • IsValid(float obj):
      • 功能: 验证浮点数是否在合法范围内
      • 返回值: 如果浮点数有效则返回 true,否则返回 false
    • Validate(ref float obj):
      • 功能: 将值调整到合法范围内
      • 参数: obj - 需要验证的值
  • 命名空间: COG.UI.CustomOption.ValueRules.Impl

BoolOptionValueRule

  • 描述: 布尔选项值规则实现
  • 主要方法:
    • IsValid(bool obj):
      • 功能: 验证布尔值是否有效(总是有效)
      • 返回值: 总是返回 true
  • 命名空间: COG.UI.CustomOption.ValueRules.Impl

2.2 自定义选项

CustomOption

  • 描述: 自定义选项类,用于在游戏中创建和管理自定义选项
  • 主要属性:
    • Id: 选项的唯一标识符
    • Name: 选项的名称(通过委托获取)
    • Page: 选项所属的页面类型(TabType)
    • ValueRule: 选项的值规则(实现 IValueRule)
    • Selection: 当前选择的索引
  • 主要方法:
    • Of<T>(TabType type, Func<string> nameGetter, IValueRule rule, CustomOption? parent = null):
      • 功能: 创建一个新的自定义选项实例
      • 参数:
        • type: 选项所属的页面类型
        • nameGetter: 获取选项名称的委托
        • rule: 选项的值规则
        • parent: 父选项(如果存在)
      • 返回值: 新创建的 CustomOption 实例
    • Register():
      • 功能: 注册选项,使其在游戏设置中可用
      • 返回值: 选项自身
    • GetBool():
      • 功能: 获取选项的布尔值
      • 返回值: 选项的当前选择的布尔值
    • GetInt():
      • 功能: 获取选项的整数值
      • 返回值: 选项的当前选择的整数值
    • GetString():
      • 功能: 获取选项的字符串值
      • 返回值: 选项的当前选择的字符串值
  • 命名空间: COG.UI.CustomOption

GlobalCustomOptionConstant

  • 描述: 全局自定义选项常量类,用于定义和注册常用选项
  • 主要属性:
    • DebugMode: 调试模式选项
    • MaxAddonNumber: 最大附加职业数量选项
    • MaxNeutralNumber: 最大中立角色数量选项
  • 初始化:
    • static GlobalCustomOptionConstant(): 静态构造函数,初始化选项
  • 命名空间: COG.Constant

3. 自定义按钮系统 (Custom Button System)

3.1 按钮管理器

CustomButtonManager

  • 描述: 自定义按钮管理器,用于注册和管理所有自定义按钮
  • 主要方法:
    • RegisterCustomButton(CustomButton button):
      • 功能: 注册一个自定义按钮
      • 参数: button - 要注册的按钮实例
    • RegisterCustomButtons(IEnumerable<CustomButton> buttons):
      • 功能: 注册多个自定义按钮
      • 参数: buttons - 要注册的按钮集合
    • GetButtons():
      • 功能: 获取所有注册的自定义按钮
      • 返回值: 按钮列表
  • 命名空间: COG.UI.Hud.CustomButton

3.2 自定义按钮

CustomButton

  • 描述: 自定义按钮类,用于在游戏中创建和管理自定义按钮
  • 主要属性:
    • Id: 按钮的唯一标识符
    • Identifier: 按钮的标识符
    • OnClick: 按钮点击时的回调函数
    • OnMeetingEnds: 会议结束时的回调函数
    • OnEffect: 按钮效果结束时的回调函数
    • CouldUse: 按钮是否可用的条件
    • HasButton: 玩家是否拥有此按钮的条件
    • Sprite: 按钮的图标
    • Position: 按钮的位置
    • Text: 按钮的文本
    • Cooldown: 按钮的冷却时间
    • EffectTime: 按钮效果持续时间
    • UsesLimit: 按钮使用次数限制
    • Hotkey: 按钮的热键
  • 主要方法:
    • Of(string identifier, Action onClick, Action onMeetingEnds, Action onEffect, Func<bool> couldUse, Func<bool> hasButton, Sprite sprite, Vector3 position, string text, Func<float> cooldown, float effectTime, int usesLimit, KeyCode hotkey, bool isMeetingButton, int order = -1):
      • 功能: 创建一个带效果的自定义按钮实例
      • 参数:
        • identifier: 按钮的标识符
        • onClick: 按钮点击时的回调函数
        • onMeetingEnds: 会议结束时的回调函数
        • onEffect: 按钮效果结束时的回调函数
        • couldUse: 按钮是否可用的条件
        • hasButton: 玩家是否拥有此按钮的条件
        • sprite: 按钮的图标
        • position: 按钮的位置
        • text: 按钮的文本
        • cooldown: 按钮的冷却时间
        • effectTime: 按钮效果持续时间
        • usesLimit: 按钮使用次数限制
        • hotkey: 按钮的热键
        • isMeetingButton: 是否是会议按钮
        • order: 按钮在 HUD 中的顺序
      • 返回值: 创建的 CustomButton 实例
    • Of(string identifier, Action onClick, Action onMeetingEnds, Func<bool> couldUse, Func<bool> hasButton, Sprite sprite, Vector3 position, string text, Func<float> cooldown, int usesLimit, KeyCode hotkey, bool isMeetingButton, int order = -1):
      • 功能: 创建一个不带效果的自定义按钮实例
      • 参数: 与上述方法类似,但不包含 onEffect 参数
    • SetActive(bool active):
      • 功能: 设置按钮的可见性
      • 参数: active - 是否显示按钮
    • Update():
      • 功能: 每帧更新按钮状态(如冷却时间、可见性等)
  • 命名空间: COG.UI.Hud.CustomButton

3.3 玩家选择菜单系统

PlayetSelectMenu.cs 文件实现了COG模组中的玩家选择菜单功能,这是一个用于在游戏会议期间提供玩家选择界面的UI组件。

3.3.1 核心类结构

PlayerSelectionManager

public class PlayerSelectionManager {
    private static List<PlayerSelectionUI> _activeInstances = new List<PlayerSelectionUI>();
    
    public static PlayerSelectionUI CreateSelectionUI(string titleText = "选择玩家", Action<PlayerControl> onPlayerSelected = null) {
        // 创建并返回新的PlayerSelectionUI实例
    }
    
    public static void CleanupAllInstances() {
        // 清理所有活动的玩家选择UI
    }
}

功能说明:

  • 作为全局管理器,确保同一时间只有一个玩家选择菜单实例存在
  • 提供创建菜单的便捷方法,支持自定义标题和选择回调
  • 确保菜单资源的正确清理,避免内存泄漏

PlayerSelectionUI

public class PlayerSelectionUI {
    public string TitleText { get; private set; }
    public PlayerControl SelectedPlayer { get; private set; }
    public GameObject UIInstance { get; private set; }
    
    // 构造函数
    public PlayerSelectionUI(MeetingHud meetingHud, string titleText, Action<PlayerControl> onPlayerSelected = null) {
        // 初始化UI
    }
    
    // 显示UI
    public void ShowPlayerSelectionUI() {
        // 创建并显示选择菜单
    }
    
    // 创建UI元素
    private void CreateUIElements(Transform container) {
        // 创建标题、退出按钮和玩家按钮
    }
    
    // 处理玩家按钮点击
    private void OnPlayerButtonClicked(PlayerControl player, Transform button) {
        // 选择玩家并触发回调
    }
    
    // 关闭UI
    public void CloseSelectionUI() {
        // 清理UI并恢复游戏状态
    }
    
    public void Cleanup() {
        // 完全清理UI资源
    }
}

4. 自定义角色系统 (Custom Role System)

4.1 角色管理

CustomRoleManager

  • 描述: 自定义角色管理器,用于注册和管理所有自定义角色
  • 主要方法:
    • RegisterRole(CustomRole role):
      • 功能: 注册一个自定义角色
      • 参数: role - 要注册的角色实例
    • RegisterRoles(CustomRole[] roles):
      • 功能: 注册多个自定义角色
      • 参数: roles - 要注册的角色数组
    • GetTypeCampRoles(CampType campType):
      • 功能: 获取指定阵营的所有角色
      • 参数: campType - 角色阵营类型
      • 返回值: 指定阵营的角色数组
    • GetRoles():
      • 功能: 获取所有自定义角色
      • 返回值: 所有自定义角色的列表
    • GetModRoles():
      • 功能: 获取所有模组角色(非基础角色)
      • 返回值: 模组角色的列表
  • 命名空间: COG.Role

4.2 角色基类

CustomRole

  • 描述: 自定义角色基类,所有自定义角色必须继承此基类
  • 主要属性:
    • Id: 角色的唯一标识符
    • Color: 角色的颜色
    • Name: 角色的名称
    • ShortName: 角色的简短名称
    • CampType: 角色的阵营类型(CampType 枚举)
    • CanKill: 是否可以击杀
    • CanVent: 是否可以跳管
    • CanSabotage: 是否可以破坏
    • RoleNumberOption: 角色数量选项
    • RoleChanceOption: 角色几率选项
    • RoleCode: 角色代码选项
    • AllButtons: 角色的所有按钮
  • 主要方法:
    • CreateOption(Func<string> nameGetter, IValueRule rule):
      • 功能: 创建一个新的自定义选项
      • 参数:
        • nameGetter: 获取选项名称的委托
        • rule: 选项的值规则
      • 返回值: 创建的自定义选项
    • AddButton(CustomButton button, Func<bool>? hasButton = null):
      • 功能: 为角色添加一个自定义按钮
      • 参数:
        • button: 要添加的按钮
        • hasButton: 玩家是否拥有此按钮的条件(默认为 PlayerControl.LocalPlayer.IsRole(this))
    • GetLongDescription():
      • 功能: 获取角色的详细介绍
      • 返回值: 角色的详细介绍字符串
    • IsAvailable():
      • 功能: 检查角色是否可用(根据配置和随机数)
      • 返回值: 如果角色可用则返回 true,否则返回 false
  • 命名空间: COG.Role

CampType

  • 描述: 角色阵营类型枚举
  • 枚举值:
    • Crewmate: 神职人员阵营
    • Neutral: 中立阵营
    • Impostor: 内鬼阵营
    • Unknown: 未知阵营
  • 命名空间: COG.Role

ModiType

  • 描述: 附加职业类型枚举
  • 枚举值:
    • Addon: 附加职业
    • SubRole: 子职业
    • Unknown: 未知类型
  • 命名空间: COG.Role

OnlyOnRoleType

  • 描述: 附加职业适用类型枚举
  • 枚举值:
    • None: 无限制
    • Crewmate: 仅限神职人员
    • Neutral: 仅限中立角色
    • Impostor: 仅限内鬼
    • Global: 全局适用
  • 命名空间: COG.Role

5. 角色按钮系统 (Role Button System)

5.1 按钮设置

KillButtonSetting

  • 描述: 击杀按钮设置类,用于自定义击杀按钮的行为
  • 主要属性:
    • ForceShow: 是否强制显示按钮的条件
    • TargetOutlineColor: 目标轮廓颜色
  • 主要方法:
    • AddAfterClick(Action action):
      • 功能: 添加点击后执行的操作
      • 参数: action - 点击后执行的操作
  • 命名空间: COG.Role

5.2 角色按钮

RoleButton

  • 描述: 角色按钮类,用于表示角色的击杀按钮
  • 主要属性:
    • Role: 角色实例
    • Button: 按钮对象
  • 主要方法:
    • Initialize():
      • 功能: 初始化按钮
  • 命名空间: COG.Role

5.3 角色按钮系统

5.3.1 击杀按钮设置

KillButtonSetting

  • 描述: 击杀按钮设置类,用于自定义击杀按钮的行为
  • 主要属性:
    • ForceShow: 获取或设置一个委托,用于判断是否强制显示按钮的条件
    • TargetOutlineColor: 获取或设置目标玩家轮廓的颜色
  • 主要方法:
    • AddAfterClick(Action action)
      • 功能: 添加点击击杀按钮后执行的操作
      • 参数:
        • action: 点击后要执行的操作
  • 命名空间: COG.Role

5.3.2 角色按钮

RoleButton

  • 描述: 角色按钮类,用于表示角色的击杀按钮
  • 主要属性:
    • Role: 获取关联的角色实例
    • Button: 获取按钮对象
  • 主要方法:
    • Initialize()
      • 功能: 初始化按钮,设置其基本属性和行为
  • 命名空间: COG.Role

6. 工具与辅助方法

6.1 颜色工具

ColorUtils

  • 描述: 提供颜色相关的实用方法
  • 主要方法:
    • ToColorString(this Color color, string str)
      • 功能: 将字符串包裹在 Rich Text 颜色标签中,使其显示为指定颜色
      • 参数:
        • color: 目标颜色
        • str: 要着色的字符串
      • 返回值: 带有颜色标签的字符串
    • AsColor(string color)
      • 功能: 将十六进制颜色字符串(如 "#FF0000")转换为 Color 对象
      • 参数:
        • color: 十六进制颜色字符串
      • 返回值: 对应的 Color 对象
    • GradientColorText(...)
      • 功能: 生成带有渐变色彩的 Rich Text 字符串
      • 参数: 渐变颜色和文本内容
      • 返回值: 带有渐变效果的字符串
  • 命名空间: COG.Utils

6.2 集合工具

CollectionUtils

  • 描述: 为集合(尤其是 List)提供扩展方法
  • 主要方法:
    • Disarrange<T>(this IEnumerable<T> list)
      • 功能: 打乱列表顺序(随机排序)
      • 返回值: 打乱顺序后的新列表
    • Pop<T>(this List<T> list)
      • 功能: 弹出并移除列表的第一个元素
      • 返回值: 被移除的元素
    • ForEach<T>(this IEnumerable<T> collection, Action<T> action)
      • 功能: 为集合中的每个元素执行指定操作
      • 参数:
        • action: 要对每个元素执行的操作
    • AsString<T>(...)
      • 功能: 将集合作为格式化字符串输出,便于调试
      • 返回值: 表示集合内容的字符串
  • 命名空间: COG.Utils

6.3 字符串工具

StringUtils

  • 描述: 提供字符串处理的扩展方法
  • 主要方法:
    • GetSHA1Hash(this string input)
      • 功能: 计算字符串的 SHA1 哈希值
      • 参数:
        • input: 要计算哈希的字符串
      • 返回值: 哈希值的十六进制字符串表示
    • CustomFormat(this string text, params object[] args)
      • 功能: 使用 % 作为占位符进行字符串格式化(类似于 string.Format,但使用 % 代替 {0} 等)
      • 参数:
        • text: 包含 % 占位符的格式字符串
        • args: 要插入到占位符的参数
      • 返回值: 格式化后的字符串
    • EncodeToBase64(this string text)
      • 功能: 将字符串进行 Base64 编码
      • 返回值: Base64 编码后的字符串
    • DecodeAsBase64(this string base64Text)
      • 功能: 将 Base64 字符串解码为原始字符串
      • 返回值: 解码后的原始字符串
  • 命名空间: COG.Utils

6.4 Yaml 解析器

Yaml

  • 描述: 一个用于加载、解析和操作 Yaml 文件/字符串的工具类
  • 主要方法:
    • LoadFromFile(string path)
      • 功能: 从文件加载 Yaml 内容
      • 参数:
        • path: Yaml 文件的路径
      • 返回值: Yaml 实例
    • LoadFromString(string text)
      • 功能: 从字符串加载 Yaml 内容
      • 参数:
        • text: 包含 Yaml 内容的字符串
      • 返回值: Yaml 实例
    • GetInt(string location)
      • 功能: 根据路径(如 "player.health")获取对应的整数值
      • 参数:
        • location: 值的路径
      • 返回值: 找到的整数值
    • GetBool(string location)
      • 功能: 根据路径获取对应的布尔值
      • 返回值: 找到的布尔值
    • GetString(string location)
      • 功能: 根据路径获取对应的字符串值
      • 返回值: 找到的字符串值
    • GetStringList(string location)
      • 功能: 根据路径获取对应的字符串列表
      • 返回值: 找到的字符串列表
    • WriteTo(string path)
      • 功能: 将修改后的 Yaml 内容写回文件
      • 参数:
        • path: 要写入的文件路径
  • 命名空间: COG.Utils

7. 自定义角色系统(Custom Role System)

7.1 注册自定义职业

CustomRole.Register<T>() 方法

  • 描述: 在模组初始化阶段调用,用于将一个实现了 CustomRole 抽象类的新职业注册到系统中。注册后,该职业可在游戏逻辑中被分配和使用
  • 泛型参数:
    • T: 继承自 CustomRole 的具体职业类
  • 命名空间: COG.Roles

7.2 获取玩家的自定义职业

PlayerControl.GetCustomRole() 方法

  • 描述: 扩展方法,返回当前玩家所拥有的 CustomRole 实例(如果已分配)
  • 返回值: CustomRole 或 null
  • 命名空间: COG.Roles

7.3 检查玩家是否拥有特定职业

PlayerControl.IsRole<T>() 方法

  • 描述: 判断当前玩家是否被分配了指定类型的自定义职业
  • 返回值: bool
  • 命名空间: COG.Roles

7.4 职业行为钩子(示例)

在 CustomRole 基类及其派生类中通常会重写以下虚方法以实现职业逻辑:

  • OnMeetingStart(): 会议开始时触发
  • OnMeetingEnd(): 会议结束时触发
  • OnKillAttempt(PlayerControl killer, PlayerControl target): 当此玩家尝试击杀或被击杀时触发
  • OnFixedUpdate(): 每帧固定更新(类似 Update,但用于物理/游戏逻辑)
  • OnTaskComplete(PlayerTask task): 玩家完成任务时触发

注意: 这些方法需由具体职业类实现,并通过 GameManager 或 RoleManager 在游戏事件中调用

8. 配置与本地化管理

8.1 加载配置文件

ConfigManager.LoadConfig(string fileName) 方法

  • 描述: 从 BepInEx/config/ 目录加载指定名称的 JSON 配置文件(如 "COG.json"),并反序列化为 PluginConfig 对象
  • 参数:
    • fileName: 配置文件名(不含路径)
  • 返回值: PluginConfig 实例
  • 命名空间: COG.Config

8.2 保存配置

ConfigManager.SaveConfig(PluginConfig config, string fileName) 方法

  • 描述: 将 PluginConfig 对象序列化并写回磁盘上的配置文件
  • 参数:
    • config: 要保存的配置对象
    • fileName: 目标文件名
  • 命名空间: COG.Config

8.3 获取本地化文本

Localization.GetString(string key) 方法

  • 描述: 根据当前语言设置,从嵌入资源中的多语言 Yaml 文件(如 zh-CN.yaml, en-US.yaml)获取对应的本地化字符串
  • 参数:
    • key: 本地化键名(例如 "role.detective.name")
  • 返回值: 本地化后的字符串,若未找到则返回键名本身
  • 命名空间: COG.Localization

9. RPC(远程过程调用)通信

所有 RPC 方法均通过 Among Us 的 SendRPC 机制实现,确保多人游戏中同步。

9.1 发送自定义 RPC

RpcUtils.SendCustomRpc(byte callId, Action<BinaryWriter> writer) 方法

  • 描述: 向服务器或其他客户端发送一条自定义的 RPC 消息
  • 参数:
    • callId: 自定义消息 ID(需全局唯一)
    • writer: 写入附加数据的委托
  • 命名空间: COG.Networking

9.2 处理接收到的 RPC

RpcUtils.OnReceiveCustomRpc 事件

  • 描述: 订阅此事件以处理接收到的自定义 RPC 消息。根据 callId 分发逻辑
  • 事件类型: Action<byte, BinaryReader>
  • 用法示例:
RpcUtils.OnReceiveCustomRpc += (callId, reader) => {
    if (callId == MyCustomRpcId) {
        // 处理自定义 RPC
    }
};
  • 命名空间: COG.Networking
  • 10. 游戏事件监听器

    10.1 注册游戏事件回调

    GameEvents.Subscribe<T>(Action<T> callback) 方法

    • 描述: 订阅特定类型的游戏事件(如 PlayerKilledEvent, MeetingStartedEvent)。当事件发生时,自动调用回调函数
    • 泛型参数:
      • T: 要订阅的事件类型,必须实现 IGameEvent 接口
    • 参数:
      • callback: 事件发生时要执行的回调函数
    • 命名空间: COG.Events

    10.2 触发自定义事件

    GameEvents.Trigger<T>(T event) 方法

    • 描述: 手动触发一个实现了 IGameEvent 接口的事件,通知所有订阅者
    • 参数:
      • event: 要触发的事件实例
    • 命名空间: COG.Events

    11. 性能与内存优化

    11.1 对象池管理器

    ObjectPool<T> 类

    • 描述: 泛型对象池,用于复用频繁创建/销毁的对象(如粒子效果、UI 元素),减少 GC 压力
    • 主要方法:
      • Get(): 从池中获取一个对象实例
      • Return(T obj): 将对象归还至池中以便复用
    • 命名空间: COG.Pooling

    11.2 协程管理器

    CoroutineManager

    • 描述: 统一管理所有协程的启动与停止,避免因 MonoBehaviour 销毁导致的协程泄漏
    • 主要方法:
      • Start(IEnumerator routine): 安全启动协程
      • StopAll(): 停止所有正在运行的协程
    • 命名空间: COG.Coroutines

    12. 调试与开发者工具

    12.1 开发者控制台命令

    DevConsole.RegisterCommand(string command, Action<string[]> handler) 方法

    • 描述: 注册一个可通过 F1 控制台输入的调试命令(仅在 Debug 模式下启用)
    • 参数:
      • command: 命令名称
      • handler: 命令处理函数,接收参数数组
    • 用法示例:
    DevConsole.RegisterCommand("killall", args => {
        foreach (var p in PlayerUtils.GetAllAlivePlayers()) 
            p.RpcMurderPlayer(p);
    });
  • 命名空间: COG.DevTools
  • 12.2 性能分析标记

    ProfilerUtils.BeginSample(string name) / EndSample() 方法

    • 描述: 在 Unity Profiler 中添加自定义性能采样区域,便于定位性能瓶颈
    • 用法示例:
    ProfilerUtils.BeginSample("MyExpensiveOperation");
    // 执行耗时操作
    ProfilerUtils.EndSample();
  • 命名空间: COG.Profiling
  • 三、监听系统

    1. 事件处理器属性 (EventHandlerAttribute)

    • 功能: 用于标记方法作为事件监听器
    • 特性:
      • 继承自 System.Attribute
      • 使用 [AttributeUsage(AttributeTargets.Method)] 限制只能用于方法
      • 指定事件处理类型(前置/后置)
    • 重要属性:
      • EventHandlerType: 事件处理类型枚举值

    2. 事件处理类型枚举 (EventHandlerType)

    • 取值:
      • Prefix: 方法体执行前触发
      • Postfix: 方法体执行后触发

    3. 监听器接口 (IListener)

    • 功能: 标记类为监听器的空接口
    • 特性:
      • 包含一个空的静态实例 EmptyListener
      • 作为类型标识使用

    4. 监听器管理器 (ListenerManager)

    • 功能: 统一管理所有监听器和事件处理器
    • 核心方法:
      • RegisterListener(IListener listener): 注册监听器实例
      • ExecuteHandlers(Event.Event @event, EventHandlerType type): 执行指定类型的事件处理器
      • AsHandlers(IListener listener): 将监听器转换为处理器数组
    • 特性:
      • 单例模式设计
      • 线程安全的处理器列表管理

    专用监听器实现

    1. 客户端选项监听器 (ClientOptionListener)

    • 功能: 处理游戏设置界面相关事件
    • 监听事件:
      • OptionsMenuBehaviourStartEvent: 设置菜单初始化
      • OptionsMenuBehaviourUpdateEvent: 设置菜单更新
    • 主要逻辑:
      • 创建模组专属选项标签页
      • 动态生成选项按钮和滑块
      • 多语言文本支持

    2. 命令监听器 (CommandListener)

    • 功能: 处理玩家聊天命令
    • 监听事件: LocalPlayerChatEvent(前缀处理)
    • 事件内含值:Text(符合条件的玩家信息)
    • 命令格式: 以"/"开头的聊天消息
    • 特性:
      • 支持命令别名系统
      • 主机权限验证
      • 参数解析和分发

    3. 自定义按钮监听器 (CustomButtonListener)

    • 功能: 管理自定义HUD按钮
    • 监听事件:
      • HudManagerStartEvent: HUD初始化
      • HudManagerUpdateEvent: HUD更新
      • HudManagerDestroyEvent: HUD销毁
    • 职责: 按钮状态管理和位置排列

    4. 自定义游戏结束逻辑监听器 (CustomGameEndLogicListener)

    • 功能: 实现自定义游戏结束条件
    • 监听事件:
      • GameStartEvent: 游戏开始初始化
      • GameCheckEndEvent: 游戏结束检查
      • AmongUsClientGameEndEvent: 客户端游戏结束
      • GameSetEverythingUpEvent: 结束界面设置
    • 特性:
      • 多阵营胜利条件支持
      • RPC数据同步机制
      • 自定义结算界面

    5. 游戏逻辑监听器 (GameListener)

    • 功能: 核心游戏事件处理
    • 监听事件:
      • PlayerFixedUpdateEvent: 玩家固定更新
      • VentCheckEvent: 通风口使用检查
      • HudManagerUpdateEvent: HUD更新
      • PlayerExileBeginEvent: 玩家被流放开始
      • GameStartEvent: 游戏开始
    • 职责: 角色能力限制、界面提示、游戏状态管理

    6. 原版Bug修复监听器 (VanillaBugFixListener)

    • 功能: 修复游戏原版Bug
    • 监听事件:
      • RoleManagerSelectRolesEvent: 角色选择
      • 流放结束事件
    • 修复问题: 大厅残留、游戏操作失效等

    7. 介绍镜头监听器 (IntroListener)

    • 功能: 自定义游戏开始介绍界面
    • 监听事件:
      • IntroCutsceneShowRoleEvent: 角色文本显示
      • IntroCutsceneBeginCrewmateEvent: 团队介绍开始
    • 特性: 自定义角色描述、阵营显示

    8. 大厅监听器 (LobbyListener)

    • 功能: 处理大厅相关逻辑
    • 监听事件:
      • GameStartManagerStartEvent: 游戏开始管理器启动
      • GameStartManagerMakePublicEvent: 设为公开游戏尝试
    • 特性: 界面元素禁用、消息提示

    9. 玩家监听器 (PlayerListener)

    • 功能: 玩家加入事件处理
    • 监听事件: PlayerControlAwakeEvent
    • 职责: 配置同步、网络通信

    10. 角色分配监听器 (RoleAssignmentListener)

    • 功能: 实现自定义角色分配算法
    • 监听事件: RoleManagerSelectRolesEvent(前缀处理)
    • 分配逻辑:
      • 按阵营优先级分配(内鬼→中立→船员)
      • 附加职业和子职业分配
      • 容错机制和回退方案

    11. RPC监听器 (RpcListener)

    • 功能: 处理自定义RPC消息
    • 监听事件: PlayerHandleRpcEvent
    • 支持RPC类型:
      • 选项同步、角色设置
      • 高级击杀、游戏事件记录
      • 理智值同步等

    12. 任务添加器监听器 (TaskAdderListener)

    • 功能: 自定义任务选择界面
    • 监听事件:
      • TaskAdderGameShowFolderEvent: 文件夹显示
      • TaskAddButtonUpdateEvent: 按钮更新
      • TaskAddButtonAddTaskEvent: 任务添加
    • 特性: 模组角色专属任务界面

    13. 版本显示监听器 (VersionShowerListener)

    • 功能: 自定义版本信息显示
    • 监听事件: PingTrackerUpdateEvent
    • 显示内容:
      • 模组版本和编译信息
      • 实时网络延迟和FPS
      • 服务器区域信息

    四、事件系统

    4.1 事件系统概述

    事件系统是COG模组的核心架构之一,用于处理游戏中的各种状态变化和交互行为。整个事件系统基于监听器模式实现,通过Event基类和IListener接口构成完整的事件处理框架。

    4.2 事件系统核心架构

    4.2.1 事件基类 (Event)

    所有事件类都继承自Event基类,该基类提供了统一的事件标识机制:

    • Name: 事件类型名称
    • Id: 事件唯一标识符(基于名称哈希)
    • GetSubClasses(): 获取所有事件子类的静态方法

    4.2.2 事件处理器类型 (EventHandlerType)

    定义了两种事件处理时机:

    • Prefix: 在原始方法执行前调用
    • Postfix: 在原始方法执行后调用

    4.2.3 事件处理器属性 (EventHandlerAttribute)

    用于标记监听器方法的特性,指定处理时机。

    4.2.4 事件处理器 (Handler)

    封装监听器实例的详细信息:

    • Listener: 监听器实例
    • Method: 处理方法
    • EventType: 事件类型
    • EventHandlerType: 处理时机类型

    4.3 事件分类详解

    4.3.1 游戏管理事件

    GameStartManager相关事件

    • GameStartManagerEvent: 游戏开始管理器事件基础类
    • GameStartManagerUpdateEvent: 游戏开始管理器更新事件
    • GameStartManagerStartEvent: 玩家进入大厅事件
    • GameStartManagerMakePublicEvent: 房间公开化事件
    • GameStartManagerBeginGameEvent: 游戏开始事件

    AmongUsClient相关事件

    • AmongUsClientEvent: 客户端事件基础类
    • AmongUsClientPlayerJoinEvent: 玩家加入事件
    • AmongUsClientLeaveEvent: 玩家离开事件
    • AmongUsClientJoinLobbyEvent: 加入房间事件
    • AmongUsClientGameEndEvent: 游戏结束事件
    • AmongUsClientCreatePlayerEvent: 创建玩家事件

    4.3.2 玩家相关事件

    玩家基础事件 (PlayerEvent)

    • 所有玩家相关事件的父类
    • 包含执行动作的玩家实例

    具体玩家事件

    • PlayerTaskFinishEvent: 玩家完成任务事件
    • PlayerShapeShiftEvent: 玩家变形事件
    • PlayerReportDeadBodyEvent: 玩家报告尸体事件
    • PlayerMurderEvent: 玩家击杀事件
    • PlayerHandleRpcEvent: 玩家RPC处理事件
    • PlayerFixedUpdateEvent: 玩家固定更新事件
    • PlayerExileBeginEvent: 玩家驱逐开始事件
    • PlayerExileEndEvent: 玩家驱逐结束事件
    • PlayerExileEndOnAirshipEvent: 飞船地图驱逐结束事件
    • PlayerCoSetTasksEvent: 玩家设置任务事件
    • PlayerControlAwakeEvent: 玩家控制觉醒事件
    • PlayerChatEvent: 玩家聊天事件
    • LocalPlayerChatEvent: 本地玩家聊天事件
    • PlayerAdjustLightingEvent: 玩家调整照明事件

    自定义职业事件

    • PlayerCustomRoleChangeEvent: 玩家自定义职业变更事件

    4.3.3 会议相关事件

    会议基础事件 (MeetingEvent)

    • 所有会议相关事件的父类
    • 包含会议界面实例

    具体会议事件

    • MeetingStartEvent: 会议开始事件
    • MeetingFixedUpdateEvent: 会议固定更新事件
    • MeetingCastVoteEvent: 会议投票事件
    • MeetingVotingCompleteEvent: 会议投票完成事件
    • MeetingCheckForEndVotingEvent: 会议检查结束投票事件
    • MeetingServerStartEvent: 服务器会议开始事件

    4.3.4 游戏逻辑事件

    游戏事件基础类

    • GameEvent: 泛型游戏事件基础类

    具体游戏事件

    • GameStartEvent: 游戏开始事件
    • GameCheckEndEvent: 游戏检查结束事件
    • GameCheckTaskCompletionEvent: 游戏检查任务完成事件
    • GameSetEverythingUpEvent: 游戏设置事件
    • GameShowSabotageMapEvent: 显示破坏地图事件
    • PingTrackerUpdateEvent: 延迟追踪更新事件
    • OptionsMenuBehaviourUpdateEvent: 选项菜单更新事件
    • OptionsMenuBehaviourStartEvent: 选项菜单开始事件

    游戏记录事件

    • StartGameEvent: 游戏开始记录事件
    • StartMeetingEvent: 开始会议记录事件
    • PlayerKillGameEvent: 玩家击杀记录事件
    • PlayerDieGameEvent: 玩家死亡记录事件
    • PlayerExileGameEvent: 玩家驱逐记录事件
    • PlayerReviveGameEvent: 玩家复活记录事件
    • PlayerDisconnectGameEvent: 玩家断开连接记录事件
    • SheriffMisfireGameEvent: 警长误杀记录事件
    • FinishTaskGameEvent: 完成任务记录事件
    • EnterVentGameEvent: 进入通风管记录事件
    • UseAbilityGameEvent: 使用能力记录事件

    4.3.5 UI相关事件

    HUD管理器事件

    • HudManagerEvent: HUD管理器事件基础类
    • HudManagerUpdateEvent: HUD更新事件
    • HudManagerStartEvent: HUD开始事件
    • HudManagerDestroyEvent: HUD销毁事件

    按钮相关事件

    • PassiveButtonEvent: 被动按钮事件基础类
    • PassiveButtonClickEvent: 被动按钮点击事件

    任务面板事件

    • TaskPanelBehaviourEvent: 任务面板行为事件
    • TaskPanelBehaviourSetTaskTextEvent: 设置任务文本事件

    任务添加相关事件

    • TaskAdderGameEvent: 任务添加游戏事件
    • TaskAdderGameShowFolderEvent: 显示任务文件夹事件
    • TaskAddButtonEvent: 任务添加按钮事件
    • TaskAddButtonUpdateEvent: 任务按钮更新事件
    • TaskAddButtonAddTaskEvent: 添加任务事件

    4.3.6 角色和物理相关事件

    角色管理事件

    • RoleManagerEvent: 角色管理器事件
    • RoleManagerSelectRolesEvent: 选择角色事件

    玩家物理事件

    • PlayerPhysicsEvent: 玩家物理事件
    • PlayerPhysicsCoSpawnEvent: 玩家协同生成事件

    4.3.7 地图元素事件

    通风管事件

    • VentEvent: 通风管事件基础类
    • VentCheckEvent: 通风管检查事件

    尸体事件

    • DeadBodyEvent: 尸体事件基础类
    • DeadBodyClickEvent: 尸体点击事件

    控制器事件

    • ControllerEvent: 控制器事件
    • ControllerManagerUpdateEvent: 控制器管理器更新事件

    4.3.8 过场动画事件

    介绍过场事件

    • IntroCutsceneEvent: 介绍过场事件基础类
    • IntroCutsceneShowRoleEvent: 显示角色事件
    • IntroCutsceneDestroyEvent: 过场销毁事件
    • IntroCutsceneCoBeginEvent: 过场开始事件
    • IntroCutsceneBeginImpostorEvent: 内鬼团队显示事件
    • IntroCutsceneBeginCrewmateEvent: 船员团队显示事件

    4.4 事件系统工作原理

    4.4.1 事件注册机制

    1. 通过[EventHandler]特性标记监听方法
    2. 系统扫描所有标记的方法并创建Handler实例
    3. 按事件类型和处理时机分类存储

    4.4.2 事件触发流程

    1. 游戏逻辑发生状态变化时触发对应事件
    2. 系统查找匹配的事件处理器
    3. 按照Prefix→原方法→Postfix的顺序执行处理器
    4. 处理器可以修改事件数据影响后续流程

    4.4.3 事件数据传递

    • 事件对象携带相关的游戏实体引用
    • 支持数据修改(如VentCheckEvent中的SetCanUse方法)
    • 保持事件数据的一致性和时效性

    4.5 事件系统特点

    4.5.1 层次化设计

    事件系统采用多层继承结构,基础事件类提供通用功能,具体事件类扩展特定功能。

    4.5.2 类型安全

    通过泛型和类型检查确保事件处理器与事件类型的匹配。

    4.5.3 灵活性

    支持前置和后置处理,允许在不同时机介入游戏逻辑。

    4.5.4 性能优化

    通过类型索引快速定位事件处理器,减少运行时反射调用。

    4.5.5 可扩展性

    易于添加新的事件类型和处理器,支持模组功能的动态扩展。

    事件系统为COG模组提供了强大的游戏逻辑拦截和修改能力,是实现各种自定义功能的基础架构。

    五、参考角色书写模式

    5.1 角色基类与分类

    COG模组中的角色系统基于CustomRole基类构建,所有自定义角色都必须继承此类。角色主要分为以下几类:

    • 基础角色(Base Role):如船员(Crewmate)、内鬼(Impostor)
    • 职业角色(Custom Role):如预言家(Seer)、清道夫(Cleaner)、小丑(Jester)
    • 附加身份(Addon):如疯子(Madman)
    • 子身份(SubRole):如死亡威慑(DeathDeterrence)
    • 未知角色(Unknown):用于占位或特殊用途

    5.2 基础角色实现模式

    5.2.1 船员角色 (Crewmate.cs)

    public class Crewmate : CustomRole
    {
        public Crewmate() : base(Palette.CrewmateBlue, CampType.Crewmate, false)
        {
            IsBaseRole = true;
        }
        
        public override IListener GetListener()
        {
            return IListener.EmptyListener;
        }
    }

    特点说明:

    • 继承CustomRole基类
    • 构造函数中设置颜色、阵营和是否为幽灵
    • 标记IsBaseRole = true表示为基础角色
    • 返回空监听器,不处理任何事件

    5.2.2 内鬼角色 (Impostor.cs)

    public class Impostor : CustomRole
    {
        public Impostor() : base(false)
        {
            CanKill = true;
            CanVent = true;
            IsBaseRole = true;
            CanSabotage = true;
            BaseRoleType = RoleTypes.Impostor;
        }
    }

    特点说明:

    • 通过布尔参数控制是否为幽灵状态
    • 显式设置内鬼能力(击杀、通风管、破坏)
    • 设置基础角色类型为RoleTypes.Impostor

    5.3 职业角色实现模式

    5.3.1 预言家角色 (Seer.cs)

    public class Seer : CustomRole
    {
        // 私有字段声明
        private CustomButton CheckButton { get; }
        private int AvailableUsageTimes { get; set; }
        private CustomOption Cooldown { get; }
        private CustomOption InitialAvailableUsableTimes { get; }
        private readonly Dictionary<byte, string> _prefixes = new();
        private PlayerControl? _current;
        private readonly HashSet<byte> _checkedPlayers = [];
        
        // 构造函数
        public Seer() : base(ColorUtils.FromColor32(30,144,255), CampType.Crewmate)
        {
            // 创建配置选项
            Cooldown = CreateOption(() => GetContextFromLanguage("role.crewmate.seer-check-cooldown"), 
                new FloatOptionValueRule(5, 1, 60, 25, NumberSuffixes.Seconds));
            InitialAvailableUsableTimes = CreateOption(() => GetContextFromLanguage("role.crewmate.seer-initial-available-usable-times"), 
                new FloatOptionValueRule(1, 1, 15, 1));
            
            // 创建自定义按钮
            var action = new LanguageConfig.TextHandler("action");
            CheckButton = CustomButton.Of(
                "seer-check",
                () => { /* 执行逻辑 */ },
                () => { }, // 重置逻辑
                () => { /* 条件判断 */ },
                () => true,
                ResourceUtils.LoadSprite(ResourceConstant.CleanDeadBodyButton),
                2,
                action.GetString("check"),
                () => Cooldown.GetFloat(),
                -1
            );
            AddButton(CheckButton);
        }
        
        // 事件处理器方法
        [OnlyLocalPlayerWithThisRoleInvokable]
        [EventHandler(EventHandlerType.Postfix)]
        public void OnPlayerMurder(PlayerMurderEvent @event) { }
        
        [OnlyLocalPlayerWithThisRoleInvokable]
        [EventHandler(EventHandlerType.Postfix)]
        public void OnPlayerRoleChange(PlayerCustomRoleChangeEvent @event) { }
        
        [OnlyLocalPlayerWithThisRoleInvokable]
        [EventHandler(EventHandlerType.Postfix)]
        public void OnGameStart(GameStartEvent _) { }
        
        // 清理方法
        public override void ClearRoleGameData()
        {
            _checkedPlayers.Clear();
            _prefixes.Clear();
        }
    }

    特点说明:

    • 完整的角色生命周期管理
    • 支持配置选项创建和管理
    • 实现自定义按钮功能
    • 使用事件监听器处理游戏事件
    • 提供数据清理方法
    • 使用特性标记限制调用条件

    5.3.2 清道夫角色 (Cleaner.cs)

    public class Cleaner : CustomRole, IListener
    {
        private DeadBody? _body;
        
        public Cleaner()
        {
            BaseRoleType = RoleTypes.Impostor;
            CanKill = true;
            CanVent = true;
            CanSabotage = true;
            
            // 创建配置选项和按钮
            CleanBodyCd = CreateOption(/* ... */);
            CleanBodyButton = CustomButton.Of(/* ... */);
            AddButton(CleanBodyButton);
        }
        
        private CustomOption CleanBodyCd { get; }
        private CustomButton CleanBodyButton { get; }
        
        public override IListener GetListener()
        {
            return this;
        }
    }

    特点说明:

    • 同时继承CustomRole并实现IListener接口
    • 在构造函数中直接设置基础角色属性
    • 通过GetListener()方法返回自身作为监听器

    5.3.3 小丑角色 (Jester.cs)

    public class Jester : CustomRole, IListener, IWinnable
    {
        private readonly CustomOption _allowReportDeadBody;
        private readonly CustomOption _allowStartMeeting;
        
        public Jester() : base(Color.magenta, CampType.Neutral)
        {
            _allowStartMeeting = CreateOption(/* ... */);
            _allowReportDeadBody = CreateOption(/* ... */);
        }
        
        // 胜利条件检查
        public void CheckWin(WinnableData data) { }
        public uint GetWeight() { return IWinnable.GetOrder(5); }
        
        // 事件处理器
        [EventHandler(EventHandlerType.Prefix)]
        public bool OnHostCheckStartMeeting(PlayerReportDeadBodyEvent @event) { }
        
        public override IListener GetListener()
        {
            return this;
        }
    }

    特点说明:

    • 实现多个接口:IListener和IWinnable
    • 支持自定义胜利条件
    • 使用前缀事件处理器修改游戏行为
    • 中立阵营角色的典型实现

    5.4 特殊身份实现模式

    5.4.1 附加身份 - 疯子 (Madman.cs)

    public class Madman : CustomRole, IListener
    {
        public Madman() : base(Color.grey, ModiType.Addon, OnlyOnRoleType.Global)
        {
        }
        
        // 使用Harmony补丁修改游戏文本
        [HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), new Type[] { typeof(StringNames), typeof(Il2CppReferenceArray) })]
        class ExileControllerMessagePatch
        {
            static void Postfix(ref string __result)
            {
                if (ExileController.Instance != null && ExileController.Instance.initData.networkedPlayer != null)
                {
                    __result = "Madman";
                }
            }
        }
    }

    特点说明:

    • 使用ModiType.Addon标识为附加身份
    • 使用Harmony补丁直接修改游戏原生方法
    • 不需要复杂的事件监听,专注于特定功能

    5.4.2 子身份 - 死亡威慑 (DeathDeterrence.cs)

    public class DeathDeterrence : CustomRole, IListener
    {
        public DeathDeterrence() : base(Color.green, ModiType.SubRole, OnlyOnRoleType.Global)
        {
            DeterrenceCooldown = CreateOption(/* ... */);
            DeterrenceButton = CustomButton.Of(/* ... */);
            AddButton(DeterrenceButton);
        }
        
        private CustomOption DeterrenceCooldown { get; }
        private CustomButton DeterrenceButton { get; }
    }

    特点说明:

    • 使用ModiType.SubRole标识为子身份
    • 可以独立拥有技能按钮和冷却时间
    • 通常与其他主职业配合使用

    5.4.3 未知角色 (Unknown.cs)

    public class Unknown : CustomRole
    {
        public Unknown() : base(Color.white, CampType.Unknown)
        {
            BaseRoleType = RoleTypes.CrewmateGhost;
        }
        
        public override IListener GetListener()
        {
            return IListener.EmptyListener;
        }
    }

    特点说明:

    • 用于特殊场景或占位
    • 设置为幽灵状态
    • 返回空监听器

    5.5 角色开发最佳实践

    5.5.1 构造函数规范

    • 必须调用基类构造函数
    • 参数顺序:颜色、阵营类型、其他可选参数
    • 在构造函数中完成所有初始化工作

    5.5.2 字段声明规范

    • 私有字段使用下划线前缀(如_checkedPlayers)
    • 属性使用PascalCase命名(如CheckButton)
    • 配置选项和按钮应声明为只读属性

    5.5.3 事件处理规范

    • 使用[EventHandler]特性标记事件处理器
    • 根据需要选择Prefix或Postfix时机
    • 对于仅本地玩家相关的事件,添加[OnlyLocalPlayerWithThisRoleInvokable]特性

    5.5.4 资源管理规范

    • 实现ClearRoleGameData()方法清理临时数据
    • 避免内存泄漏,及时清理集合和引用
    • 按钮和选项应在构造函数中创建并注册

    5.5.5 接口实现规范

    • 需要事件监听的角色实现IListener接口
    • 需要自定义胜利条件的角色实现IWinnable接口
    • 通过GetListener()方法返回适当的监听器实例

    5.6 角色类型选择指南

    角色类型 适用场景 继承方式 典型示例
    基础角色 游戏默认角色 CustomRole Crewmate, Impostor
    职业角色 主要游戏职业 CustomRole, IListener Seer, Cleaner, Jester
    附加身份 额外身份效果 CustomRole, IListener Madman
    子身份 依附于主职业的技能 CustomRole, IListener DeathDeterrence
    未知角色 特殊用途占位 CustomRole Unknown

    注意:编写职业时,不需要声明他们的Name,ShortName以及Description,他们是根据语言系统自动获取的

    这种分层的角色设计模式确保了代码的可维护性和扩展性,开发者可以根据具体需求选择合适的实现方式。

    六、RPC(远程过程调用)系统

    6.1 RPC系统概述

    RPC系统是COG模组中用于实现多玩家同步的核心通信机制。该系统基于Among Us原生的RPC机制进行扩展,支持自定义RPC消息的发送、接收和处理。

    6.2 核心枚举定义

    6.2.1 已知RPC类型枚举 (KnownRpc)

    • 位置: COG.Rpc.KnownRpc
    • 功能: 定义所有自定义RPC消息的唯一标识符
    • 枚举值:
    public enum KnownRpc : uint
    {
        ShareRoles = 100,                    // 分享角色信息
        HideDeadBody,                        // 隐藏尸体
        UpdateOption,                        // 更新选项设置
        SetRole,                             // 设置角色
        Feedback,                            // 反馈信息
        Revive,                              // 复活玩家
        NotifySettingChange,                 // 通知设置变更
        Mark,                                // 标记玩家
        ShareOptions,                        // 分享选项配置
        ClearSabotages,                      // 清除破坏
        ShareWinners,                        // 分享胜利者
        KillWithoutDeadBody,                 // 无尸体击杀
        ShareAbilityOrVentUseForInspector,   // 分享能力/通风管使用
        SyncRoleGameData,                    // 同步角色游戏数据
        DieWithoutAnimationAndBody,          // 无动画死亡
        SyncGameEvent,                       // 同步游戏事件
        AdvancedMurder,                      // 高级击杀
        SetSanity,                           // 设置理智值
        PlayKillAnimation                    // 播放击杀动画
    }

    6.3 RPC处理器接口与实现

    6.3.1 RPC处理器接口 (IRpcHandler)

    • 位置: COG.Rpc.IRpcHandler
    • 功能: 所有RPC处理器的基接口
    • 主要属性:
      • Handlers: 静态处理器集合,存储所有已注册的处理器
      • OnReceive: 接收RPC消息时的回调委托
    • 核心方法:
      • Register(IRpcHandler handler): 注册处理器到全局集合

    6.3.2 基础RPC处理器 (RpcHandler)

    • 位置: COG.Rpc.RpcHandler
    • 功能: 处理无参数的RPC消息
    • 构造函数:
      public RpcHandler(byte callId, Action onPerform)
      public RpcHandler(KnownRpc callId, Action onPerform)
      public RpcHandler(RpcCalls callId, Action onPerform)
    • 主要方法:
      • Perform(): 本地执行RPC逻辑
      • Send(): 发送RPC消息到网络
      • PerformAndSend(): 执行并发送(组合操作)

    6.3.3 泛型RPC处理器

    系统提供了一系列泛型RPC处理器,支持1到5个参数的消息处理:

    • 处理器类型:
      • RpcHandler<T>: 单参数处理器
      • RpcHandler<T1, T2>: 双参数处理器
      • RpcHandler<T1, T2, T3>: 三参数处理器
      • RpcHandler<T1, T2, T3, T4>: 四参数处理器
      • RpcHandler<T1, T2, T3, T4, T5>: 五参数处理器
    • 通用模式:
      • 每个泛型处理器都包含对应的构造函数重载
      • Perform(T args): 带参数执行
      • Send(T args): 带参数发送
      • PerformAndSend(T args): 带参数执行并发送

    6.4 RPC消息写入器 (RpcWriter)

    6.4.1 消息写入方法

    基础类型写入:

    • Write(bool value): 写入布尔值
    • Write(byte value): 写入字节
    • Write(int value): 写入整数
    • Write(float value): 写入浮点数
    • Write(string value): 写入字符串

    高级类型写入:

    • WritePacked(int value): 写入压缩整数
    • WriteBytesAndSize(byte[] bytes): 写入字节数组(带长度)
    • WriteNetObject(InnerNetObject obj): 写入网络对象
    • WriteVector2(Vector2 vec): 写入二维向量

    6.4.2 消息发送控制

    启动方法:

    // 多种重载形式
    public static RpcWriter Start(byte callId, PlayerControl[]? targets = null)
    public static RpcWriter Start(KnownRpc callId, PlayerControl[]? targets = null)
    public static RpcWriter Start(PlayerControl playerControl, KnownRpc callId, PlayerControl[]? targets = null)

    完成发送:

    • Finish(): 完成消息构建并立即发送

    快捷方法:

    • StartAndSend(): 启动并立即发送的便捷方法

    6.5 RPC监听器 (RpcListener)

    6.5.1 核心处理方法

    方法: AfterRpcReceived(PlayerHandleRpcEvent @event)

    处理逻辑:

    1. 读取RPC调用ID并转换为KnownRpc枚举
    2. 根据不同的RPC类型执行相应的处理逻辑
    3. 调用注册的IRpcHandler处理器
    4. 通知所有自定义角色处理RPC消息

    6.5.2 具体RPC处理逻辑

    选项同步相关:

    • UpdateOption: 更新单个选项设置
    • ShareOptions: 批量同步所有选项配置

    角色管理相关:

    • SetRole: 为玩家设置自定义角色
    • SyncRoleGameData: 同步角色特定的游戏数据

    游戏逻辑相关:

    • KillWithoutDeadBody: 执行无尸体击杀
    • Revive: 复活玩家
    • HideDeadBody: 隐藏尸体对象
    • AdvancedMurder: 处理高级击杀选项

    玩家标记系统:

    • Mark: 为玩家添加或删除标记标签

    游戏事件同步:

    • SyncGameEvent: 同步自定义游戏事件记录

    6.6 RPC工具类 (RpcUtils)

    6.6.1 扩展方法

    RPC发送快捷方法:

    // 为PlayerControl添加的扩展方法
    public static RpcWriter StartRpcImmediately(this PlayerControl playerControl, KnownRpc callId, PlayerControl[]? targets = null)
    public static RpcWriter StartRpcImmediately(this PlayerControl playerControl, RpcCalls callId, PlayerControl[]? targets = null)

    消息读取工具:

    • ReadVector<T>(this MessageReader reader): 从消息读取器读取Vector2
    • ReadPlayerControl(this MessageReader reader): 读取PlayerControl对象

    6.7 RPC处理补丁 (RPCHandlerPatch)

    6.7.1 补丁方法

    位置: COG.Patch.RPCHandlerPatch

    功能: 使用Harmony补丁拦截游戏原生的RPC处理

    前缀处理 (Prefix):

    • 在原始RPC处理前执行
    • 触发PlayerHandleRpcEvent的前置处理器
    • 返回布尔值控制是否继续执行原始方法

    后缀处理 (Postfix):

    • 在原始RPC处理后执行
    • 记录RPC接收日志
    • 触发PlayerHandleRpcEvent的后置处理器

    6.8 RPC系统特点

    6.8.1 类型安全

    • 使用强类型枚举定义RPC消息
    • 泛型处理器确保参数类型匹配
    • 编译时类型检查减少运行时错误

    6.8.2 灵活扩展

    • 支持1-5个参数的泛型处理器
    • 易于添加新的RPC类型
    • 模块化的处理器注册机制

    6.8.3 性能优化

    • 消息压缩支持减少网络流量
    • 批量操作减少RPC调用次数
    • 目标选择机制优化网络传输

    6.8.4 调试支持

    • 详细的RPC发送/接收日志
    • 目标玩家列表可视化
    • 错误处理和异常捕获

    RPC系统为COG模组提供了可靠的多玩家数据同步能力,是实现复杂游戏逻辑和角色能力的基础通信框架。

    七、补充说明

    7.1 协程IEnumerator

    7.1.1 说明

    协程时Unity的重要任务类型,它可以使代码有时限的运行。

    7.1.2 使用方法

    运行IEnumerator方法时,请使用Coroutines.Start(你的协程方法());,并且引用Reactor.Utilities与UnityEngine命名空间。