跳到主要内容

设置你的第一场战斗

本指南向你展示如何配置遭遇区域、触发野生战斗并理解战斗流程。完成后,你将拥有一场可以在编辑器中测试的可运行战斗。

最终效果

这就是你要构建的目标:一场从草地图块触发的野生遭遇,带有 四个操作的菜单,以及由遭遇表选出的对手。

前提条件​

  • 已安装 MCE 并完成设置向导。
  • 你的数据库中至少有一只怪物(演示项目的或你自己创建的)。
  • 你有一个带有玩家角色和基于网格的瓦片地图的场景,以及该地图的 SceneInfo 资产。

步骤 1:创建遭遇区域​

野生战斗由瓦片地图上的遭遇图块触发。遭遇图块是预制体带有 EncounterTile 组件的普通图块;该组件只有一项设置 Encounter Type,用于指定图块触发哪种遭遇:Grass、Water、Surfing、Cave、FishingOldRod、FishingGoodRod、FishingSuperRod、Diving、RockSmash、HeadButt、BugContest 或 Other。

  1. 使用遭遇图块预制体 -- 引擎附带现成的,例如 Assets/OpenMon/Game/Prefabs/World/Water/Water/WaterEncounterTile.prefab -- 或自己制作:创建一个预制体,添加 EncounterTile 组件并选择它的 Encounter Type。
  2. 让该预制体成为一个图块资产的 GameObject,并把图块绘制在应当出现野生怪物的瓦片地图图层上(通常是你的 “Grass” 图层)。

玩家每走上这样的图块一步,都会按遭遇几率进行判定。在 SceneInfo 被标记为地下城的场景中,每走过的一个图块都算作 Cave 遭遇,无需绘制图块。

步骤 2:配置遭遇表​

草丛里的内容是一个 WildEncountersSet ScriptableObject,而地图的 SceneInfo 决定哪个集合对应哪种遭遇类型。创建该集合:

  1. Assets > Create > MCE > Battle > Encounters > WildEncountersSet(或 Hub 的 Database 选项卡中的 New Wild Encounter Set 按钮)。
  2. 给它起一个描述性的名称,比如 Route1_GrassEncounters。
  3. 填写它的三个列表:
列表含义
Base Encounters总会出现的怪物
Encounters Per Moment某个时段额外出现的怪物:Night、Dawn、Day 或 Dusk
Encounters Per Weather在某种战斗外天气下额外出现的怪物

每个条目是一个 WildEncounter:

字段说明
Monster可以出现的 MonsterEntry
Form Calculator选择形态的资产(SingleFormEncounter 总是返回一种形态)
Min Level / Max Level野生怪物的等级范围(1-100;最大值不能低于最小值)
Weight该条目的相对几率(越高越常见)

例如:

怪物等级范围权重
Flameleon3-540
Leaflet3-540
Sparkit4-620
  1. 打开你地图的 SceneInfo 资产,在它的 Encounters 组中,为遭遇类型(例如 Grass)添加条目并分配你的 WildEncountersSet。没有该条目,地图就没有该类型的野生怪物。

步骤 3:设置战斗场景​

MCE 使用一个专用的战斗场景,在战斗开始时以叠加方式加载到世界场景之上。你需要:

  1. 一个 BattleLauncher 资产。它是一个 ScriptableObject(Create > MCE > Battle > Launcher),通过 BattleLauncherInstaller 绑定,而不是你场景的组件。它的 Battle Scene 字段指向战斗场景,并保存战斗音乐列表。项目设置中已经包含一个。
  2. 构建设置中的战斗场景。默认的那个(Battle.unity,位于 Assets/OpenMon/Core/Game/Scenes/)随 MCE 附带。

BattleLauncher 负责过渡:

  • 构建一个 BattleParameters 对象(战斗类型、敌人类型、队伍、角色、场景设定、天气)。
  • 以叠加方式加载战斗场景。
  • 把参数交给该场景中的 BattleManager。
  • 战斗结束时卸载场景并返回世界。

BattleManager 组件​

BattleManager 是战斗系统的核心。在战斗场景中,你会找到一个带有 BattleManager 组件的 GameObject。它旁边需要十五个模块,每个都是同一 GameObject 上的 MonoBehaviour,外加 BattleStateMachine:

模块用途
BattleManagerBattlersModule双方战斗者的查找、精灵、形态、逃跑
BattleManagerHealthModuleHP 变化、濒死检测与处理
BattleManagerMovesModule招式执行、命中
BattleManagerItemsModule战斗中的道具使用
BattleManagerCaptureModule野生怪物的捕捉机制
BattleManagerAIModule敌方的决策
BattleManagerAnimationModule战斗开场与流程动画
BattleManagerAudioModule战斗音乐与音效
BattleManagerStatusesModule异常状态(灼伤、中毒、睡眠等)
BattleManagerBattlerStatsModule能力等级及其变化
BattleManagerBattlerSwitchModule战斗者的上场与下场
BattleManagerRostersModule战斗中的队伍管理
BattleManagerScenariosModule天气、地形和场景设定
BattleManagerMegaModuleMega 进化
BattleManagerCharactersModule训练师角色

