View Format: Multi-Page Single Page

Simple Enemy Forge

Generate Complete Enemy Databases, Squads, Spawn Tables, and More in Minutes.

Enemy Forge Wizard

The Enemy Forge is the foundation wizard of the entire Simple Enemy Forge ecosystem. Every enemy you create here becomes available to Squad Forge (as squad members), Spawn Forge (as spawn entries), Scaling Forge (as scaling targets), Wave Forge (as wave entries), Behavior Forge (as behavior subjects), and Faction Forge (as faction members). The Enemy Forge is always your starting point — define your enemies once, use them everywhere.

Open the wizard via Window › Simple Enemy Forge › Enemy Forge Wizard. It walks you through a 5-step process to define your enemy properties, create enemies, configure output paths, and generate a complete database with type-safe enums and a custom Inspector editor.

The wizard produces a ScriptableObject-based enemy database that is fully data-driven. Nothing is hardcoded — there is no built-in "HP" stat, no predefined "Rank" dropdown, no assumed "Aggro Range" field. You define every property your enemies need using four flexible types, and the system works for any genre: RPG, soulslike, survival horror, tower defense, sci-fi, RTS, or anything else you can imagine.

The 5 Steps: Setup → Definitions → Enemies → Settings → Generate. You can move freely between steps using the tab bar at the top of the wizard window. Each step validates its input and shows errors or warnings in real time.

The Dynamic Property System

At the heart of the Enemy Forge is a dynamic property system that lets you define the exact shape of your enemy data. Rather than working around a rigid, genre-specific schema, you build a property system that matches your game perfectly. A fantasy RPG might need "Enemy Type" and "Base HP"; a soulslike might need "Poise" and "Stagger Threshold"; a tower defense might need "Speed" and "Armor Type". The Enemy Forge handles all of these through four property types.

Property Type What It Is Stored As Example
Category A dropdown menu. Each category has a label and a list of entries the user picks from. Can be single-select (pick one value) or multi-select (pick multiple values via checkboxes). int[] / SimpleIntArray[] "Enemy Type" with entries [Humanoid, Undead, Beast, Dragon], "Rank" with entries [Minion, Elite, Boss]
Flag A boolean on/off toggle with a configurable default value. Flags are perfect for binary properties that either apply to an enemy or do not. bool[] "Is Boss" (default: false), "Is Flying" (default: false), "Has Shield" (default: false)
Numeric A number value with min/max constraints. Can be a single value or a range (stores both a min and max value). Can be integer or float. float[] "HP" (integer, 1–99999, default 100), "Aggro Range" (float, 0.0–50.0, default 10.0), "XP Reward" (integer, 0–9999)
Text A free-form string field with a lineCount hint (1–6) that controls UI height. lineCount=1 renders as a single-line field; lineCount=2+ renders as a multiline text area. string[] "Lore" (lineCount: 3), "Weakness Hints" (lineCount: 2), "Battle Cry" (lineCount: 1)

Internally, each enemy stores parallel arrays whose indices map to the definitions you create in Step 2. For example, if you define three categories — "Enemy Type" at index 0, "Rank" at index 1, and "Element" at index 2 — then every enemy has a categoryValues[] array of length 3, where categoryValues[0] holds the Enemy Type selection, categoryValues[1] holds the Rank selection, and so on. The same pattern applies to flags, numerics, and texts.

How it works at runtime: The generated database includes metadata arrays (CategoryLabels, FlagNames, NumericNames, TextNames, and entry arrays for each category) so that property indices always resolve to human-readable names. You never need to remember that "index 2" means "Element" — the database tells you.

This architecture means you can add, remove, or reorder property definitions at any time during development. When you add a new category, the SyncAllEnemiesWithDefinitions() method automatically extends every enemy's arrays to match. When you remove a property, the arrays shrink. The wizard keeps your data in sync so you never end up with mismatched array lengths.

Genre Templates

Step 1 of the wizard offers a template dropdown that pre-populates your property definitions with genre-appropriate categories, flags, numerics, and texts. Templates are a starting point — you can freely modify, add, or remove any property after applying a template. They are designed to save you the work of researching what properties a typical game in that genre needs.

Each template also includes example enemies that demonstrate realistic property values. These examples serve as reference data for AI content generation — when you export your schema, AI models can see how properties are used in practice and generate consistent, well-structured enemies.

