Skip to main content

Inspector extensions

Guide

Add serialized project fields to all six Workbench modes through derived content types

Derive a project type from GameCard, GameDeck, GamePlayerUnit, GameEnemyUnit, GameStatus, or GameEncounter and add Unity-serialized fields, after compilation you can create the derived asset in a GCS database and edit its fields in Workbench or the normal Unity Inspector without changing GCS source

tip

The extension belongs to the derived type. Existing base-type assets do not acquire new fields, and GCS does not convert an existing GameCard or other base asset into a derived type automatically.

Download the Inspector extension samples

Download GCS-Samples.zip and extract it outside Unity, copy the required Runtime and Editor directories from Samples~/InspectorExtensions/ into the corresponding project assemblies

Keep the content type in a runtime assembly​

Place derived content classes and option providers in a player-available assembly that references TinyGiants.GCS.Runtime:

{
"name": "YourGame.GCS.Content",
"references": [
"TinyGiants.GCS.Runtime"
]
}

Use a top-level public, concrete, non-generic class and place it in a same-named .cs file so Unity can associate its MonoScript, types without that association or in Editor-only assemblies are excluded from the creation list

Workbench modeDerive fromStored byRuntime enumeration
CardGameCardGameCardDatabase.CardsGCSApi.Cards()
DeckGameDeckGameDeckDatabase.DecksGCSApi.Decks()
PlayerGamePlayerUnitGamePlayerUnitDatabase.PlayerUnitsGCSApi.PlayerUnits()
EnemyGameEnemyUnitGameEnemyUnitDatabase.EnemyUnitsGCSApi.EnemyUnits()
StatusGameStatusGameStatusDatabase.StatusesGCSApi.Statuses()
EncounterGameEncounterGameEncounterDatabase.EncountersGCSApi.Encounters()

The following class adds one RequiredLevel field to SampleCard

using TinyGiants.GCS.Runtime;

public sealed class SampleCard : GameCard
{
public int RequiredLevel;
}

After compilation, open Card mode and click + to choose a content type when several are available, the built-in GameCard appears first, followed by project types in display-name order, assembly names distinguish types with the same name

Select SampleCard to create a sub-asset in the current Card Database, its fields appear in both Workbench and the normal Unity Inspector, the other five modes create derived content through their respective GCS base types

Declare fields with Unity serialization rules​

Workbench enumerates the same visible serialized properties as Unity. A field appears when all of these conditions are satisfied:

  • It is an instance field, not a property, constant, or static field.

  • It is public or marked [SerializeField].

  • Its type is supported by Unity serialization.

  • It is not marked [HideInInspector].

Public primitives, enums, Unity object references, serializable classes and structs, arrays, and supported lists therefore work without a GCS-specific registration call. A C# property such as public int Level { get; set; } is not a serialized field and does not appear automatically.

using System;
using System.Collections.Generic;
using TinyGiants.GCS.Runtime;
using UnityEngine;

public sealed class SampleCard : GameCard
{
[GCSSection("Basic")]
[Min(1)]
[Tooltip("Minimum character level required to play this card.")]
public int RequiredLevel = 1;

[GCSSection("Visual")]
[Tooltip("Optional voice line used by project presentation code.")]
public AudioClip VoiceLine;

[GCSSection("Extension Data", "◆")]
[Range(0, 100)]
public int Power = 10;

[TextArea(2, 5)]
public string Lore;
}

Required Level appears in Basic, Voice Line in Visual, and Power and Lore in the Extension Data foldout with the ◆ icon

Control section placement with declaration order​

Sections follow their first appearance and fields follow declaration order within each section, reusing a section appends the field to that section, no Order value or alphabetical field sorting is used

DeclarationResult
[Header("Extension Data")]Selects or creates the Extension Data foldout starting with this field
[GCSSection("Basic")]Selects the existing Basic foldout, or creates it when it does not exist
A following field without either attributeContinues in the current section for the same declaring type
First field of a derived declaring type without either attributeEnters the Additional foldout
Another [Header] or [GCSSection]Changes the current section from that field onward
tip

Section names are case-sensitive, use Basic, Visual, or Behavior to reuse a built-in foldout, derived fields appear after existing fields and cannot be inserted between base-class fields

GCSSectionAttribute accepts either a glyph or a project-relative Assets/ or Packages/ icon path. A supplied icon overrides the built-in icon for a section with the same name:

[GCSSection("Progression", "★")]
public int RequiredLevel;

[GCSSection(
"Presentation",
"Assets/YourGame/GCS/Editor/Icons/presentation.png")]
public Color PresentationTint;

Asset paths can reference an imported Sprite or Texture2D. An invalid path falls back to the section's built-in icon and writes one warning for that path to the Console.

The following standard Unity attributes have explicit Workbench behavior:

