跳到主要内容

Extending Monster Data

仅限 Source 和 Studio 版

此页面记录的内部引擎代码仅供 Source 和 Studio 版许可证持有者使用。

MCE 的怪物数据模型是为扩展而设计的。本页涵盖关键数据结构、如何添加具有自定义形态的新怪物、进化系统如何工作,以及如何安全地扩展模型而不破坏现有的存档文件。

数据模型概述​

MonsterEntry(ScriptableObject — 每个物种一个)
├── DexNumber
├── AvailableForms: List<Form>
└── DataByForm: List<DataByFormEntry> ← 每个形态一个条目
├── Graphics(精灵图、材质、图标、世界精灵图)
├── Types(FirstType, SecondType)
├── Dex(描述、品种、身高、体重、叫声)
├── Abilities(普通 + 隐藏)
├── Training(EVYield, CatchRate, BaseFriendship, BaseExperience, GrowthRate)
├── Breeding(EggGroups, EggCycles, BreedingData, 性别比例)
├── Wild(逃跑几率、携带物品)
├── Moves(等级、进化时、蛋招式、其他/TM)
├── BaseStats: Dictionary<Stat, byte>
├── Evolutions: List<EvolutionData> ← 多态
├── MegaEvolutions: Dictionary<Item, Form>
└── Flags(IsLegendary, IsMythical, IsUltraBeast, CanLearnAnyMove)

在运行时,MonsterInstance 将 MonsterEntry 与实例特定状态(等级、个体值、努力值、当前 HP、昵称、携带物品、亲密度和当前形态)包装在一起。

添加新怪物​

步骤 1:创建 MonsterEntry 资源​

使用菜单 Assets > Create > MCE > Monster Database > Monster(或右键单击 Create > MCE > Monster Database > Monster)创建一个新的 MonsterEntry ScriptableObject。

设置以下字段:

  • DexNumber — 怪物在图鉴中的索引。
  • LocalizableName — 本地化键(由 "Auto" 按钮根据资源名称自动填充,遵循 Monsters/{name} 模式)。
  • AvailableForms — 至少添加一个 Form 资源(通常为 "Normal")。引擎会自动从每个形态的 ShinyVersion 读取闪光变体。

步骤 2:配置 DataByFormEntry​

对于 AvailableForms 中的每个形态,DataByForm 列表中存储一个 DataByFormEntry。每个条目包含:

Graphics​

字段类型用途
Front / BackMaterial战斗精灵材质(雌性/默认)
FrontMale / BackMaleMaterial雄性替代材质(如果 HasMaleMaterialOverride 则显示)
FrontShiny / BackShinyMaterial闪光变体材质
Icon / IconShiny / IconMale / IconShinyMaleSprite菜单/队伍图标
EggMaterial / EggIconMaterial / Sprite蛋的视觉效果
WorldIdleSprite* / WorldWalkingSprite*Sprite / List<Sprite>世界地图跟随精灵(4个方向,带有雄性/闪光变体)

Types 和 Dex​

public MonsterType FirstType;     // 主要属性
public MonsterType SecondType; // 次要属性(可以为 null/None)
public string DexDescriptionKey; // 图鉴条目的本地化键
public MonsterSpecies Species; // 例如 "种子怪兽"
public float Height; // 单位:米
public float Weight; // 单位:千克
public AudioReference Cry; // 音效

Abilities​

public List<Ability> Abilities;        // 普通特性(1-2个)
public List<Ability> HiddenAbilities; // 隐藏特性

Training​

public List<StatByteValuePair> EVYield;  // 击败后获得的努力值点数
public byte CatchRate; // 0-255
public byte BaseFriendship; // 初始亲密度值
public uint BaseExperience; // 基础经验值产出
public GrowthRate GrowthRate; // 经验值曲线类型

Breeding​

public List<EggGroup> EggGroups;         // 兼容的蛋组
public byte EggCycles; // 孵化步数
public List<BreedingData> BreedingData; // 蛋中可孵化的物种
public List<PrehatchCallback> PrehatchCallbacks; // 孵化前形态变化
public bool HasBinaryGender;
public float FemaleRatio; // 0.0 到 1.0

Base Stats​

public SerializableDictionary<Stat, byte> BaseStats;  // HP, Atk, Def, SpA, SpD, Spe
public SerializableDictionary<Stat, uint> StatLimits; // 硬上限(例如,脱壳忍者 HP=1)

招式分配​

招式分为四个类别:

字段学习时机
MovesByLevel(List<MoveLevelPair>)达到特定等级时
OnEvolutionMoves(List<Move>)进化为此形态后立即
EggMoves(List<Move>)通过繁殖继承
OtherLearnMoves(List<Move>)TM、技能导师或其他方式

CanLearnAnyMove 标志完全绕过 OtherLearnMoves 列表,允许怪物学习数据库中的任何招式(用于特殊怪物)。

Inspector 会实时验证招式并标记跨类别的重复项。

步骤 3:使用怪物创建助手​

