Skip to content

ooonush/Stats

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Stats

Most games include a stat system, whether it's a simple health scale with a fixed value or a more complex RPG system with the ability to pump and many interconnected stats that are calculated over the course of the game. The purpose of the Stats assets is to make it easier for Unity developers to add, maintain, and extend this kind of functionality to their games.

This version is in preview stage and may be unstable, it is not recommended to use it in production. If you encounter any bugs, please make a bug report. You can also suggest features that you would like to see in the Asset.

Multiplayer Support

Stats has support for multiplayer using FishNet. For this you need to install the FishyStats addon.

Installation

Requires a version of Unity 2022.3 or newer. You can add https://github.com/ooonush/Stats.git?path=Assets/Plugins/Stats to Package Manager

image

image

If you want to set a target version, Stats uses the ..* release tag so you can specify a version like #0.0.2-pre.1. For example https://github.com/ooonush/Stats.git?path=Assets/Plugins/Stats#0.0.2-pre.1.

Basic Concepts

Stats

Stats are specific numeric traits of a character. These values can be calculated by formula, depend on other stats, change and modified during the game.

For example, a stat can be a strength, which depends on the level of the character or the weapon he is wielding.

To create a Stat, right click on the project window and go to Create → Stats → Stat.

image

The value of the stat starts with the Base value. If you specify the Formula value, then the final value of the stat will be calculated by this formula.

For example, when creating a strength stat, Base can be set to 15. And the Formula will increase this value every level.

The Type value is required for Stat. If it is not specified, the stat will not be taken into account.

To create a Stat Type, right click on the project window and go to Create → Stats → StatType.

image image

For example, I want to make a different strength value for Archer and Warrior. To do this, first I create a StatType Strength, then I create a Warrior Strength, Archer Strength stats and specify Base and Formula values in them. The Type value for both stats is the same - Strength.

Attributes

Attributes are specific numerical traits of a character that have min and max values.

To create a Attribute, right click on the project window and go to Create → Stats → AttributeType.

image

  • MinValue is the minimum value of the attribute.
  • MaxValue is the type of stat that will be the maximum value. Unlike MinValue, MaxValue can change during the game.
  • Start Percent is the percent at which the attribute starts.

A typical example of an attribute in games is character Health. In this case, we need to specify MinValue = 0 and create new Stat and StatType for MaxHealth. Start Percent = 1, will mean that the character's original Health is full.

Traits Classes

TraitsClass contains all Stats and Attributes of a character. You can have several character classes with different traits (stats and attributes).

To create a TraitsClass, right click on the project window and go to Create → Stats → TraitsClass.

For example, an Archer class might look like this:

image

When you have many trait classes, you may forget to add some Stat to one of them (e.g. Strength). Therefore, it is recommended that you use custom trait classes. To do this, you must create a script and inherit TraitsClassBase to control the traits of the character using code. For example, this is what a custom trait class might look like:

[CreateAssetMenu(menuName = "Character Class", fileName = "Character Class")]
public class CharacterClass : TraitsClassBase
{
    [SerializeField] private StatItem _maxHealth;
    [SerializeField] private StatItem _strength;

    [SerializeField] private AttributeItem _health;
}

This way, you explicitly specify in the code what stats your character has and never forget to add a Stat or Attribute to traits class.

image

Traits

Traits is the component that binds the TraitsClass Asset to the GameObject.

image

After adding a component, you can specify the desired traits class through the Inspector:

image

After starting the game, the Traits component will be ready to use. The Inspector will show the values of all current Stats and Attributes. So you can see and change character traits as you play.

image

Note that the Stat value changes for the Base value. And in the header of the stat the Value to which the formula and modifiers are applied is specified.

Formulas

The formulas are currently under development, but you can always create your own implementation by inheriting the StatFormula class.

[CreateAssetMenu(menuName = "Stats/Level Formula")]
public class LevelFormula : StatFormula
{
    public StatType Level;
    [Range(0f, 1f)] public float PercentForLevel;

    public override float Calculate(IRuntimeStat stat, ITraits traits)
    {
        IRuntimeStat level = traits.RuntimeStats.Get(Level);
        return Mathf.Round(stat.Base + stat.Base * (level.Value - 1) * PercentForLevel);
    }
}

An example of a formula that adds a percentage to the status depending on the level.

Modifiers

In addition to formulas, often games require you to change your character's stats based on equipment. Modifiers can be added and removed at any time. For example, if we wanted to increase a character's MaxHealth when they put on Armor, we would add the modifier.

Modifiers are Constant or Percent numeric values that are added to Stats at runtime. The Constant modifier adds a fixed value to the base value of the stat. And Percent, adds a specified percentage to the base value of the stat.

Modifiers are only added to stats during runtime using code.

Status Effects

Status effects are similar to modifiers, but can perform complex logic. For example, status effects can be used to make a player affected by poison, which takes away health for a certain period of time.

To create a status effect, you need to inherit the StatusEffect class.

Scripting

Custom TraitsClass

Custom traits class is useful in large projects where you need to keep track of multiple trait classes. When you create a custom TraitsClass, you create a kind of contract that allows you to customize characters and add new ones more easily. It's also useful if you want to have control over traits in your code.

To create a custom trait class, you need to inherit TraitsClassBase:

[CreateAssetMenu(menuName = "Character Class", fileName = "Character Class")]
public class CharacterClass : TraitsClassBase
{
    [SerializeField] private StatItem _maxHealth;
    [SerializeField] private StatItem _strength;

    [SerializeField] private AttributeItem _health;
}

You can also create lists or arrays of stats and attributes. A good practice is a combination of the two:

[CreateAssetMenu(menuName = "Character Class", fileName = "Character Class")]
public class CharacterClass : TraitsClassBase
{
    [SerializeField] private StatItem _maxHealth;
    [SerializeField] private StatItem _strength;

    [SerializeField] private AttributeItem _health;

    [SerializeField] private List<StatItem> _additionalStats;
    [SerializeField] private AttributeItem[] _additionalAttributes;
}

Traits

Initialization

To use the Traits component, you must specify TraitsClass. This can be done with the help of the Inspector, or in the code, using the Initialize() method. The best practice is to call Initialize() in the Awake() method of another class, or immediately after calling Instantiate().

public class Character : MonoBehaviour
{
    public Traits Traits;
    public CharacterClass CharacterClass; // This is the custom TraitsClass

    private void Awake()
    {
        Traits.Initialize(CharacterClass);
    }
}
public class CharacterSpawner : MonoBehaviour
{
    public Traits CharacterPrefab;

    // CharacterClass is the custom TraitsClass
    public Traits CreateCharacter(CharacterClass characterClass)
    {
        Traits characterTraits = Instantiate(CharacterPrefab, transform.position, Quaternion.identity);
        characterTraits.Initialize(characterClass);

        return characterTraits;
    }
}

RuntimeStats and RuntimeAttributes

Traits has RuntimeStats and RuntimeAttributes, which can be used to get specific stats and attributes. To do this, call the Get() method in RuntimeStats or RuntimeAttributes and pass a StatType or AttributeType object there, respectively.

public class Character : MonoBehaviour
{
    [SerializeField] private Traits _traits;

    [SerializeField] private StatType _strengthType;
    [SerializeField] private AttributeType _healthType;

    private RuntimeStat _strength;
    private RuntimeAttribute _health;

    private void Awake()
    {
        _strength = _traits.RuntimeStats.Get(_strengthType); // Getting the Strength RuntimeStat
        _health = _traits.RuntimeAttributes.Get(_healthType); // Getting the Health RuntimeAttribute
    }
}

The Get() method returns RuntimeStat and RuntimeAttribute. These are objects that contain information such as the current stat value, the attribute value, stat modifiers, and much more.

RuntimeStat

Value - The current value of the stat, including formulas and modifiers. Base - The base value of the stat, without formulas and modifiers. You can change this value. ModifiersValue - The value of the modifiers that are added to the Base value after the Formulas.

RuntimeAttribute

MinValue - The min value of the attribute. MaxValue - The max value of the attribute. Is the Value of RuntimeStat Value - The current value of the attribute. You can change this value. Ratio - The Value to MaxValue ratio.

Modifiers

You can add modifiers when a character puts on some armor, and remove a modifier when a character removes an armor.

private RuntimeStat _maxHealth;

public void PutArmor(int protection)
{
    _maxHealth.AddModifier(ModifierType.Constant, protection);
}

public void RemoveArmor(int protection)
{
    _maxHealth.RemoveModifier(ModifierType.Constant, protection);
}