GHUD turns spreadsheet tabs into generated Unity classes. See below to learn how this data is organized.
This organization of spreadsheet tab data is the heart of the GHUD system.
When transferred from Google Sheets to Unity through GHUD,
Each tab becomes its own class
Each column becomes its own serialized variable in that class
Row 1 names the columns, and in turn the serialized variables
Row 2 defines variable types
Notice how custom enums can also be used as types. The style standard is for all enums to begin with capital 'E'.
Each row, Row 3 and below, becomes an instance of that class
Column A becomes an enum and is used to reference rows
Row 3 contains default values for the base instance of the class, denoted by enum value 0: "None"Â
Row 4 and below is intended for custom class instances
Each column contains the value for corresponding Row 1 variables.
Data in each cell must fit the type formatting set by the type in Row 2.
After the GHUD transfer, cell data is accessible through [TabName].[ColumnName]([RowID]);
Example:
float baseSpeed = Characters.Speed(_character);
where _character is an ECharacter
The GHUD system generates enums for each tab in the connected Google Sheets.
These enums serve as reference tools for the tab rows.
All generated enums are written to: Assets\GHUDatabase\GHUDScripts\Enums\Enums.cs
\âž
It is expected that Cell A3 in each data tab will have the value "None" to denote an empty/default value for each enum.
While it does not need to be added in the Sheets, each enum is concluded with a "COUNT" entry.
This COUNT element is included as general coding practice,
and also makes for a built in way to access the effective row count for each tab.
GHUD does however generate a property for accessing this value directly from each tab class:
[TabName].RowCount
Example:
Items.RowCount
which is equivalent to
(int)EItem.COUNT
Example Use-Case:
To calculate over all rows, you can use a for-loop to iterate over all or a subsection of rows in a tab.
Consider the following table:
[Tab].RowCount can be used as the upper limit of a loop that checks every generated row entry.
This can be used to perform query-like searches, or collect qualifying rows in a list.
The following method finds every item matching a specified EItemType, adds each match to a temporary list, and returns the completed list:
public static List<EItem> GetItemsOfType(EItemType itemType)
  {
  List<EItem> itemsOfType= new();
  for (int i = 0; i < Items.RowCount; i++)
    {
EItem item = (EItem)i;
EItemType iItemType = Items.ItemType(item);
    if (iItemType == itemType)
      itemsOfType.Add(item);
  }
return itemsOfType;
    }