Template Categories Flags Numerics Texts Examples Highlights
None (Start Fresh) 0 0 0 0 0 Blank canvas — define everything from scratch
Generic RPG 4 6 10 2 3 Enemy Type, Rank, Element, base stats (HP, ATK, DEF, SPD)
Soulslike 4 5 15 3 3 Poise, stagger threshold, weakness system, attack patterns
Survival / Horror 5 8 12 2 3 Detection range, stealth modifier, fear factor, mutation tier
Tower Defense 4 6 10 1 3 Speed, armor type, wave group, path priority
Sci-Fi / Shooter 5 5 14 2 3 Shield type, tech level, faction allegiance, energy weapon resist
RTS / Strategy 6 8 16 2 4 Unit class, population cost, build time, supply cap

All templates use generic, non-trademarked terminology and are safe for commercial use. No template references specific game names, trademarked abilities, or copyrighted content.

Switching templates clears existing definitions. When you select a new template, a confirmation dialog appears before any definitions are replaced. If you select "None (Start Fresh)" while definitions exist, you will be asked "Clear Definitions?" before proceeding. If you have already customized your properties, consider exporting your schema first as a backup before switching templates.

Step 1 — Setup

The Setup step configures the basic identity of your enemy database. Everything you set here determines the names of the generated files, the C# namespace they live in, and which genre template (if any) pre-populates your property definitions. Take a moment to choose meaningful names — they will appear throughout your codebase once generation is complete.

Database Configuration

1 Database Name
A unique name for your enemy database (e.g., "GameEnemies", "DungeonMonsters"). This must be a valid C# identifier — letters, numbers, and underscores only, and it cannot start with a number. The database name is used as part of the generated asset file name.
2 Namespace (Optional)
The C# namespace for all generated scripts (e.g., "MyGame.Enemies", "RPG.Monsters"). If provided, all generated classes and enums are wrapped in this namespace, which is useful for avoiding naming collisions in larger projects. Leave this empty to generate code in the global namespace.
3 Class Prefix
A short identifier (e.g., "Dungeon", "Fantasy", "SciFi") used to name all generated files. This prefix is prepended to every generated class name using PascalCase. For example, a prefix of Dungeon generates DungeonType.cs, DungeonDatabase.cs, Editor/DungeonDatabaseEditor.cs, and DungeonDatabase.asset. The prefix must be a valid C# identifier if provided.
4 Append Suffix
A toggle that inserts a configurable suffix between the class prefix and the type name. When enabled, a text field appears where you can type the suffix (e.g., "Enemy"). With prefix MyGame and suffix Enemy, the generated names become MyGameEnemyType.cs, MyGameEnemyDatabase.cs, and MyGameEnemyDatabase.asset. This is useful when you want generated names to be more descriptive without making the prefix itself long.

Template Selection

Below the database configuration, you will find the Template Selection section. Choose a genre template from the dropdown to pre-populate your definitions with genre-appropriate properties, or select None (Start Fresh) to define everything from scratch. When a template is selected, the wizard displays a description of the template, the number of categories, flags, numerics, texts, and example enemies it includes. Templates can be freely modified after selection — they are a starting point, not a constraint.

Simple Attribute Forge Integration

If the Simple Attribute Forge package is installed in your project, an additional section appears in Step 1 titled "Simple Attribute Forge Integration". When SAF is detected, enemies gain the ability to reference modifier assets — ScriptableObject references to modifier assets created with Simple Attribute Forge's Modifier Wizard. These modifiers represent buffs, debuffs, or status effects that the enemy can apply to the player (e.g., a "Poison" modifier on a snake enemy, a "Burning" modifier on a fire elemental).

SAF integration is lighter for enemies than for items. Unlike Simple Item Forge (which uses SAF for requirements, stat bonuses, and modifiers), Simple Enemy Forge only uses SAF for modifier references. Enemies do not have attribute requirements or stat bonuses — they have their own dynamic numeric properties for stats like HP, Attack, and Defense. For full details on the detection lifecycle and what happens when SAF is installed or removed, see the SAF Integration page.

Simple Item Forge Integration

If the Simple Item Forge package is installed in your project, an additional section appears that enables loot table linking. Each enemy can be assigned a lootTableCode that references a loot table from SIF's Loot Forge. This creates a direct connection between enemies and their drop tables — when an enemy is defeated, your game logic can look up the enemy's loot table code and roll against it to determine item drops.

SIF is optional. The Enemy Forge works perfectly without Simple Item Forge installed. You can add or remove SIF at any time with zero compilation errors. The loot table fields are always present in the runtime struct but only populated when SIF is linked. For full details, see the SIF Integration page.

Generated Names Preview

The bottom of Step 1 shows a Generated Names Preview that reflects your current configuration. Based on your class prefix (and suffix, if enabled), it displays three generated names: the Enum ({GP}Type), the Database ({GP}Database), and the Asset ({GP}Database.asset). The custom editor name is not shown here. This lets you verify your naming before proceeding.