AttributeWorkbench behavior
[Header]Starts or reuses a foldout and makes it current
[Tooltip]Applies the tooltip to the field row and control
[Space]Adds vertical space before the field
[Min]Clamps an edited integer or float to the declared minimum
[Range]Uses an integer or float slider with an input field
[TextArea], [Multiline]Uses a multiline text field
[Delayed]Commits string, integer, or float input after editing finishes
[InspectorName]Replaces the nicified field label or enum-option label
[SerializeField], [HideInInspector]Follow normal Unity visibility rules
tip

[GCSLabel] takes precedence over [InspectorName], registered controls and GCS attributes such as [GCSOptions] select controls before Unity CustomPropertyDrawer

Without a GCS control, Workbench uses Unity PropertyField, preserving project PropertyAttribute and CustomPropertyDrawer behavior, register a custom UI Toolkit control through Inspector controls

Supply single-choice options​

GCSOptionsAttribute applies to a serialized string. It accepts one of three sources:

[GCSOptions(
"Physical", "Fire", "Frost", "Lightning", "Arcane",
Editable = true)]
public string DamageSchool = "Physical";

[GCSOptions(typeof(SampleFactionOptionProvider))]
public string Faction = "alliance.kingdom";

Editable = false uses a selection-only field. Editable = true also accepts values outside the current option list. Workbench adds non-empty values stored in the same field across the current database to later dropdowns. Set EmptyValue to define the value stored when the field is cleared; the built-in Rarity, Enemy Tier, and Encounter Difficulty fields use Common, Normal, and Normal respectively.

In this sample, DamageSchool accepts either a preset or a new value. Faction stores the value returned by SampleFactionOptionProvider while showing the provider's label in the dropdown.

Built-in sourceValues supplied by GCS
NoneNo built-in single-choice values
CardTypeBuilt-in and authored card types
CardRarityBuilt-in and authored card rarities
CardTagAuthored card tags
CardKeywordRegistered CardTag IDs with their display labels
EnemyTierBuilt-in and authored enemy tiers
EncounterDifficultyBuilt-in and authored encounter difficulties

Dynamic providers implement the Runtime interface IGCSInspectorOptionProvider and provide a public parameterless constructor, Workbench creates the provider when rebuilding options and passes the current content object to GetOptions

using System.Collections.Generic;
using TinyGiants.GCS.Runtime;
using UnityEngine;

public sealed class SampleFactionOptionProvider : IGCSInspectorOptionProvider
{
public IEnumerable<GCSInspectorOption> GetOptions(Object target)
{
yield return new GCSInspectorOption(
"alliance.kingdom",
"Kingdom Alliance",
new[] { "World", "Alliance", "Kingdom" },
"Human kingdoms joined under one banner.");

yield return new GCSInspectorOption(
"horde.ashen",
"Ashen Horde",
new[] { "World", "Horde", "Ashen Marches" },
"Raiders from the volcanic frontier.");
}
}

Each option returned by the provider contains four values

Provider memberPurpose
ValueValue written to the serialized field
LabelText shown to the author in the dropdown
GroupPathExplicit page path with any depth, one segment can contain /
TooltipHover description for the option
tip

Workbench ignores case when removing duplicate Value entries, use stable, unique values

Supply multiple-choice values​

GCSMultiSelectAttribute applies to List<string> or string[]. It accepts the same fixed-option, provider-type, and built-in-source constructors as GCSOptionsAttribute.

[GCSMultiSelect(
"Humanoid", "Construct", "Undead", "Beast", "Quest",
Mode = GCSMultiSelectMode.Freeform)]
public List<string> Traits = new List<string>();

[GCSMultiSelect(
typeof(SampleSpellOptionProvider),
Mode = GCSMultiSelectMode.OptionsOnly)]
public List<string> GrantedSpells = new List<string>();
ModeAuthoring behavior
FreeformAccepts comma-delimited project values and also exposes the available values in a multiple-selection dropdown
OptionsOnlyShows ordered removable chips and only adds values through the supplied option dropdown

Traits uses Freeform, so it accepts typed values and dropdown selections. Workbench adds values already stored in SampleCard.Traits to the dropdown for later use.

GrantedSpells uses OptionsOnly, so it accepts only values returned by SampleSpellOptionProvider and displays selected values as removable chips.

Select other GCS content​

GCSSubAssetAttribute replaces an ordinary ScriptableObject reference with the GCS content picker. The picker searches registered databases, accepts derived instances of the requested base type, and can render a compact row or a summary card.

[GCSSubAsset(
typeof(SampleStatus),
Style = GCSSubAssetStyle.Card)]
public SampleStatus SignatureStatus;

SampleCard.SignatureStatus accepts only SampleStatus assets. Use GCSSubAssetListAttribute on an array or list when a field stores multiple GCS content references:

[GCSSubAssetList(typeof(GameEnemyUnit), Required = true)]
public List<GameEnemyUnit> Reinforcements = new List<GameEnemyUnit>();
tip

Required shows an inline warning when the reference is empty, or when a required list has no entries. It does not add an Issue Badge automatically. Register a project validator when the missing reference must participate in Workbench validation.

These attributes select existing content; they do not create an embedded ScriptableObject inside the current content asset.

Flatten serializable settings with GCSInline​

GCSInlineAttribute exposes the direct visible children of a serializable class or struct in the current section without adding another nested property box.

using System;
using TinyGiants.GCS.Runtime;
using UnityEngine;

[Serializable]
public sealed class SampleCardStats
{
[Min(0)]
public int BaseDamage = 6;

[Range(0f, 1f)]
public float CriticalChance = 0.1f;
}

public sealed class SampleCard : GameCard
{
[GCSInline]
public SampleCardStats Stats = new SampleCardStats();
}
tip

Inspector initializes a null field when its type is concrete and has a parameterless constructor, inline data must follow Unity serialization rules

FlowGraph Editor requires a direct field path on the content object, so declare GCSFlowGraphAttribute on the content class rather than inside inline data

Inline does not change serialized paths, validators use nameof(SampleCard.RequiredLevel) for a top-level field or a full path such as Stats.BaseDamage for a nested field, Workbench resolves PropertyPath before falling back to FieldLabel, see Inspector controls

Add a FlowGraph editing field​

GCSFlowGraphAttribute applies to a direct GameEffectFlowGraph field and adds Edit FlowGraph plus a static summary.

public sealed class ProjectCard : GameCard
{
[GCSFlowGraph(GCSFlowGraphSummary.Entries)]
public GameEffectFlowGraph AlternateBehavior = new GameEffectFlowGraph();
}

Entries summarizes entry nodes. EnemyIntents summarizes patterns, leaf intents, and action nodes.

tip

Declare graph fields directly on a derived Card, Status, or Enemy, FlowGraph Editor checks nodes using that content type

The battle runtime automatically executes only the built-in Behavior, which is also the only graph checked by built-in Workbench validation, execute additional graphs such as AlternateBehavior through project code and register project validators

Reuse other built-in field controls​

The following Runtime attributes select built-in GCS field controls

AttributeRequired fieldResult
[GCSAssetName]stringEdits the value and renames the content sub-asset when the value is non-empty
[GCSDescription]stringUses the GCS description editor with token and semantic-reference support
[GCSLabel("Label")]Any serialized fieldOverrides the displayed label
[GCSSegmentedBool("Off", "On")]boolUses a two-segment Boolean picker
[GCSDeckEntries]GameDeck.EntriesUses the card count picker and deck summary UI
[GCSSection("Name", "Glyph or asset path")]Any serialized fieldSelects a named Workbench section from this field onward and optionally supplies its icon

Attributes applied to an incompatible field type fall back to the normal serialized-property control instead of converting the stored data.

Read the same typed data at runtime​

Databases and GCSApi expose the base types, so use normal C# type checks or OfType<T>() to reach project fields:

using System.Linq;
using TinyGiants.GCS.Runtime;

SampleCard firstSampleCard = GCSApi.Cards()
.OfType<SampleCard>()
.FirstOrDefault();

if (firstSampleCard != null)
{
int requiredLevel = firstSampleCard.RequiredLevel;
}

if (cardInstance.GetActiveCard() is SampleCard activeSampleCard)
{
PlayVoice(activeSampleCard.VoiceLine);
}

if (unitState.Source is SampleEnemy sampleEnemy)
{
ApplyEnemyRules(sampleEnemy);
}
tip

The same pattern works with GCSApi.Decks(), PlayerUnits(), EnemyUnits(), Statuses(), and Encounters(). These methods enumerate active databases and allocate a fresh list, so cache a result that is read repeatedly.

Derived fields are persistent authored definition data. Per-card-instance values, temporary unit values, and other battle state belong on the corresponding runtime state or in supported battle variables rather than on the ScriptableObject definition.

Preserve data through authoring operations​

Workbench saves derived fields through Unity serialization, Duplicate and Paste preserve the concrete type and all serialized fields while assigning a unique name and new GCS identity, pasting requires a type compatible with the current mode

Workbench Clipboard stores object references in the current Unity Editor domain, copy again after restarting the editor or a Domain Reload

Built-in field edits, creation, duplication, deletion, and database membership changes support Unity Undo, custom controls also support Undo/Redo when saving through GCSInspectorFieldContext.Modify, see Inspector controls

Preserve data references when changing saved types or fields

  • Use [FormerlySerializedAs] when renaming a serialized field.

  • Keep derived type names and assembly ownership stable, or provide an explicit asset migration.

  • Do not expect duplication to convert a base asset into a derived type; it preserves the source object's concrete type.

  • Create a project migration tool when existing base assets must become a new derived type.

tip

List, Preview, Used By, card-face Prefabs, and runtime presentation continue to read built-in GCS fields, read derived data in the corresponding UI or presentation code when it needs to appear there