Item attributes and upgrade levels

Hi

TL;DR
Is there some way I could enforce specific items upgrade levels attributes to match defined items attributes - I don’t quite see how I could easily solve this with (for example) custom editor or property drawers…

I’ve bumped into this “pattern” several times and decided to ask if there is some relatively simple way to do this better. Let’s say I have an item definition with stats, which are attributes of some item - like damage, speed etc:

// In item
public List<Stat> stats;

Stat itself looks like this, StatType is just an enum (Damage, Speed):

public class Stat
{
    public StatType statType;
    public int amount;
}

So item current stats could be Damage 5 and Speed 3.

Then elsewhere in my project, I would have a list of upgrades, where I just manually set the addedValues that user gets (when upgrading to level) to match the item’s defined attributes - i.e. if user’s item has two attributes, I’ll add two items in each upgrade level…

public class UpgradeLevel
{
   public List<int> addedValues;
}

So this will work, but will be easy to fill incorrectly.

Any ideas how to make this less error-prone? Edit: meaning that I might use some completely different way to organize upgrades if that is needed.

I think by making an excel (wide vision of values) and then just export it to a xml file and use it in your project. And after that just make a check by level method, to see if you have big gaps between values at different levels.

Another way to do it is by making a simple algorithm to fill those values.
Like:

float GetDamageBasedOnLevel(int lvl, float baseDamage)
{
  return baseDamage + someFormula(lvl);
}

Hi @adi7b9

“I think by making an excel (wide vision of values) and then just export it to a xml file and use it in your project.”

Thanks for the idea - that is something completely opposite of what I’ve tried so far, and something I could try. Although I don’t like XML, I probably would use JSON or CSV instead… also, I find working inside Unity more familiar, so I’m first thinking about some solution that would work inside Editor before resorting to using external data files. But using formula to generate level data instead of manually created values is worth considering.

Look for scriptable objects architectures in unity

HI @neoshaman

“Look for scriptable objects architectures in unity”

Already using those - and that is pretty generic advice - don’t you think :slight_smile: ? I don’t just see how to arrange my data… the issue is with base data vs upgrades - what I was trying to ask is how to make the process less error prone - I’m kinda repeating myself, but:

Lets say I have two specific stats in one particular item type. How would I setup something with scriptable objects, so that upgrade level data would only allow those same stats, but I could enter values for those… that is pretty much what I asked - because I just can’t figure it out… I can use it as it is, but the potential for incorrect entries remains - it is not a big problem, I just thought someone might have better ideas for how the data can be organized.

Your stat has a current value. Why not have it contain an array with the value of all upgrade levels and the stat just remembers which level it is at and fetches the correct value from the array. Then you can fill the array from an editor, an xml file (mods), procedurally/calculation. But this only works for plain upgrades. If there are other (de)buffs you will need a more sophisticated system to take care of the current state.

So I would join this:

    public class Stat
    {
        public StatType statType;
        public int amount;
    }

    public class UpgradeLevel
    {
       public List<int> addedValues;
    }

into something like (Pseudocode):

    public class Stat
    {
        public StatType statType;
        public int currentLevel;

       public List<int> levelValues; // consider an array here

       public int Value {get {return levelValues[currentLevel]; } }
    }

Then some boilerplate code to upgrade level, check within array bounds. Have a max level ect. .

There is a great talk from Ryan Hipple about using Scriptable Objects for “exotic” purposes. He shows a generic value class which might (or might not) be usefull for your purpose. If you have not seen this I recommend to take the time.

1 Like

You missed the architecture part would have lead you to this (and a few other articles):

https://www.youtube.com/watch?v=raQ3iHhE_Kk

Unite Austin 2017 - Game Architecture with Scriptable Objects

Hi again @neoshaman
“You missed the architecture part would have lead you to this (and a few other articles):”

I’ve watched that video several times carefully, made notes and still don’t see any particular thing that might be helpful for me in this particular case. Can you be more specific?

Only proper example I can find in architecture part is the use of scriptable objects as containers for variables. So I can drop a SO into some field and fill the fields. This would prevent messing a particular value combination for sure. But if I have 10x different things, out of which I would make various combinations, would I make scriptable object for each combination, and for each level value? I think this would be more wasteful than writing some custom editor and/or property drawers, which I’ve used (or tried to use) earlier.

I don’t see any other use for ScriptableObjects (in this case) - that is why I asked - I was looking for some insights. Maybe I will watch the video again at some point.

I told you about excel table and i understand that you don’t want to have external files. Ok.
But you can make a little pure c# code and take this file and translate it into a real C# code.

Like:

//This is your table (i represented it like a table), but it can be a xml, json, csv or txt
/*------------------------------------------------
| Level | Att | Def | Air | Fire | Earth | Water |
--------------------------------------------------
| 1     | 1   | 0   | 0   | 2    | 3     | 0     |
--------------------------------------------------
| 2     | 1   | 2   | 1   | 2    | 3     | 0     |
--------------------------------------------------
| 3     | 1   | 4   | 0   | 2    | 3     | 3     |
--------------------------------------------------
| 4     | 3   | 3   | 0   | 2    | 5     | 0     |
--------------------------------------------------*/

//And your final class should be like (your little c# code generates the next code)

List<cTable> list = new List<cTable>();

list.Add(new cTable{ Level = 1, Att = 1, Def = 0, Air = 0, Fire = 2, Earth = 3, Water = 0 });
list.Add(new cTable{ Level = 2, Att = 1, Def = 2, Air = 1, Fire = 2, Earth = 3, Water = 0 });
list.Add(new cTable{ Level = 3, Att = 1, Def = 4, Air = 0, Fire = 2, Earth = 3, Water = 3 });
list.Add(new cTable{ Level = 4, Att = 3, Def = 3, Air = 0, Fire = 2, Earth = 5, Water = 0 });

And use this generated c# class into your project :slight_smile:

I recommend to stay away from enumerations. While they might appear to be a very straightforward solution, you will quickly hit walls with them. Generic example, imagine associating more data with a stat. You would then have to somehow wrap the enumeration value with the associated data, which then would lead to the need of indexing structures to get access to them and so on.

Take a look on this, for instance: https://discussions.unity.com/t/767848/10 The issue in this thread is about something else, but later on the discussion shifted to stats, be it item or unit stats.

Basically, I see two different approaches:

  • Implicit items with a generic approach - writing a container-wrapper-hybrid class that represents all kinds of (basic) items, which you then fill with the corresponding modifiers and so on. This approach is very handy if you plan to have highly configurable items, with many many different combinations of item modifiers.
  • Explicit items knowing exactly what an item is aka “unique” items - This is good if you don’t have many items, allowing you to create or generate the items, with each item being an unique asset.

I don’t quite get the essence of your issue.

You have items. Items have attributes and levels. Now item have upgrade levels and you would like to upgrade the attributes as well? Let’s say an item is level 1 with an attribute “Damage” level 1, then an item of level 2 should have an attribute of level 2 as well?

I would solve this by explicitely storing the upgrade levels in a separate object, in a table. Each attribute has such a table, with entries for every possible item level and your item references said attribute and when looking up the value, it looks it up in the table for the corresponding level.

In the Unreal Engine 4, for instance, there exists DataTable for this, which is more or less an Excel table linked into the engine. This is a very good solution, because it gives you control over individual values, be it manually typed in or auto-generated by a formula. The only issue with this one is that if not optimized, it loads the whole table although only one row is needed to be checked. Might need a real database solution in Unity that is optimized for such purposes.