公共 API
公共 API 随 Indie、Pro、Source 和 Studio 版提供(Starter 版的 API 是封闭的);完整的 C# 源码只包含在 Source 版和 Studio 版中。本指南介绍稳定的公共 API 范围,你可以用它从自己的 C# 脚本中与 MCE 交互并扩展它,而无需把游戏玩法代码耦合到内部实现。
稳定性是对一个名称的承诺。其余一切都可能在版本之间变化。
SDK 命名空间
MCE 通过 OpenMon.MCE.SDK 命名空间公开其公共 API。其中包含简洁的接口,让你无需把代码耦合到内部实现,就能访问引擎的各个子系统。
IMonsterCaptureEngine
主入口点。所有其他接口都通过它访问:
using OpenMon.MCE.SDK;
public class MyGameManager : MonoBehaviour
{
[Inject] private IMonsterCaptureEngine engine;
private void Start()
{
Debug.Log($"MCE Version: {engine.Version}");
Debug.Log($"Monsters in DB: {engine.Database.MonsterCount}");
Debug.Log($"Player name: {engine.Player.PlayerName}");
}
}
属性:
| 属性 | 类型 | 说明 |
|---|---|---|
Database | IMonsterDatabase | 对怪物/招式/道具数据库的只读访问 |
Battle | IBattleSystem | 战斗状态和事件 |
Player | IPlayerData | 玩家的队伍、金钱、名称、徽章 |
SaveSystem | ISaveSystem | 保存和加载操作 |
Version | string | MCE 引擎版本字符串 |
IMonsterDatabase
查询怪物、招式、特性、道具和属性:
IMonsterDatabase db = engine.Database;
// Get a monster by dex number (National Dex; the parameter is a uint)
MonsterEntry flameleon = db.GetMonster(152u);
// Get a monster by name
MonsterEntry sparkbit = db.GetMonster("Sparkbit");
// Get all monsters
IReadOnlyList<MonsterEntry> allMonsters = db.GetAllMonsters();
// Get a move
Move thunderbolt = db.GetMove("Thunderbolt");
// Get all moves
IReadOnlyList<Move> allMoves = db.GetAllMoves();
// Get type effectiveness. Types are ScriptableObjects fetched from the database
// (there is no MonsterType enum), so look them up by name first:
MonsterType electric = db.GetAllTypes().FirstOrDefault(t => t.name == "Electric");
MonsterType water = db.GetAllTypes().FirstOrDefault(t => t.name == "Water");
float effectiveness = db.GetTypeEffectiveness(electric, water);
// Returns 2.0 (super effective)
// Get all types
IReadOnlyList<MonsterType> types = db.GetAllTypes();
// Get total count
int count = db.MonsterCount;
IBattleSystem
用于战斗状态和事件的 SDK 契约:
IBattleSystem battle = engine.Battle;
// Is a battle in progress?
if (battle.IsInBattle) { /* ... */ }
// Subscribe to battle events
battle.OnBattleStarted += HandleBattleStarted;
battle.OnBattleEnded += HandleBattleEnded; // bool playerWon
IsInBattle、OnBattleStarted 和 OnBattleEnded 由引擎钩子(BattleSystemFacade.NotifyBattleStarted / NotifyBattleEnded)提供数据,而当前的引擎并不调用这些钩子,因此它们一直处于空闲状态。请改为通过战斗启动器观察战斗 —— 参见模式 1 以及 IBattleSystem 中的说明。
IPlayerData
访问玩家的当前状态:
IPlayerData player = engine.Player;
// Player's monster team
Roster roster = player.PlayerRoster;
int teamSize = roster.RosterSize;
MonsterInstance firstMonster = roster[0];
// Player's money, as of the last save (the live balance is in the bag)
int money = player.Money;
// The name of the player's CharacterData asset
string name = player.PlayerName;
// Player's badge count
int badges = player.BadgeCount;
ISaveSystem
用于保存和加载的 SDK 契约:
ISaveSystem saves = engine.SaveSystem;
saves.Save(1);
saves.Load(1);
bool hasData = saves.HasSaveData(1);
saves.DeleteSave(1);
saves.OnSaved += (slot) => Debug.Log($"Saved to slot {slot}");
saves.OnLoaded += (slot) => Debug.Log($"Loaded from slot {slot}");
依赖注入
MCE 使用 Zenject(Extenject)进行依赖注入。要在你的脚本中访问 SDK 接口,请使用 [Inject] 属性:
using Zenject;
using OpenMon.MCE.SDK;
public class MyCustomFeature : MonoBehaviour
{
// IMonsterCaptureEngine is the only SDK interface bound in DI
// (see SDKInstaller). Reach the subsystems through its properties.
[Inject] private IMonsterCaptureEngine engine;
private IMonsterDatabase database;
private IBattleSystem battle;
private void Awake()
{
// engine is injected before Awake/Start runs
database = engine.Database;
battle = engine.Battle;
}
}
如果你是依赖注入的新手,简单版本是这样的:在一个私有的 IMonsterCaptureEngine 字段上方加上 [Inject],Zenject 会自动为它赋值。你不需要调用 GetComponent 或 FindObjectOfType。该字段会在 Start() 运行之前被填充,其他 SDK 接口都挂在它上面(engine.Database、engine.Battle、engine.Player、engine.SaveSystem)。
如果你的脚本不是由 Zenject 管理的 MonoBehaviour,可以手动解析:
var engine = ProjectContext.Instance.Container.Resolve<IMonsterCaptureEngine>();
可在 Inspector 中配置的选项
MCE 的许多系统无需代码,就通过 Unity Inspector 公开配置:
ScriptableObject 配置
| 资产 | 用途 |
|---|---|
MCESettings | 全游戏设置:每种类型的遭遇几率(TileEncounterChance)、野生闪光几率、经验规则、一个游戏日的长度、情绪气泡 |
BattleConfigurationFile | 面向玩家的战斗设置:战斗风格和动画加速 |
BattleLauncher | 战斗场景、野生与训练师战斗音乐,以及默认战斗 AI |
MonsterEntry | 怪物物种数据 |
Move | 招式定义(每个招式一个具体类) |
WildEncountersSet | 遭遇表,从每张地图的 SceneInfo 资产链接 |
BattleAI | AI 策略,通过其 Fallback 串联 |
组件配置
大多数 MCE 组件都在 Inspector 中公开关键设置:
- MCECharacterController / PlayerCharacter:每一步的速度(秒每图块)、要显示的角色或怪物。
- EncounterTile:图块的遭遇类型。
- SceneInfo(及其
SceneInfoAsset):地图名称、音乐、地区、天气、遭遇。 - Actor 及其子类:CommandGraph 槽位(启用时、循环、交互、按需)。
事件与回调
MCE 的组件不使用 UnityEvent 字段;它触发你可以在脚本中订阅的普通 C# 事件和回调。最有用的几个:
| 位置 | 事件 | 触发时机 |
|---|---|---|
IBattleLauncher | SubscribeToBattleLaunched、SubscribeToBattleToUnload、SubscribeToBattleEnded(Action<BattleResultParameters>) | 战斗场景被启动、即将卸载,或战斗结束 |
IBattleSystem | OnBattleStarted、OnBattleEnded(bool playerWon) | 保留:当引擎调用其钩子时,SDK 外观层会触发它们(尚未连接) |
Roster | OnRosterUpdated | 队伍的怪物发生变化 |
QuestManager | OnQuestStarted、OnObjectiveProgressed、OnObjectiveChanged、OnQuestCompleted、OnQuestFailed、OnRewardsGranted | 对应的任务事件 |
PlayerEmotes(静态) | Played(string balloonName) | 本地玩家显示了情绪气泡 |
要从图中运行你自己的 C#,请编写一个自定义节点(下一节)。
无需源码扩展 MCE
模式 1:事件监听器
创建一个监听 MCE 事件并触发你自己逻辑的 MonoBehaviour。战斗启动器在 DI 中绑定,并在战斗结束时发出通知:
using OpenMon.MCE.Battle;
public class AchievementTracker : MonoBehaviour
{
[Inject] private IBattleLauncher battleLauncher;
private int battlesWon;
private void OnEnable() => battleLauncher.SubscribeToBattleEnded(OnBattleEnded);
private void OnDisable() => battleLauncher.UnsubscribeFromBattleEnded(OnBattleEnded);
private void OnBattleEnded(BattleResultParameters result)
{
if (!result.PlayerWon) return;
battlesWon++;
if (battlesWon >= 100)
UnlockAchievement("Battle Master");
}
}
模式 2:数据库查询
使用数据库 API 构建自定义功能:
public class TypeMatchupHelper : MonoBehaviour
{
[Inject] private IMonsterCaptureEngine engine;
private IMonsterDatabase database;
private void Awake()
{
database = engine.Database;
}
public List<MonsterType> GetWeaknesses(MonsterType type)
{
var weaknesses = new List<MonsterType>();
foreach (var attackType in database.GetAllTypes())
{
if (database.GetTypeEffectiveness(attackType, type) > 1f)
weaknesses.Add(attackType);
}
return weaknesses;
}
}
模式 3:存档集成
把你自己的标志和计数器作为游戏变量存储在 GlobalGameData(CommandGraph 使用的同一个存储)上,从而保留在玩家的存档中。它们会与其他所有内容一起被保存、加载和重置:
using OpenMon.MCE.Player;
// The marker interface IPlayerDataReceiver is what makes the player's data
// (GlobalGameData, Roster, CharacterData...) injectable into a class.
public class AchievementCounters : MonoBehaviour, IPlayerDataReceiver
{
[Inject] private GlobalGameData globalGameData;
public void CountBattleWon()
{
int won = globalGameData.GetVariable<int>("battlesWon");
globalGameData.SetVariable("battlesWon", won + 1);
}
}
该变量必须存在于持有者上(在 GlobalGameData 资产的游戏变量中定义它)。参见存档系统指南。
模式 4:自定义 CommandGraph 节点
要从可视化脚本中触发你自己的 C#,请编写一个节点。节点是位于你自己的程序集中的一个小型 [Serializable] 类,派生自 SingleExecutionPathCommandNode;编辑器会把它列在图的 Create Command 菜单中,位于其命名空间最后一段之下:
using System;
using System.Collections;
using OpenMon.MCE.Actors.CommandGraph;
namespace MyGame.Events
{
[Serializable]
public class AwardAchievement : SingleExecutionPathCommandNode
{
[UnityEngine.SerializeField] private string AchievementId;
protected override IEnumerator Run(CommandParameterData parameterData,
Action<CommandParameterData> callback)
{
MyAchievements.Unlock(AchievementId);
yield break;
}
}
}
随后该节点会显示为 Create Command / Events / AwardAchievement。节点可以读取的上下文(CommandParameterData),参见 CommandGraph API。
没有源码时你做不到的事
| 限制 | 变通方法 |
|---|---|
| 修改伤害公式本身 | 通过特性、道具、状态和招式(ScriptableObject)添加系数; 公式属于 Source 版和 Studio 版 |
| 添加新的战斗模块 | 模块是引擎组件(Source 版和 Studio 版);通过 IBattleSystem 事件做出反应 |
| 修改移动物理 | 调整步速以及 TileData 中的图块类型 |
| 修改战斗循环的排序方式 | Source 版和 Studio 版 |
| 修改界面布局 | 覆盖预制体(如果已公开),或在其上叠加你自己的界面 |
如果你觉得公共 API 对你的项目限制太多,请考虑升级到 Source 版。它包含完整的 C# 源码和架构文档,可用于深度定制。
程序集引用
要在你的脚本中引用 MCE 类型,你的程序集定义(.asmdef)必须引用:
{
"references": [
"OpenMon.MCE.Runtime"
]
}
如果你没有使用程序集定义,MCE 类型可通过 DLL 全局使用。
最佳实践
- 依赖接口,而不是实现。使用
IMonsterCaptureEngine,而不是具体类。 - 取消订阅事件。始终在
OnDisable或OnDestroy中移除事件处理器,以防内存泄漏。 - 不要缓存过时数据。每次需要当前数据时都查询 SDK,尤其是
IPlayerData。 - 使用 Zenject 注入。避免使用
FindObjectOfType或单例来访问 MCE。 - 查看 API 参考。SDK 接口的每个成员都有 XML 文档。你的 IDE 会显示提示。