You can optionally begin the loop at 1 so that it skips the None entry.
It stops before Items.RowCount, preventing it from reaching the generated EItem.COUNT value.
Then, to To retrieve all PowerUp items with the example GetItemsOfType above:
List<Item> powerUps = GetItemsOfType(EItemType.PowerUp);
Using the example Items table, the returned list would contain the data for:
EItem.Boots
EItem.Shield
Every generated tab exposes RowIteration, so you can use foreach instead of manually managing indices.Â
RowIteration automatically skips None and never reaches COUNT.
Using the same Items tab example:
public static List<EItem> GetItemsOfType(EItemType itemType)
{
  List<EItem> itemsOfType = new();
  foreach (EItem item in Items.RowIteration)
  {
    if (Items.ItemType(item) == itemType)
      itemsOfType.Add(item);
  }
  return itemsOfType;
}
Columns whose Row 1 values share a base name and end in digits generate grouped accessors. The base name must start with a capital letter and be a valid C# identifier. Use a unique numeric suffix for each column in the group.
The generated group name is the plural form of the base name: Value0, Value1
Becomes: Values
For another example, the F - K columns in this table generate the plural accessor: Chracters.Abilities
Note:
All columns in a group must use the same type in Row 2.
The plural name must be distinct from the base name. Do not use base names that cannot be pluralized into a different identifier, such as Sheep.
Only trailing digits create grouped columns. Names such as EnemyA, EnemyB, etc. remain normal columns.
Trailing digits must be integers greater than or equal to zero.
Indexed Access for Grouped Columns
Pass a row enum and suffix index to retrieve a single value from the group:
EAbility ability = Characters.Abilities(ECharacter.Mage, 2);Â
// ability == EAbility.Slow
The index corresponds to the column’s numeric suffix, not its physical position in the sheet.
For example, columns labeled as:
Value0, Value2, Value15, Value7
Would use the indices 0, 2, 7, and 15, where all other indices between 0 and 15 are set to the default value of the type.
This allows for code to accept a growing set of columns.
This is useful for when you don't know how many you'll need, or at what indices you'll need them.
An example of this might be perks gained at specific character levels.Â
Here the LevelPerks are not given every level, but you could add a column for a new LevelPerk at any time, and as long as the code is using the grouped reference "LevelPerks" instead of a hard-coded reference to each column, you could add new perks without needing to change the existing level up code.
See this example of how a method might check for perks, not knowing which exist in the sheets:
public static bool TryGetPerkForLevelUp(ECharacter character, int level, out EPerk perk)
{
perk = Characters.LevelPerks(character, level);
return perk != EPerk.None;
}
A negative index also returns the default value, but logs an error, since you shouldn't ever expect that to return a meaningful value.
Group Length
Each group generates a length property: [Tab].[ColumnGroup]LengthÂ
This length property is an int valued at one more than the group's largest suffix.
Using the Perks example above,Â
Characters.PerksLength == 11
Iteration
Use the generated Length property to safely iterate through every possible suffix.
In this example, there is a tab for Characters that includes grouped columns for SpellAtLevels,
As well as a tab for Spells that contains an MPCost Column.
To illustrate how grouped columns could be useful, imagine we wanted to generate a list of usable spells for a menu.
The following method returns every spell the character has learned by the given level and can afford with their remaining MP:
public static List<ESpell> GetAffordableSpells(ECharacter character, int level, int mp)
{
List<ESpell> affordableSpells = new();
int limit = Mathf.Min(level + 1, Characters.SpellAtLevelsLength);
for (int i = 0; i < limit; i++)
{
ESpell spell = Characters.SpellAtLevels(character, i);
if (spell == ESpell.None)
continue;
if(Spells.MPCost(spell) > mp)
continue;
if(!affordableSpells.Contains(spell))
affordableSpells.Add(spell);
}
return affordableSpells;
}
Remember, this also works for sparse groups because missing suffixes return the type’s default value.
Iterating with Group Arrays
Grouped columns also provide an array accessor containing every value in the group for a selected row. This array can be processed directly with a foreach loop.
The accessor for grouped arrays is an overload of the index-based accessor, just without the index parameter.
The following method is equivalent to the previous example, but uses the Group Array accessor instead of indexed access.
Note that Grouped Arrays are instantiated each time they are called, so it is important to cashe them before looping over them.
Vector and Color Helpers
Similar to Grouped Arrays, Grouped numeric columns can also generate vector and color accessors when the group contains the required number of columns.
You can see which kinds of Group methods will be generated in this table:
These generated helper methods function similarly to Unity's built-in Vector and Color methods.
The above data tab would generate the following helper methods within the generated Artifacts class:
Note:Â
The names of the Grouped Columns don't matter for these generated helprs. If the column types are float or int, so long as they have 2 to 4 columns, the matching Vector methods will be generated, and so long as they have 3 or 4 columns, Color methods will be generated.Â
While not shown here, Position also generates Color methods, and Aura also generates Vector3Int methods.
Vector Helper
In the above, the grouped Position columns generate a Vector3 accessor: PositionVector3
Vector3 position = Artifacts.PositionVector3(row);
That sets position with the lowest index column as x, the middle index column as y, and the highest index column as z.
Accessors for X, Y, and Z are added as well:
float x = Artifacts.PositionX(row); // == Artifacts.Position0(row) == Artifacts.Positions(row, 0)
float y = Artifacts.PositionY(row); // == Artifacts.Position1(row) == Artifacts.Positions(row, 1)
float z = Artifacts.PositionZ(row); // == Artifacts.Position2(row) == Artifacts.Positions(row, 2)
Color Helper
Likewise, the grouped Aura columns generate a Color accessor: AuraColor
Color aura = Artifacts.AuraColor(row);
That sets aura with the lowest index column as r, the second index column as g, the third index column as b, and 1 for a.
If there is a fourth color column, it uses it for a, but if there are only three columns, the color alpha will always be 1.
Accessors for R, G, B, and A are added as well:
float r = Artifacts.AuraR(row); // == Artifacts.Aura0(row) == Artifacts.Auras(row, 0)
float g = Artifacts.AuraG(row); // == Artifacts.Aura1(row) == Artifacts.Auras(row, 1)
float b = Artifacts.AuraB(row); // == Artifacts.Aura2(row) == Artifacts.Auras(row, 2)
float a = Artifacts.AuraA(row); // == Artifacts.Aura3(row) == Artifacts.Auras(row, 3) // or 1f
Color Value Interpretation
The type of the Grouped Columns change how the values are converted into a Unity Color.
If the type is float, the Color Helper methods will take the value as is, but clamp it between 0f and 1f.
If the type is int, the Color Helper methods effectively divide the value by 255, then clamp it between 0f and 1f.
In effect, if you want to enter a value between 0 and 1 for the RGBA, use a float; if you want 0 to 255, use an int.