Skip to content

A flexible, generalized numerical system for computing game values.

License

Notifications You must be signed in to change notification settings

dlqw/numerickit-wf-unity

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Numeric System

HuaYe Studio rdququ

Static Badge Static Badge

Introduction

The Numeric System is a toolset designed to address the numerical needs of gameplay, aiming to provide a simple and efficient solution for handling combat system calculations.

  • Event Store-based Numeric Change Tracking: Ensures traceability, easy self-verification, and security of original data.
  • Fixed-point Arithmetic: Guarantees numerical consistency across platforms and devices, enhancing network synchronization reliability.
  • Simple Syntax: Supports the addition of integers, floating points, fractions, or percentages to numerical values using addition, multiplication, or custom modifiers.

Table of Contents

Download and Deployment

Get from GitHub

git clone [email protected]:dlqw/NumericSystem.git

Get from npm

npm i numericsystem

Getting Started

Creating the First Numeric

To use the tool, you need to reference WFramework.CoreGameDevKit.NumericSystem;

using WFramework.CoreGameDevKit.NumericSystem;

You can manually create a Numeric object and pass an integer or floating point number to its constructor.

Numeric health = new Numeric(100);

This value (100 in the example above) acts as the base value of the Numeric object and is read-only. You can retrieve its value using GetOriginValue(). To change this base value, create a new Numeric object.

// Get original/base value
var healthBasicValue = health.GetOriginValue();

// Change the base value => Create a new Numeric object
health = new Numeric(200);

Alternatively, you can assign an integer or floating-point number to a Numeric object to create a new one.

Numeric health = 100;
Debug.Log(health.GetHashCode()); // 402183648

health = 100.67f;
Debug.Log(health.GetHashCode()); // 1146914344

Note that in this case, health now points to a newly allocated Numeric object.

Attaching AdditionNumericModifier to Numeric

You can manually create an addition modifier using AdditionNumericModifier. The internal value of the modifier is immutable and can be accessed through StoreValue.

AdditionNumericModifier strongBuff = new AdditionNumericModifier(20);
var buffValue = strongBuff.StoreValue;

Alternatively, you can quickly create it using integers or floating-point numbers.

AdditionNumericModifier strongBuff = 20f;

To attach a NumericModifier to a Numeric object, use the AddModifier method or the addition operator. The following examples illustrate valid usage:

// Success
health.AddModifier(strongBuff);
health = health + 20;
health += 20;
health = health + strongBuff;
health += strongBuff;

// Error
health.AddModifier(20);

Removing a modifier follows similar syntax, using the RemoveModifier method or the subtraction operator.

Important: Avoid mixing these two:

health += -20;
health -= 20;

The first example attaches a modifier with a value of -20, while the second removes a modifier with a value of 20.

Important: Do not attach the same modifier multiple times without removing it first. You can use overloaded addition and subtraction operators to create temporary modifier objects.

Important: Avoid mixing integers and floating-point numbers in the same Numeric object. The Numeric System is designed to work with either int -> int or float -> float, depending on your needs.

Getting the Final Value of Numeric

You can obtain the final value by calling FinalValue or FinalValueF, which return the result as an integer or floating-point number, respectively.

Numeric health = 100;

health += 20.3f;

Debug.Log(health.FinalValue);
Debug.Log(health.FinalValueF);

Using Multiplication Modifiers(FractionNumericModifier)

Multiplication modifiers are slightly more complex, but you can still construct, attach, or remove them in a similar way.

health.AddModifier(new FractionNumericModifier(1, 2, FractionNumericModifier.FractionType.Increase));
health *= (200, FractionNumericModifier.FractionType.Override);

health.RemoveModifier(new FractionNumericModifier(1, 2, FractionNumericModifier.FractionType.Increase));
health /= (200, FractionNumericModifier.FractionType.Override);

You can build multiplication modifiers using (numerator:int, denominator:int, type:FractionNumericModifier.FractionType). You can either directly create the object or use C# tuple syntax, which offers clarity or convenience, depending on your preference.

Here are two types of multiplication modifiers:

public enum FractionType
{
    Override, // Replace the original value
    Increase, // Increment based on the original value
}

An increment modifier will add a new value to the original, while a replace modifier directly sets the new value. For example, if the original value is 100 and the increment modifier is 2/1, the result will be 300, whereas the replace modifier will yield 200.

The formulas are as follows:

$ Increase = (1 + (\frac{numerator}{denominator})) \times Input $ $ Override = (\frac{numerator}{denominator}) \times Input$

Modifiers' Name, Tags, and Count

All modifiers have overloaded constructors that include Name:string, Tags:string[], and Count:int. The Name serves as a unique identifier. If you attach a modifier with the same Name, it will accumulate its Count and replace the previous one.

Anonymous modifiers are named "DEFAULT MODIFIER."

The purpose of Tags will be detailed in the next section.

Count is an internal counter for stacking modifiers.

Using Tags to Partially Modify Base Values

Tags apply to addition modifiers, multiplication modifiers, and the base value of a Numeric object. Addition modifiers with specific Tags indicate which multiplication modifiers will affect them. Similarly, Numeric base values have a default tag of "SELF".

Multiplication modifiers' Tags define which addition modifiers or base values they will affect.

Numeric health = 100;

health += (20, new[] { "Equipment" }, "Armor", 1);
Debug.Log(health.FinalValue); // 120
health *= (120, FractionType.Override, new[] { "Equipment" }, "Armor Upgrade", 1);
Debug.Log(health.FinalValue); // 124
health *= (50, FractionType.Increase, new[] { NumericModifierConfig.TagSelf }, "Upgrade", 1);
Debug.Log(health.FinalValue); // 174

Using Custom Modifiers

Custom modifiers are invoked at the end of the calculation pipeline and can enforce specific constraints. For example, to limit a player's health, you can create a Func<int,int> or Func<float,float> that takes the target value as input and returns the constrained result.

Numeric health = 100;

Func<int, int> healthLimit = value => Mathf.Clamp(value, 0, 150);

health.AddModifier(new CustomNumericModifier(healthLimit));
health -= healthLimit;
health += new CustomNumericModifier(healthLimit);

In this example, the player's health is constrained between 0 and 150. The creation and attachment of CustomNumericModifier are similar to other modifiers, including implicit type conversions and operator overloads (+, -).

Important: Do not attach conflicting conditions to the same Numeric object, as the final outcome may be undefined.

File Path Description

NumericSystem
├── .gitignore
├── README.md
├── package.json
├── src
│   └── NumericSystem
│       ├── FixedPoint.cs
│       ├── Numeric.cs
│       └── NumericModifier.cs
└── LICENSE

Author

rdququ

License

This project is released under the MIT License

About

A flexible, generalized numerical system for computing game values.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages