A lightweight, pure C# framework for managing entity statistics and mathematical modifiers (buffs/debuffs) in video games.
Designed with data-driven architecture and maximum portability in mind, b3agz.Stats is engine-agnostic. Whether you are using Unity, Godot, MonoGame, or building a custom engine, this library SHOULD drop right in without issue (.NET version permitting).
-
Pure C#: Zero external dependencies.
-
Composition: Attach a StatBlock to any entity (Player, Enemy, Item) without locking yourself into a rigid class hierarchy.
-
Beautiful Syntax: Clean operator overloads allow for you to use the Stat class like a numerical primative (eg; Stat health = 100).
-
Fail-Fast Safety: Explicit error handling for bad data (e.g., throws exceptions on divide-by-zero effects) prevents silent "ghost bugs" during gameplay.
-
Template Generation: Set up new StatBlock instances using an array of tuples, allowing for stat templates that can be easily applied.
-
Forgiving Lookups: StatBlock identifiers are case-insensitive by default, saving on JSON serialization and string-matching headaches.
Because this is a pure C# library, installation is entirely up to your project structure. You can drag and drop the files directly into your project or add this repository as a Git submodule.
The Submodule: Add this repository as a Git submodule to keep it updated with future releases.
Use the StatBlock class to give any object a collection of stats.
using b3agz.Stats;
public class Player {
public StatBlock Stats { get; } = new();
public Player() {
Stats.Add("Health", 100f);
Stats.Add("Mana", 50f);
Stats.Add("Armor", 10f);
}
}The StatBlock includes an indexer, allowing you to treat your stats exactly like an array or dictionary, while handling all the float conversions under the hood.
public void TakeDamage(float incomingDamage) {
float mitigatedDamage = incomingDamage - Stats["Armor"];
Stats["Health"] -= mitigatedDamage;
}If you have data defined elsewhere (like a JSON file or a ScriptableObject), you can initialize a StatBlock instantly using the CreateFromTemplate method.
(string, float)[] warriorTemplate = new (string, float)[] {
("Health", 200f),
("Mana", 0f),
("Stamina", 150f)
};
Player genericWarrior = new();
genericWarrior.Stats.CreateFromTemplate(warriorTemplate);Effects represent mathematical modifications (buffs or debuffs). They are completely self-contained and immutable.
// Create a debuff that divides Armor by 2
Effect sunder = new("Sunder Armor", "Armor", 2f, EffectOperation.Divide);
// Apply it directly to the stat
sunder.ApplyTo(player.Stats["Armor"]);-
Iteration: StatBlock implements IEnumerable, meaning you can foreach over it directly, or use LINQ to query stats (e.g.,
stats.Where(s => s.Value < 0)). -
Serialisation: The EffectOperation is stored as an Enum, ensuring that effects can be easily serialised and deserialised from save files.
-
Operators: While Effect.ApplyTo() mutates the stat in place for performance, the standard arithmetic operators (
+, -, /, *) will always return a new instance of a Stat object to prevent accidental memory mutation. -
Unit Tests More for my own practice than a real need for it, the project includes unit tests to ensure basic functionality.
Designed and maintained by John Bullock (aka b3agz).
This library is MIT License, which means you can do essentially whatever you want with it—use it in free games, commercial games, game jams, or weird art projects.
While you are under absolutely no obligation to do so, if b3agz.Stats helps you ship a game, I would absolutely love to hear about it! Drop me a message on YouTube or Twitch so I can check out what you built.