BakingSheet

repository·master·Indexed 19 days ago

https://github.com/cathei/bakingsheet

A datasheet management system for C# and Unity that allows developers to define data schemas in code and use spreadsheets (Excel, Google Sheets, CSV, JSON) as the source of truth. It functions as an ORM for datasheets, bridging designer-friendly spreadsheet editing with strongly-typed C# objects. Version 4.1.3 supports various column types including primitives, enums, collections, and cross-sheet references.

Tokens
10.3K
Snippets
28
Records
41
Agent score
66%

What's inside BakingSheet

  1. Use List columns for arrays

    master

    To represent a simple array in a spreadsheet, use a column type that implements IList<T>.

    BakingSheet supports two header styles for lists:

    1. Flat header: ColumnName:Index (e.g., Monsters:1, Monsters:2).
    2. Split header: A parent column name with sub-columns for indices (e.g., Monsters as parent, 1, 2, 3 as children).

    Note: Spreadsheet indices are 1-based.

    public class DungeonSheet : Sheet<DungeonSheet.Row>
    {
        public class Row : SheetRow
        {
            public string Name { get; private set; }
    
            // Use any supported type as a list
            public List<MonsterSheet.Reference> Monsters { get; private set; }
            public List<ConsumableSheet.Reference> Loots { get; private set; }
        }
    }
  2. Using Cross-Sheet References

    master

    To add reliability and type safety when one sheet refers to another, use the Sheet<TKey, TRow>.Reference type.

    Requirements:

    1. The reference type must be defined as OtherSheet.Reference within your row class.
    2. Both sheets must be properties on the same SheetContainer class.

    Benefits:

    • The type is serialized as the TKey (the ID).
    • It verifies that a row with the same ID exists in the target sheet during loading.
    • It provides an error message if the reference is missing.
    • You can access the target row directly via the .Ref property.
    public class HeroSheet : Sheet<HeroSheet.Row>
    {
        public class Row : SheetRowArray<Elem>
        {
            // ...
        }
    
        public class Elem : SheetRowElem
        {
            public float StatMultiplier { get; private set; }
            public int RequiredExp { get; private set; }
            // Reference to ConsumableSheet
            public ConsumableSheet.Reference RequiredMaterial { get; private set; }
        }
    }
    
    public class SheetContainer : SheetContainerBase
    {
        public HeroSheet Heroes { get; private set; }
        public ConsumableSheet Consumables { get; private set; }
    }
    
    // Usage:
    var heroRow = sheetContainer.Heroes["HERO001"];
    var consumableRow = heroRow.GetLevel(5).RequiredMaterial.Ref;
    logger.LogInformation(consumableRow.Name);
  3. Using Row Arrays for 2D structures

    master

    When your spreadsheet data has a 2D structure (e.g., a main row with multiple sub-rows/elements), use SheetRowArray<TElem>.

    In the spreadsheet, rows without an Id are automatically considered part of the previous row. This allows you to visually group sub-elements under a parent row.

    SheetRowArray<TElem> provides:

    • IEnumerable<TElem> implementation
    • Indexer this[int]
    • Count property

    If you need to extend a list vertically without pairing them as Elem objects, consider using VerticalList<T>, though splitting the sheet is generally recommended.

    public class HeroSheet : Sheet<HeroSheet.Row>
    {
        public class Row : SheetRowArray<Elem>
        {
            public string Name { get; private set; }
            public int Strength { get; private set; }
            public int Inteligence { get; private set; }
            public int Vitality { get; private set; }
    
            public Elem GetLevel(int level)
            {
                // Level 1 would be index 0
                return this[level - 1];
            }
    
            public int MaxLevel => Count;
        }
    
        public class Elem : SheetRowElem
        {
            public float StatMultiplier { get; private set; }
            public int RequiredExp { get; private set; }
            public string RequiredMaterial { get; private set; }
        }
    }
  4. How BakingSheet works: The Core Concept

    master

    BakingSheet acts as an ORM (Object-Relational Mapping) for datasheets. It allows you to define your data schema using C# classes, enabling designers to work in familiar spreadsheet tools (Excel, Google Sheets, CSV, JSON) while programmers interact with strongly-typed C# objects.

    The Workflow:

    1. Define Schema: Programmers create C# classes representing the datasheet.
    2. Design: Designers fill out the spreadsheet using standard functions and features.
    3. Convert: An edit-time script converts the spreadsheet into a serialized format (like JSON) and validates the data against your C# schema.
    4. Runtime: The runtime script reads the serialized data into C# instances.
    5. Use: Business logic uses the C# objects directly without manual parsing.
  5. Use DirectAssetPath for Unity Assets folder assets

    master

    Use DirectAssetPath to reference any asset located anywhere under the Unity Assets folder.

    Important Note: The result of calling Get<T> on a DirectAssetPath is only valid when using a ScriptableObject reference or an importer. For more details, refer to the [Converting with ScriptableObject] documentation.

  6. Use Nested Type columns for complex structures

    master

    For complex, hierarchical data structures, you can map a column to a custom struct or class. The data in the spreadsheet (whether using flat or split headers) will be mapped to the properties of that type.

    public struct SituationText
    {
        public string Greeting { get; private set; }
        public string Purchasing { get; private set; }
        public string Leaving { get; private set; }
    }
    
    public class NpcSheet : Sheet<NpcSheet.Row>
    {
        public class Row : SheetRow
        {
            public string Name { get; private set; }
    
            // Maps spreadsheet columns to the properties of SituationText
            public SituationText Texts { get; private set; }
        }
    }
  7. Use Converters to import/export datasheets

    master

    Converters allow you to import data from various sources or export processed data to specific formats. You can use heavy converters (like Excel or Google Sheets) during a pre-build/editor step to convert data into a lightweight format (like JSON) for production use.

    Supported converters include:

    • Excel: Import only (BakingSheet.Converters.Excel)
    • Google Sheets: Import only (BakingSheet.Converters.Google)
    • CSV: Import and Export (BakingSheet.Converters.Csv)
    • JSON: Import and Export (BakingSheet.Converters.Json)
    • ScriptableObject: Import and Export (Read-only) (Unity only)
    // Example: Baking sheets from an Excel file
    var logger = new UnityLogger();
    var sheetContainer = new SheetContainer(logger);
    var excelConverter = new ExcelSheetConverter("Excel/Files/Path");
    
    await sheetContainer.Bake(excelConverter);
  8. Use Dictionary columns for key-based access

    master

    To map specific keys to values in a spreadsheet, use a column type that implements IDictionary<TKey, TValue>. This is useful for mapping enums or strings to specific data points.

    public enum Situation
    {
        Greeting,
        Purchasing,
        Leaving
    }
    
    public class NpcSheet : Sheet<NpcSheet.Row>
    {
        public class Row : SheetRow
        {
            public string Name { get; private set; }
    
            // Maps the Situation enum to a string value
            public Dictionary<Situation, string> Texts { get; private set; }
        }
    }
  9. Use AddressablePath for Addressable Assets

    master

    Use AddressablePath to reference assets via their Addressable system addresses.

    Requirement: Methods like LoadAsync<T> or Get<T> are only available and accessible if the Addressable Assets package is installed in your Unity project.

  10. Use ResourcePath for Unity Resources folder assets

    master

    Use ResourcePath to reference assets located within a Unity Resources folder.

    Rules for ResourcePath:

    • The path must be relative to the Resources folder.
    • Do not include the file extension.
    • To reference sub-assets, use square brackets: My/Asset/Path[SubAssetName].
  11. Compare ScriptableObject vs JSON converters

    master

    Choose the converter based on your workflow requirements:

    FeatureScriptableObject ConverterJSON Converter
    Primary Use CaseUnity Editor integration & AddressablesRaw data management & network transmission
    Data FormatUnity ScriptableObject assetsRaw JSON strings
    WorkflowInspect/edit via Unity InspectorSend directly over the wire or manage as text files