跳到主要内容

公共 API

公共 API 随 Indie、Pro、Source 和 Studio 版提供(Starter 版的 API 是封闭的);完整的 C# 源码只包含在 Source 版和 Studio 版中。本指南介绍稳定的公共 API 范围,你可以用它从自己的 C# 脚本中与 MCE 交互并扩展它,而无需把游戏玩法代码耦合到内部实现。

公共 API

稳定性是对一个名称的承诺。其余一切都可能在版本之间变化。

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}");
}
}

属性:

属性类型说明
DatabaseIMonsterDatabase对怪物/招式/道具数据库的只读访问
BattleIBattleSystem战斗状态和事件
PlayerIPlayerData玩家的队伍、金钱、名称、徽章
SaveSystemISaveSystem保存和加载操作
VersionstringMCE 引擎版本字符串

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}");
尚未连接到引擎的存档

在当前的引擎中,engine.SaveSystem 没有连接到 SavegameManager:Save 和 Load 只会发出没有任何东西处理的请求,事件也永远不会触发。引擎保存的是具名文件夹(参见存档系统指南)。要把你自己的标志和计数器随玩家存档一起持久化,请使用 GlobalGameData 上的游戏变量(参见模式 3)。

依赖注入​

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;
}
}
没有 Zenject 经验?

如果你是依赖注入的新手,简单版本是这样的:在一个私有的 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 资产链接
BattleAIAI 策略,通过其 Fallback 串联

组件配置​

大多数 MCE 组件都在 Inspector 中公开关键设置:

  • MCECharacterController / PlayerCharacter:每一步的速度(秒每图块)、要显示的角色或怪物。
  • EncounterTile:图块的遭遇类型。
  • SceneInfo(及其 SceneInfoAsset):地图名称、音乐、地区、天气、遭遇。
  • Actor 及其子类:CommandGraph 槽位(启用时、循环、交互、按需)。

事件与回调​

MCE 的组件不使用 UnityEvent 字段;它触发你可以在脚本中订阅的普通 C# 事件和回调。最有用的几个:

位置事件触发时机
IBattleLauncherSubscribeToBattleLaunched、SubscribeToBattleToUnload、SubscribeToBattleEnded(Action<BattleResultParameters>)战斗场景被启动、即将卸载,或战斗结束
IBattleSystemOnBattleStarted、OnBattleEnded(bool playerWon)保留:当引擎调用其钩子时,SDK 外观层会触发它们(尚未连接)
RosterOnRosterUpdated队伍的怪物发生变化
QuestManagerOnQuestStarted、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 全局使用。

最佳实践​

  1. 依赖接口,而不是实现。使用 IMonsterCaptureEngine,而不是具体类。
  2. 取消订阅事件。始终在 OnDisable 或 OnDestroy 中移除事件处理器,以防内存泄漏。
  3. 不要缓存过时数据。每次需要当前数据时都查询 SDK,尤其是 IPlayerData。
  4. 使用 Zenject 注入。避免使用 FindObjectOfType 或单例来访问 MCE。
  5. 查看 API 参考。SDK 接口的每个成员都有 XML 文档。你的 IDE 会显示提示。