单击任何 DataByFormEntry 上的 Open Helper 按钮,打开 Tools > MCE > Monster Creation Helper 编辑器窗口,该窗口提供了填写所有所需数据的简化工作流程。

步骤 4:注册到数据库​

将新的 MonsterEntry 添加到 MonsterDatabaseInstance 资源中,使其出现在图鉴中并对所有引擎系统可用。

形态系统​

形态允许单个物种拥有多个视觉和机械变体(例如,地区形态、超级进化、性别差异)。

每个 Form 是一个 ScriptableObject,包含:

  • 名称标识符
  • HasShinyVersion 标志及其 ShinyVersion 形态的引用
  • 闪光变体上的 IsShiny 标志

MonsterEntry.AvailableForms 列出所有非闪光形态。闪光形态通过 AvailableFormsWithShinnies 自动派生。

添加新形态​

  1. 创建一个 Form ScriptableObject 资源。
  2. 如果形态有闪光变体,创建第二个 Form 资源并通过 ShinyVersion / IsShiny 链接它们。
  3. 将普通形态添加到怪物的 AvailableForms 列表中。
  4. DataByForm 中将出现一个新的 DataByFormEntry 插槽 — 填写形态特定的数据(属性、种族值、精灵图、招式、进化)。

进化系统​

架构​

进化由多态命令模式驱动:

EvolutionData(抽象基类)
└── TargetSpeciesAndFormEvolutionData(抽象 — 添加 TargetSpecies/TargetForm/KeepShinyIfItIs)
├── EvolveByLevel
├── EvolveByFriendship
│ └── EvolveByFriendshipAtSpecificTime
├── EvolveOnItemUse
├── EvolveWhenTraded
├── EvolveOnLevelUpHoldingItem
├── EvolveOnLevelUpWhenAMoveHasBeenLearnt
└── ...(30+ 内置进化类型)

每个 DataByFormEntry 都有一个使用 [SerializeReference] 进行多态序列化的 List<EvolutionData> Evolutions 字段。这意味着您可以在 Inspector 中混合搭配不同的进化类型。

EvolutionData 抽象 API​

每个进化子类必须实现以下方法:

public abstract class EvolutionData
{
// 检查触发器 — 如果条件满足则返回 true
public abstract bool CanEvolveAfterLevelUp(MonsterInstance monster,
DayMoment currentTime,
PlayerCharacter playerCharacter,
out bool consumeHeldItem);

public abstract bool CanEvolveAfterBattleThroughExtraData(MonsterInstance monster,
DayMoment currentTime,
out bool consumeHeldItem);

public abstract bool CanEvolveWhenUsingItem(MonsterInstance monster,
DayMoment currentTime,
Item item,
PlayerCharacter playerCharacter,
out bool consumeHeldItem);

public abstract bool CanEvolveWhenTrading(MonsterInstance monster,
DayMoment currentTime,
MonsterInstance otherMonster,
PlayerCharacter playerCharacter,
out bool consumeHeldItem);

// 进化成什么
public abstract (MonsterEntry, Form) GetTargetEvolution(MonsterInstance monster, DayMoment currentTime);

// 进化后钩子(可选)
public virtual void AfterEvolutionCallback(MonsterInstance evolvedMonster, ...);

// 图鉴显示数据
public abstract List<DexMonsterRelationshipData> GetDexRelationships(...);

// 深拷贝
public abstract EvolutionData Clone();
}

内置进化类型​

MCE 包含 30+ 种进化类型。以下是最常用的:

类触发条件
EvolveByLevel怪物达到目标等级
EvolveByLevelAtSpecificTime等级 + 时间段
EvolveByLevelWhenSpecificGender等级 + 性别检查
EvolveByFriendship达到亲密度阈值
EvolveByFriendshipAtSpecificTime亲密度 + 时间段
EvolveByFriendshipWithMoveOfType亲密度 + 知道特定属性的招式
EvolveOnItemUse玩家对怪物使用特定道具
EvolveOnItemUseAtSpecificTime使用道具 + 时间段
EvolveOnItemUseOnSpecificGender使用道具 + 性别检查
EvolveOnLevelUpHoldingItem持有特定道具时升级
EvolveOnLevelUpWhenAMoveHasBeenLearnt升级 + 知道特定招式
EvolveOnLevelUpWithOtherMonInParty升级 + 队伍中有另一物种
EvolveWhenTraded交易给其他玩家
EvolveWhenTradedHoldingItem持有特定道具时交易
EvolveWhenTradedWithMonster交易获得特定物种
EvolveByCriticalHitCounter单场战斗中造成 N 次会心一击
EvolveByDefeatingRivals击败 N 只特定物种的怪物
EvolveByUsingMoveXTimes使用特定招式 N 次
EvolveByEvolutionCounter计数器达到阈值后进化
EvolveByLevelWithSceneTag在具有特定标签的场景中升级
EvolveToRandomSpeciesByLevel升级时随机目标物种
SplitEvolutionByLevel特殊双重进化(创建第二只怪物)

创建自定义进化类型​

以下是"HP 低于 50% 时进化"的示例:

using System;
using System.Collections.Generic;
using UnityEngine;
using OpenMon.MCE.Characters;
using OpenMon.MCE.GameFlow;
using OpenMon.MCE.MonsterDatabase.Items;
using OpenMon.MCE.MonsterDex;
using OpenMon.MCE.UI.Dex;
using OpenMon.Foundation.Localization;

namespace OpenMon.MCE.Monster.Evolution
{
[Serializable]
public class EvolveWhenLowHP : TargetSpeciesAndFormEvolutionData
{
[SerializeField]
[Range(0f, 1f)]
public float HPThreshold = 0.5f;

public override bool CanEvolveAfterLevelUp(MonsterInstance monster,
DayMoment currentTime,
PlayerCharacter playerCharacter,
out bool consumeHeldItem)
{
consumeHeldItem = false;
uint maxHP = MonsterMathHelper.CalculateStat(monster, Stat.Hp, null);
float hpRatio = (float)monster.CurrentHP / maxHP;
return monster.AllowEvolution(this) && hpRatio <= HPThreshold;
}

// 对于此进化未使用的触发器返回 false
public override bool CanEvolveAfterBattleThroughExtraData(MonsterInstance m,
DayMoment t, out bool c) { c = false; return false; }
public override bool CanEvolveWhenUsingItem(MonsterInstance m, DayMoment t,
Item i, PlayerCharacter p, out bool c) { c = false; return false; }
public override bool CanEvolveWhenTrading(MonsterInstance m, DayMoment t,
MonsterInstance o, PlayerCharacter p, out bool c) { c = false; return false; }

public override List<DexMonsterRelationshipData> GetDexRelationships(
MonsterDexEntry entry, FormDexEntry formEntry, MonsterGender gender,
ILocalizer localizer)
{
return new List<DexMonsterRelationshipData>
{
new()
{
Species = TargetSpecies,
Form = TargetForm,
Gender = gender,
Mode = DexMonsterRelationshipData.RelationShipDisplayType.Text,
Text = "HP < " + (HPThreshold * 100) + "%",
LocalizableDescriptionKey = "Dex/Evolutions/LowHP"
}
};
}

public override EvolutionData Clone() =>
new EvolveWhenLowHP
{
HPThreshold = HPThreshold,
TargetSpecies = TargetSpecies,
TargetForm = TargetForm,
KeepShinyIfItIs = KeepShinyIfItIs
};
}
}

因为 Evolutions 使用 [SerializeReference],您的新子类在添加进化条目时会自动出现在 Inspector 的类型选择器下拉菜单中。

进化如何在运行时触发​

EvolutionManager ScriptableObject 在运行时协调进化:

  1. 升级后,TriggerEvolutionsAfterLevelUp() 遍历所有 NeedsLevelUpEvolutionCheck 设置为 true 的队伍怪物。
  2. 对于每个怪物,遍历 FormData.Evolutions 并调用 CanEvolveAfterLevelUp()。
  3. 在第一个匹配项上,调用 TriggerEvolution(),它会:
    • 加载进化场景(淡出至黑屏)。
    • 播放进化动画(玩家可以取消)。
    • 如果未取消,调用 AfterEvolutionCallback() 用于进化后逻辑。
  4. 战斗后,TriggerEvolutionAfterBattleThroughExtraData() 处理基于计数器的进化(会心一击、招式使用等)。

序列化注意事项​

[SerializeField] vs [SerializeReference]​

  • [SerializeField] — 用于具体类型(种族值、精灵图、基础数据)。Unity 按值序列化。
  • [SerializeReference] — 用于多态字段,如 List<EvolutionData>、List<BreedingData> 和 List<PrehatchCallback>。Unity 存储具体类型名称,使 Inspector 能够显示类型选择器。

存档迁移​

重要

切勿在没有迁移计划的情况下重命名序列化字段。使用 [FormerlySerializedAs("OldName")] 在重命名时保留数据。MCE 已广泛使用此模式(例如,FrontShinny 已重命名为 FrontShiny)。

向 DataByFormEntry 或 MonsterInstance 添加新字段时:

  1. 添加字段并设置合理的默认值。
  2. 现有资源将以默认值反序列化 — 无数据丢失。
  3. 存档文件使用通过 MCESavesSerializer 的 JSON 序列化。加载旧存档时,新字段将为 null/默认值。在代码中使用空检查或默认初始化来处理此情况。
  4. 切勿从序列化类中删除字段。将它们标记为 [Obsolete] 并忽略即可。

扩展 MonsterEntry​

要向 MonsterEntry 自身添加新的顶级字段(而非每个形态的数据):

  1. 在 MonsterEntry.cs 中使用 [SerializeField] 添加字段。
  2. 如果重命名现有字段,请使用 [FormerlySerializedAs]。
  3. 现有怪物资源将以默认值加载 — 使用编辑器脚本或 Inspector 批量更新它们。

要添加新的按形态数据,请按照带有 TriInspector 属性的现有组/节模式向 DataByFormEntry 添加字段。