你不需要逐个配置它们。每个模块通过 BattleManager 访问其同级模块,其余接线来自 Zenject 依赖注入。

步骤 4:了解战斗循环​

BattleStateMachine 把战斗作为一个每回合重复的循环来驱动,直到战斗结束:

Update fought battlers → Request actions (player menu / AI) →
→ Determine action order → Perform actions →
→ End-of-turn callbacks and status countdown →
→ Check and process fainted battlers → [next turn]
... until IsBattleOver → end animation, prize/defeat money, end callbacks

该循环是战斗预制体的一部分;你无需配置它。要理解或修改它,请参见战斗内部机制。

步骤 5:测试战斗​

  1. 确保你的世界场景中有:

    • 一个 PlayerCharacter,其队伍中至少有一只怪物。
    • 绘制在瓦片地图上的遭遇图块。
    • 一个 SceneInfo,其针对该类型的 Encounters 条目指向已配置的 WildEncountersSet。
  2. 在编辑器中按 Play。

  3. 让你的角色走到遭遇图块上。

  4. 走几步之后(由遭遇几率决定)会触发战斗。

快速测试

每步的几率来自你的 MCESettings 资产的 TileEncounterChance 表(每种遭遇类型一个值),除非地图的 SceneInfo 为该类型定义了自己的几率。要不走动就立即测试战斗,请把你正在测试的类型的几率设为 1(100%)。

理解战斗流程​

战斗开始时,底层发生的是:

  1. 玩家走上一个 EncounterTile;这一步按该 Encounter Type 的几率进行判定。
  2. 地图上匹配的 WildEncountersSet 给出当前时段和天气下可能的遭遇;按权重选出其中一个,并从其范围中抽取一个等级。
  3. 用野生怪物的数据创建一个 BattleParameters 对象,BattleLauncher 以叠加方式加载战斗场景。
  4. BattleManager 初始化所有模块。
  5. 播放战斗开场。
  6. 向玩家呈现行动选项:Fight、Bag、Monster、Run。
  7. 行动按优先级和速度顺序执行。
  8. 循环持续,直到一方濒死或玩家逃跑/捕捉。
  9. BattleResultParameters 被送回世界(谁赢了、捕获的怪物、重生标志)。
  10. 战斗场景卸载,世界恢复。

战斗类型​

两个枚举描述战斗是什么:

枚举值含义
BattleTypeSingleBattle、DoubleBattle每方一只怪物,或两只
EnemyTypeWild、Trainer野生怪物(可以捕捉)或 NPC 训练师(不可捕捉,支付奖金)

两者都包含在 BattleParameters 中,并在 BattleManager 上公开。

监听战斗事件​

响应战斗事件而无需把你的游戏玩法代码耦合到内部实现。战斗启动器已在依赖注入中绑定,因此任何脚本都可以订阅它:

using OpenMon.MCE.Battle;

public class MyBattleTracker : MonoBehaviour
{
[Inject] private IBattleLauncher battleLauncher;

private void OnEnable()
{
battleLauncher.SubscribeToBattleLaunched(HandleBattleLaunched);
battleLauncher.SubscribeToBattleEnded(HandleBattleEnded);
}

private void OnDisable()
{
battleLauncher.UnsubscribeFromBattleLaunched(HandleBattleLaunched);
battleLauncher.UnsubscribeFromBattleEnded(HandleBattleEnded);
}

private void HandleBattleLaunched()
{
Debug.Log("A battle has been launched!");
}

private void HandleBattleEnded(BattleResultParameters result)
{
Debug.Log($"Battle ended. Player won: {result.PlayerWon}");
}
}

battleLauncher.BattleInProgress 告诉你是否有战斗正在进行。SDK 还提供带有 OnBattleStarted 和 OnBattleEnded 的 IBattleSystem(engine.Battle),但它与引擎的挂钩尚未连接;参见公共 API 指南和 IBattleSystem。

故障排除​

问题解决方案
战斗没有触发检查图块上是否有带正确 Encounter Type 的 EncounterTile,地图的 SceneInfo 是否有该类型的 WildEncountersSet,以及 TileEncounterChance 是否有对应的值(缺失的类型按 0 计算,并只记录一次警告)
遭遇后黑屏确认战斗场景在你的 Build Settings 场景列表中,并已在 BattleLauncher 上设置
怪物以等级 0 出现确保遭遇条目中设置了 Min Level 和 Max Level
没有战斗音乐检查 BattleLauncher 的野生/训练师战斗列表中是否有音乐,以及 BattleManagerAudioModule 是否已分配音频
战斗开始时崩溃打开 OpenMon Hub → Database 选项卡 → Validate Database 检查是否缺少数据库引用

下一步​