メインコンテンツまでスキップ

Extending Monster Data

Source / Studio エディション限定

このページは、Source / Studio エディションのライセンス所有者のみが利用可能な内部エンジンコードについて説明しています。

MCEのモンスターデータモデルは拡張を考慮して設計されています。このページでは、主要なデータ構造、カスタムフォームを持つ新しいモンスターの追加方法、進化システムの仕組み、および既存のセーブファイルを壊さずに安全にモデルを拡張する方法について説明します。

データモデル概要​

MonsterEntry(ScriptableObject — 種別ごとに1つ)
├── DexNumber
├── AvailableForms: List<Form>
└── DataByForm: List<DataByFormEntry> ← フォームごとに1エントリー
├── 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 — 最低でも1つの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; // 基礎XP獲得量
public GrowthRate GrowthRate; // XP曲線タイプ

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)

わざの割り当て​

わざは4つのカテゴリーに分類されます:

フィールド習得タイミング
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アセットに追加すると、図鑑に表示され、すべてのエンジンシステムで利用可能になります。

フォームシステム​

フォームを使用すると、1つの種が複数のビジュアルおよびメカニカルバリアント(地域フォーム、メガ進化、性別差など)を持つことができます。

各FormはScriptableObjectで、以下を持ちます:

  • 名前識別子
  • HasShinyVersionフラグとそのShinyVersionフォームへの参照
  • 色違いバリアントのIsShinyフラグ

MonsterEntry.AvailableFormsはすべての非色違いフォームをリストします。色違いフォームはAvailableFormsWithShinniesを介して自動的に派生します。

新しいフォームの追加​

  1. Form ScriptableObjectアセットを作成します。
  2. フォームに色違いバリアントがある場合は、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特定の種と交換
EvolveByCriticalHitCounter1回のバトルでN回の急所ヒットを出す
EvolveByDefeatingRivals特定の種のモンスターをN体倒す
EvolveByUsingMoveXTimes特定のわざをN回使用する
EvolveByEvolutionCounterカウンターしきい値後に進化
EvolveByLevelWithSceneTag特定のタグを持つシーンでレベルアップ
EvolveToRandomSpeciesByLevelレベルアップ時にランダムな目標種
SplitEvolutionByLevel特殊な二重進化(2体目のモンスターを生成)

カスタム進化タイプの作成​

以下は「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/デフォルトになります。コード内でnullチェックまたはデフォルト初期化で処理します。
  4. シリアライズされたクラスからフィールドを決して削除しないでください。代わりに[Obsolete]としてマークし、無視します。

MonsterEntryの拡張​

MonsterEntry自体に新しいトップレベルフィールドを追加するには(フォームごとではないデータ):

  1. MonsterEntry.csに[SerializeField]でフィールドを追加します。
  2. 既存フィールドの名前変更には[FormerlySerializedAs]を使用します。
  3. 既存のモンスターアセットはデフォルト値でロードされます — エディタースクリプトまたはInspectorを使用してバッチ更新します。

新しいフォームごとのデータを追加するには、TriInspector属性を使用して既存のグループ/セクションパターンに従ってDataByFormEntryにフィールドを追加します。