Validation

A validation panel at the very bottom checks your configuration in real time. It reports errors (empty database name, invalid C# identifiers) and warnings (empty namespace, existing files that will be overwritten). You cannot proceed to the next step until all errors are resolved.

Step 2 — Definitions

This is where you design the "shape" of your enemy data. Every property you define here becomes available on every enemy in Step 3. If you selected a genre template in Step 1, the definitions will already be populated — you can modify, add, or remove any of them. If you started fresh, this step begins empty and you build your property system from scratch.

Each section uses a ReorderableList with drag-and-drop reordering and a count field in the header for quick bulk creation. Type a number into the count field and press Enter to instantly create that many entries, which you can then fill in.

Categories

Categories are dropdown menus your enemies will use. They are the most structured property type, ideal for classification and filtering. Each category definition has three components:

  • Label — The display name shown in the UI (e.g., "Enemy Type", "Rank", "Element", "Movement Type")
  • Entries — The list of options the user picks from. Each entry is a string (e.g., "Humanoid", "Undead", "Beast"). Entries are displayed in a nested ReorderableList within each category, so you can drag to reorder them. The count field in the entries header lets you create many entries at once.
  • Allow Multiple — A toggle that changes how the category behaves:
    • OFF (default) — Single-select dropdown. The user picks exactly ONE value. A default entry applies to new enemies. The value is stored as a single integer index.
    • ON — Multi-select checkboxes. The user can pick MULTIPLE values simultaneously. There is no single default. The values are stored as an array of selected indices (using SimpleIntArray for Unity serialization).

For example, an "Element" category with Allow Multiple ON and entries [Fire, Ice, Lightning, Poison] lets an enemy have both "Fire" and "Ice" selected simultaneously, which is perfect for dual-element bosses. Meanwhile, an "Enemy Type" category with Allow Multiple OFF ensures every enemy is classified as exactly one type.

Flags

Flags are boolean on/off toggles. They are the simplest property type, perfect for binary states that either apply to an enemy or do not. Each flag definition has two components:

  • Name — The display name (e.g., "Is Boss", "Is Flying", "Is Undead", "Has Shield", "Can Summon")
  • Default Value — The initial on/off state for newly created enemies. Most flags default to false, but you might want a flag like "Is Hostile" to default to true.

In Step 3, flags appear as a vertical list of toggle checkboxes (one flag per row, full-width ToggleLeft). They are also useful for Behavior Forge conditions — a behavior rule might check "Is Flying" to determine whether to use a ground attack or aerial attack pattern.

Numerics

Numeric properties store number values with configurable constraints. They are the most versatile property type, handling everything from HP to aggro range to XP rewards. Each numeric definition has these components:

  • Name — The display name (e.g., "HP", "Attack", "Defense", "Aggro Range", "XP Reward")
  • TypeInteger or Float. Integer properties show IntField in the editor; float properties show FloatField. Choose integer for whole numbers like HP, damage, or XP. Choose float for decimal values like speed multipliers, aggro range, or critical chance.
  • Is Range — When OFF, the property stores a single value. When ON, the property stores a min/max pair, which is useful for damage ranges (e.g., "10-25 damage"), patrol distances, or any property that naturally varies between two bounds. Range values are stored in two parallel arrays: numericValues[] for the min and numericRangeMaxValues[] for the max.
  • Min / Max Constraints — Hard limits that the value(s) must stay within. For example, "HP" might be constrained to 1–99999, while "Critical Chance" might be constrained to 0.0–1.0. These constraints are enforced in the wizard UI with clamped fields.
  • Default Value(s) — The starting value for new enemies. For range properties, there is a default min and a default max.

Texts

Text properties store free-form string content. They are ideal for lore, weakness hints, battle cries, ability descriptions, or any content that does not fit neatly into a category, flag, or number. Each text definition has two components:

  • Name — The display name (e.g., "Lore", "Weakness Hints", "Battle Cry", "Special Ability")
  • Line Count (1–6) — Controls the UI height of the text field in Step 3 and in the generated custom editor. A lineCount of 1 renders a compact single-line TextField. A lineCount of 2 or higher renders a multiline TextArea whose height scales with the lineCount value. Use 1 for short labels like "Battle Cry" or "Patrol Zone". Use 3 or higher for longer content like "Lore" or "Combat Strategy Notes".

Schema Export / Import

At the top of Step 2, four buttons provide the Schema Export/Import system for AI-assisted content generation:

  • Export Full JSON — Exports a comprehensive JSON file containing all definitions, all existing enemies, comprehensive AI instructions (what it can do, what it cannot do, property guidance, balance tips, ecosystem overview), and SAF modifier data plus SIF loot table data if available. This is the most complete export — an AI model receiving this file has everything it needs to generate enemies that perfectly match your property system.
  • Export Light JSON — Exports definitions and instructions only, with an empty enemies array and no integration data. The instructions tell the AI to ask you for context before generating. This mode is much smaller and useful when you want the AI to be interactive rather than just filling in data.
  • Export Markdown — Exports a human-readable Markdown document ideal for pasting directly into ChatGPT, Claude, or any LLM chat interface. Contains the same information as Full JSON but formatted as tables, bullet points, and code blocks for easy reading.
  • Import Schema (JSON) — Reads a JSON file and populates both definitions and enemies. Supports the schema format produced by the export buttons. The import system validates enemy codes, maps category/flag/numeric values by name, detects duplicates, and reports skip reasons for any entries that fail validation. After import, a popup shows the count of imported and skipped enemies.

See the AI Workflow page for the full schema export/import guide, including the exact JSON format, tips for working with AI models, and the phased generation workflow.

Schema version: The current schema version is 1.0. The schema includes a schemaInfo section with version, generation timestamp, template tracking (whether the template was modified and what changed), and a structure guide explaining each section. The definitions section contains all four property type arrays with their full metadata (labels, entries, defaults, constraints).

Step 3 — Enemies

This is where you create and edit your enemies using the properties defined in Step 2. The interface uses a split-panel layout with an enemy list on the left and the enemy editor on the right. Select an enemy from the list to view and edit its properties. The wizard provides a rich set of tools for searching, filtering, sorting, and bulk-editing enemies.

Left Panel — Enemy List (280px)

The left panel contains the master enemy list with several tools above it:

  • Search bar + Find button — Type a search term to filter enemies by name or code. The status text shows the match count (e.g., "12 matches of 87"). Click the Find button to cycle through matching enemies one by one, automatically selecting and scrolling to each match.
  • Category filter — Two dropdown menus that work together. The first dropdown selects which category to filter by (e.g., "Enemy Type", "Rank"). The second dropdown selects which specific value within that category to show (e.g., "Undead", "Boss"). Only enemies matching the selected category value are displayed. Both dropdowns include an "(All)" option to show everything.
  • Sort dropdown — Five options: None (original order), Name A-Z, Name Z-A, Code A-Z, Code Z-A. Sorting reorders the actual enemy data for consistent ordering.
  • Multi-select toolbar — A row of bulk operation buttons:
    • Select All toggle — Selects or deselects all currently visible (filtered) enemies
    • Delete button — Deletes all selected enemies after a confirmation dialog
    • Duplicate button — Duplicates all selected enemies, appending " (Copy)" to each name and "_COPY" to each code
    • Set Icon button — Opens a sprite picker to assign the same icon to all selected enemies at once. This is invaluable for batch icon assignment — select all undead enemies, click Set Icon, pick a placeholder sprite.
  • Status bar — Shows "Showing X of Y | Z selected" at the bottom of the list area, giving you a quick count of visible enemies, total enemies, and selected enemies.
  • ReorderableList — The enemy list itself supports drag-and-drop reordering and has a count field in the header for quick bulk creation. Each enemy row shows a checkbox (for multi-select) and the enemy name. The [+] button at the bottom adds a new empty enemy.
  • Pagination — A "Show All" toggle (on by default) controls pagination. When unchecked, enemies are paginated with a configurable "Per Page" count and page navigation buttons ([<] Page X of Y [>]). This keeps the list responsive with large databases.

Right Panel — Enemy Editor

Select an enemy from the list to edit it in the right panel. The editor shows all of the enemy's properties organized into collapsible sections:

Identity Section (Always Visible)

  • Name — The display name of the enemy (e.g., "Goblin Warrior", "Shadow Drake"). This is the name shown in-game.
  • Code — The unique identifier used in code (e.g., GOBLIN_WARRIOR, SHADOW_DRAKE). Codes must be valid C# identifiers and are typically written in UPPER_SNAKE_CASE. The Auto button next to the code field converts the enemy name to UPPER_SNAKE_CASE automatically, handling spaces, invalid characters, and leading digits.
  • Description — A TextArea for the enemy's description or lore text.
  • Icon — A Sprite ObjectField for the enemy's portrait or sprite, shown with a 70x70 preview thumbnail.

Categories Section (Collapsible)

Shows a header with the count (e.g., "Categories (4)") and a foldout arrow. When expanded, each category is displayed according to its Allow Multiple setting:

  • Single-select categories show a dropdown menu with all entries
  • Multi-select categories show a grid of checkboxes, one for each entry

The section is always visible regardless of how many categories exist.

Flags Section (Collapsible)

Shows all flags as a vertical list of toggle checkboxes — one flag per row, full-width ToggleLeft. Each toggle is labeled with the flag name. The header shows the count (e.g., "Flags (6)"). If no flags are defined, the section shows "No flags defined." as a placeholder. This is the only section in the wizard that displays a placeholder when its definition count is zero.

Numerics Section (Collapsible)

Shows each numeric property with the appropriate field type. Single values display as an IntField or FloatField (depending on the isInteger setting). Range values display with separate Min and Max fields arranged vertically, with the property name as a label above. All values are clamped to the constraints defined in Step 2. The header shows the count (e.g., "Numeric Properties (12)").

Texts Section (Collapsible)

Shows each text property with a UI field sized according to lineCount. Properties with lineCount=1 display as a single-line TextField. Properties with lineCount=2+ display as a multiline TextArea whose height scales with the lineCount value. Text areas with content exceeding the fixed height show an inner scrollbar for overflow. The header shows the count (e.g., "Text Properties (2)").

Placeholders in the generated editor: In the generated custom editor (the Inspector for the ScriptableObject), all four property sections display a placeholder message when their definition count is zero: "No categories defined.", "No flags defined.", "No numeric properties defined.", and "No text properties defined." In the wizard, only the Flags section shows a placeholder.

SAF Modifiers Section (Collapsible, Conditional in Wizard)

In the wizard, this section only appears when Simple Attribute Forge is installed (hidden otherwise). In the generated custom editor, the section is always emitted in the code and auto-adapts at runtime via Type.GetType() — when SAF is installed, the ObjectField uses the correct modifier base type; when SAF is removed, it falls back to ScriptableObject. No regeneration is needed when SAF is installed or uninstalled. It contains a ReorderableList of modifier references (stored as ScriptableObject[]). These are Unity asset references that link to modifier assets created with Simple Attribute Forge's Modifier Wizard. Each entry is an ObjectField where you drag and drop a modifier asset. Example: a "Poison" modifier that applies damage over time when the enemy attacks, or a "Slow" modifier that reduces player movement speed. Note that modifiers are Unity object references and cannot be set via JSON import — they must be assigned manually or via the generated custom editor.

SIF Loot Table Section (Collapsible, Conditional in Wizard)

In the wizard, this section only appears when Simple Item Forge is installed (hidden otherwise). In the generated custom editor, the section is always emitted and auto-adapts at runtime via Type.GetType(), just like the SAF section — no regeneration needed. It contains two fields:

  • Loot Table Code — A string field for the loot table code from SIF's Loot Forge (e.g., "GOBLIN_DROPS", "BOSS_DRAGON_LOOT"). When SIF is detected, this field shows a dropdown populated with available loot table codes from your SIF database.
  • Loot Table — An ObjectField for a direct reference to a SIF Loot Database ScriptableObject, provided as a convenience for Inspector-based workflows.

Duplicate Enemy Button

At the bottom of the right panel, a Duplicate Enemy button creates a copy of the currently selected enemy with " (Copy)" appended to the name and "_COPY" appended to the code.

Inline Validation

The editor validates enemies in real time and shows colored messages directly in the enemy editor:

  • Error — Empty name or code (the enemy cannot be generated without these)
  • Error — Code is not a valid C# identifier (must be letters, numbers, underscores; cannot start with a number)
  • Warning — Duplicate name or code (two enemies share the same name or the same code, which will cause issues during generation)

Step 4 — Settings

Configure where generated files are saved and which files to generate. This step gives you full control over your project's file organization.

Output Paths

A single Output Folder field with a Browse button that opens a folder picker dialog. The path must be within the Assets folder. The default is Assets/Generated/Enemies. Scripts are automatically placed in a Scripts subfolder, the custom editor in a nested Scripts/Editor subfolder, and the asset file in the output folder itself.

A Custom Paths toggle below the output folder reveals separate Scripts Folder and Assets Folder fields when enabled, each with its own Browse button. This lets you place scripts and data assets in different locations if your project structure requires it. When Custom Paths is disabled, the resolved paths are shown as read-only labels beneath the output folder field.

Code Generation Options

Two toggles control which files are generated. Each toggle shows the exact file name that will be created based on your class prefix:

Toggle Generates Description
Generate Enum {GP}Type.cs A C# enum with one entry per enemy code. Provides compile-time safety and IntelliSense support for enemy references in your code. Recommended.
Generate Database {GP}Database.cs + .asset The main database class implementing ISimpleEnemyDataSource and the populated data asset. Required for runtime use. Also generates the custom editor (Editor/{GP}DatabaseEditor.cs).
What is {GP}? GP stands for "Generated Prefix." It is derived from the Class Prefix you set in Step 1. For example, a class prefix of "Dungeon" produces GP = "Dungeon". Files generated: DungeonType.cs, DungeonDatabase.cs, DungeonDatabase.asset, Editor/DungeonDatabaseEditor.cs.

File Preview

Below the toggles, a Generated File Preview section shows the full list of files that will be created based on your current selections, including their complete paths. Checkmarks indicate which files are enabled. This lets you review exactly what will be written to disk before you proceed to generation.

Existing files will be overwritten. If files with the same names already exist at the target paths, the validation panel warns you. Generation always overwrites — it does not merge. If you have manually edited a generated file, consider backing it up before regenerating.

Step 5 — Generate

The final step. Review your complete configuration and click Generate to create your enemy database. The wizard performs a two-phase generation process that handles Unity's domain reload automatically.

Generation Summary

A read-only summary panel at the top shows all key metrics at a glance:

  • Database Name, Namespace, and Template used
  • Total enemy count, category count (with total entries), flag count, numeric count, text count
  • Integration status (SAF enabled with modifier count, SIF enabled with loot table link count)

Files to Generate

A list of every file that will be created, with full paths. This is your final chance to review before committing.

Phase 1 — Script Generation

When you click Generate, the wizard writes all C# files to disk:

Scripts Path (e.g., Assets/Generated/Enemies/Scripts)/ ├── {GP}Type.cs — Enum of all enemy codes ├── {GP}Database.cs — ScriptableObject with all data └── Editor/ └── {GP}DatabaseEditor.cs — Custom Inspector editor Asset Path (e.g., Assets/Generated/Enemies/Data)/ └── {GP}Database.asset — Your data

After writing the files, the wizard saves all wizard data (enemies, definitions, metadata) to a temporary JSON file at Temp/SimpleEnemyForge_PendingAssetData.json and sets a SessionState flag. It then triggers AssetDatabase.Refresh() to compile the new scripts.

Phase 2 — Asset Creation (Automatic)

Unity recompiles the new scripts, which causes a domain reload. All static variables are reset, but the SessionState flag and temp file survive. An [InitializeOnLoad] handler detects the pending flag and temp file, then uses EditorApplication.delayCall to wait until Unity is fully ready. A retry loop checks whether the generated database type is loaded, then creates the ScriptableObject asset and populates it with all enemy data and metadata arrays.

Generate clicked → Write C# scripts to disk → Save wizard data to Temp/SimpleEnemyForge_PendingAssetData.json → Set SessionState flag → AssetDatabase.Refresh() → Unity compiles new scripts (domain reload — static state lost) → [InitializeOnLoad] detects flag + temp file → EditorApplication.delayCall (wait for Unity to be ready) → Retry loop checks if generated type is loaded (up to 10 retries) → CreateDatabaseAsset() — populate SO with all data → Cleanup: delete temp file, clear SessionState flag

Post-Generation Actions

After successful generation, the wizard displays action buttons:

  • Open Scripts Folder — Opens the generated scripts directory in your file browser
  • Select Database Asset — Selects and pings the generated .asset file in the Unity Project window
  • Create New Database — Resets the wizard to start fresh for a new database
Do not close Unity during generation. The two-phase process requires a domain reload to complete. The wizard persists its state to a temp JSON file and uses SessionState flags to survive the reload, but closing Unity mid-process will abort asset creation. The generated scripts will still be valid — you can manually create the ScriptableObject asset later.

What Gets Generated

The Enemy Forge generates up to 3 script files (depending on your settings in Step 4), plus the data asset. Each file serves a specific purpose in your runtime architecture.

{GP}Type.cs — Enum

A strongly-typed enum with one entry per enemy code. This provides compile-time safety and full IntelliSense support, so you never need to pass raw strings around your codebase. The enum values are generated from the enemy codes you defined in Step 3.

public enum DungeonType { GoblinWarrior, SkeletonArcher, ShadowDrake, AncientLich, // ... one entry per enemy (PascalCase) }

{GP}Database.cs — ScriptableObject

A ScriptableObject class that implements ISimpleEnemyDataSource. It contains all enemy data in serialized arrays and metadata arrays for category labels (with entries), flag names, numeric property names, and text property names. This single asset is the complete runtime representation of your enemy database.

The generated class includes implementations of all ISimpleEnemyDataSource methods: enemy access (GetEnemyByCode, GetEnemyByName), dynamic filtering (GetEnemiesByCategory, GetEnemiesByFlag, GetEnemiesByNumericRange), and metadata queries (GetCategoryLabels, GetCategoryEntries, etc.).

Accessing Your Database in Code

The generated database ScriptableObject implements ISimpleEnemyDataSource, which provides all methods you need for enemy access, filtering, and metadata queries. Assign the database asset to a serialized field on your MonoBehaviour or ScriptableObject, then call methods directly:

// Reference the database via a serialized field [SerializeField] private DungeonDatabase dungeonDatabase; // Access enemies by code string var goblin = dungeonDatabase.GetEnemyByCode("GOBLIN_WARRIOR"); Debug.Log(goblin.Value.name); // "Goblin Warrior" Debug.Log(goblin.Value.description); // "A ferocious goblin..." // Filter by category (e.g., category 0 = Enemy Type, entry 2 = Undead) var undead = dungeonDatabase.GetEnemiesByCategory(0, 2); // Filter by flag (e.g., flag 0 = Is Boss) var bosses = dungeonDatabase.GetEnemiesByFlag(0, true); // Filter by numeric range (e.g., numeric 0 = HP, range 500-2000) var toughEnemies = dungeonDatabase.GetEnemiesByNumericRange(0, 500f, 2000f); // Metadata: get property names string[] categoryLabels = dungeonDatabase.GetCategoryLabels(); string[] flagNames = dungeonDatabase.GetFlagNames(); string[] numericNames = dungeonDatabase.GetNumericNames();

You can also use the ISimpleEnemyDataSource interface for polymorphic access, which is how the other forges (Squad, Spawn, Wave, etc.) interact with your enemy databases:

ISimpleEnemyDataSource db = dungeonDatabase; var allEnemies = db.GetEnemyDefinitions(); var codes = db.GetEnemyCodes(); bool hasEnemy = db.HasEnemy("SHADOW_DRAKE");

{GP}DatabaseEditor.cs — Custom Inspector

A custom editor generated alongside your database that replaces Unity's default Inspector (which shows unhelpful "Element 0", "Element 1" labels) with a professional split-panel editor. This editor is placed in an Editor/ subfolder so it is automatically excluded from runtime builds. It provides a full-featured editing experience directly in the Inspector window.

Split Panel Layout

Enemy list on the left (280px), full editor on the right. Select any enemy from the list to view and edit all of its properties with proper field names and types.

Search, Sort & Filter

Search by name or code, sort by Name or Code (A-Z / Z-A), and filter by any category value using two dropdown menus. An X button clears all filters at once.

Multi-Select & Bulk Operations

Per-enemy checkboxes, a Select All toggle, and bulk buttons for Delete Selected, Duplicate Selected, and Set Icon (assign the same sprite to all selected enemies at once).

Pagination

A "Show All" toggle (on by default) controls pagination. When unchecked, enemies are paginated with a configurable "Per Page" count and page navigation buttons ([<] Page X of Y [>]). This keeps the editor responsive with large databases.

All Properties By Name

Every property displays its human-readable name from your definitions — not array indices. Categories show dropdown menus (or checkbox grids), flags show labeled toggles, numerics show named fields with constraints, and texts show appropriately-sized text areas.

SAF & SIF Sections

The Modifiers section and Loot Table section are always emitted in the generated editor code. They auto-adapt at runtime via Type.GetType(): when SAF or SIF is installed, the ObjectFields use the correct types; when removed, they fall back to ScriptableObject. No regeneration is needed when integration packages are installed or uninstalled.

{GP}Database.asset — Data File

The ScriptableObject asset file containing all of your enemy data. This is the file you assign to a MonoBehaviour's serialized field or reference from any script that needs enemy data. It is a standard Unity asset that can be version-controlled, duplicated, and referenced from any scene or prefab. It is also the asset you assign to Squad Forge, Spawn Forge, Scaling Forge, Wave Forge, Behavior Forge, and Faction Forge as their linked enemy database.

Runtime Data Structures

The Enemy Forge uses a small set of runtime structs and an interface that live in the SimpleEnemyForge namespace. These types are defined in the package's Runtime/ folder and are available to all of your game scripts.

SimpleEnemyDefinition

The core enemy struct. Each enemy stores fixed identity fields and dynamic property arrays whose indices correspond to the definitions you created in Step 2.

public struct SimpleEnemyDefinition { // Identity (always present) public string name; public string code; public string description; public Sprite icon; // Dynamic property arrays (indices match Step 2 definitions) public int[] categoryValues; // Single-select category indices public SimpleIntArray[] categoryMultiValues; // Multi-select category indices public bool[] flagValues; public float[] numericValues; // Single values (or min for ranges) public float[] numericRangeMaxValues; // Max values for range numerics public string[] textValues; // Simple Attribute Forge integration (always compiled; populated when SAF is linked) public ScriptableObject[] modifiers; // Simple Item Forge integration (always compiled; populated when SIF is linked) public string lootTableCode; public ScriptableObject lootTable; // Convenience accessors public int GetCategoryValue(int categoryIndex); public int[] GetCategoryMultiValues(int categoryIndex); public bool GetFlagValue(int flagIndex); public float GetNumericValue(int numericIndex); public (float min, float max) GetNumericRange(int numericIndex); public string GetTextValue(int textIndex); }

SimpleIntArray

A wrapper struct for serializing arrays of int, since Unity cannot serialize jagged arrays directly. Used by categoryMultiValues for multi-select categories.

public struct SimpleIntArray { public int[] values; public int Length => values?.Length ?? 0; public int this[int index] { get; } }

ISimpleEnemyDataSource

The interface that all generated enemy databases implement. It provides methods for enemy access, metadata queries, and dynamic filtering. See the API Reference page for complete method documentation.

Method Group Key Methods
Enemy Access GetEnemyDefinitions(), GetEnemyByCode(string), GetEnemyByName(string), GetEnemyCodes(), GetEnemyNames()
Metadata EnemyCount, HasEnemy(string), GetEnemyEnumType()
Property Definitions GetCategoryLabels(), GetCategoryEntries(int), GetFlagNames(), GetNumericNames(), GetTextNames()
Dynamic Filtering GetEnemiesByCategory(int, int), GetEnemiesByFlag(int, bool), GetEnemiesByNumericRange(int, float, float)

The interface is also used by the dependent forges for multi-database merging. Squad Forge, Spawn Forge, Wave Forge, and others accept a List<ScriptableObject> of enemy databases. Each database is cast to ISimpleEnemyDataSource using the database is ISimpleEnemyDataSource pattern, and enemy codes are merged across all databases (first-database-wins for duplicates). This means you can split your enemies across multiple databases (e.g., one for dungeon enemies, another for world bosses) and link them all to a single Squad or Spawn setup.

Tips & Best Practices

Start With a Template

Even if you plan to customize everything, starting with a genre template gives you a solid foundation. It shows how properties are typically structured for your genre and saves time on the initial setup. You can always modify, add, or remove properties afterward.

Use AI for Bulk Content

Define 3–5 enemies manually in Step 3 to establish your patterns, then export your schema (Full JSON or Markdown) and let an AI model generate the remaining enemies. Import the JSON back and review. This workflow can produce dozens or hundreds of well-structured enemies in minutes. See AI Workflow for details.

Auto-Generate Codes

Use the Auto button next to the Code field to convert enemy names to UPPER_SNAKE_CASE automatically. This ensures consistent, valid C# identifiers and saves you from typing codes manually. "Goblin Warrior" becomes GOBLIN_WARRIOR, "Shadow Drake (Elder)" becomes SHADOW_DRAKE_ELDER.

Design for the Ecosystem

If you plan to use the other forges, design your enemies with those systems in mind. Squads need groups of enemies that make sense together (a patrol, a boss + minions). Spawn tables need variety across difficulty tiers. Waves need different enemy types for early, mid, and late game. Scaling needs numeric properties with reasonable base values to multiply. Think about the whole pipeline.

Use Flags for Combat Properties

Flags like "Is Boss", "Is Flying", "Has Shield", and "Can Summon" are directly useful to Behavior Forge. Behavior rules can check flag values as conditions — for example, a rule that triggers "SUMMON_MINIONS" when "Can Summon" is true and HP is below 50%. Design your flags with behavior conditions in mind.

Keep Codes Unique and Descriptive

Enemy codes are your primary identifiers across the entire ecosystem. Make them descriptive enough to understand at a glance. Prefer GOBLIN_WARRIOR over ENEMY_01. The Squad, Spawn, Wave, Scaling, Behavior, and Faction forges all reference enemies by code, so clarity matters.

Batch Icon Assignment

Use the Set Icon bulk operation to assign icons efficiently. Select all enemies of a type (use the category filter to show only Undead, for example), click Select All, then click Set Icon to assign a placeholder sprite to all of them at once. Replace with final art later in the generated custom editor.

Regenerate Freely

You can regenerate your database at any time. The wizard overwrites existing files, so your generated code always matches your current wizard state. If you add enemies, change definitions, or update properties, just run Step 5 again. The only thing you lose is manual edits to generated files (which you should avoid).

The Enemy Forge is the foundation. Every enemy you create here becomes available to Squad Forge (as squad members), Spawn Forge (as spawn entries), Scaling Forge (as scaling targets), Wave Forge (as wave entries), Behavior Forge (as behavior subjects), and Faction Forge (as faction members). The other six forges all link to your Enemy Database and validate enemy codes against it. Define your enemies once, use them everywhere.