HuaYe Studio rdququ
The Numeric System is a powerful and flexible 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.
- Extensible Architecture: Support for custom modifiers, conditional modifiers, and modifier priorities.
- Thread-Safe Operations: Optional thread-safe wrapper for multi-threaded scenarios.
- Serialization Support: Built-in support for modifier serialization/deserialization.
This major release adds advanced features while maintaining 100% backward compatibility:
New Features:
- ✨ Modifier Priority System - Control modifier application order with fine-grained priorities
- ✨ Conditional Modifiers - Apply modifiers based on dynamic conditions using predicate conditions
- ✨ Serialization Support - Serialize and deserialize modifiers for save/load functionality
- ✨ Thread-Safe Wrapper - Thread-safe
Numericoperations for multi-threaded environments - ✨ Performance Benchmarks - Comprehensive benchmark suite using BenchmarkDotNet
- ✨ Diagnostic Tools - Enhanced debugging and diagnostic capabilities
- ✨ Fluent API - Rich extension methods and builder patterns for better developer experience
Improvements:
- 🚀 Increased test coverage from 117 to 129 tests (100% pass rate)
- 🚀 Enhanced error messages and validation
- 🚀 Improved caching mechanism
- 🚀 Better performance for modifier queries
Documentation:
- 📚 Complete XML documentation coverage
- 📚 Performance benchmark documentation
- 📚 Architecture refactoring plan
For detailed information, see changelogs/1.2.0.md
- Fixed Division by Zero: Added validation in
FractionNumericModifierconstructor - Fixed Multi-Fraction Modifier Bug: Redesigned the
Applymethod - Added Overflow Protection: Prevented silent value corruption
- Improved CustomNumericModifier Safety: Enhanced null checking
For detailed information, see changelogs/1.1.0_CN.md
- Introduction
- Table of Contents
- Changelog
- Download and Deployment
- Getting Started
- Advanced Features
- API Reference
- File Path Description
- License
git clone git@github.com:dlqw/NumericSystem.gitnpm i numericsystemTo 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()); // 1146914344You can use operators or explicit methods:
// Using operators (recommended)
health += 20;
health -= 10;
// Using explicit methods
health.AddModifier(new AdditionNumericModifier(20));
health.RemoveModifier(new AdditionNumericModifier(10));// Percentage increase
health *= (150, FractionType.Increase); // +50%
// Percentage override
health *= (200, FractionType.Override); // ×2.0
// Remove modifiers
health /= (150, FractionType.Increase);Numeric health = 100;
health += 20.3f;
Debug.Log(health.FinalValue); // 120 (int)
Debug.Log(health.FinalValueF); // 120.3f (float)// Create named modifier with tags
health += (20, new[] { "Equipment" }, "Armor", 1);
health *= (120, FractionType.Override, new[] { "Equipment" }, "ArmorUpgrade", 1);
health *= (50, FractionType.Increase, new[] { "Buff" }, "StrengthBoost", 1);Custom modifiers are invoked at the end of the calculation pipeline and can enforce specific constraints.
Numeric health = 100;
// Clamp health between 0 and 150
health.ClampMax(150, "MaxHealthCap");
health.ClampMin(0, "MinHealthCap");
// Or use custom function
Func<int, int> healthLimit = value => Mathf.Clamp(value, 0, 150);
health.AddModifier(new CustomNumericModifier(healthLimit));Control the order in which modifiers are applied using priorities:
var health = new Numeric(100);
// Add modifiers with different priorities
health += (50, new[] { "Base" }, "RaceBonus", 1, ModifierPriority.Base); // 100
health += (30, new[] { "Base" }, "ClassBonus", 1, ModifierPriority.Base); // 100
health += (50, new[] { "Equipment" }, "Armor", 1, ModifierPriority.Equipment); // 200
health += (30, new[] { "Buff" }, "Strength", 1, ModifierPriority.Buff); // 300
health *= (50, FractionType.Increase, Array.Empty<string>(), "Multiplier", 1, ModifierPriority.Multiplier); // 500
// Application order: Base → Equipment → Buff → MultiplierPriority Levels:
Critical(0) - Highest priorityBase(100) - Base attributesEquipment(200) - Equipment modifiersBuff(300) - Buff/Debuff effectsSkill(400) - Skill bonusesDefault(400) - Default priorityMultiplier(500) - Percentage modifiersClamp(600) - Constraint modifiers
Apply modifiers based on dynamic conditions:
var health = new Numeric(100);
// Condition: Health below 30%
var lowHpCondition = ConditionBuilder.Where(h => h.FinalValue < 30);
var emergencyShield = ConditionalNumericModifier.ConditionalAdd(
lowHpCondition,
50,
"EmergencyShield"
);
health.AddModifier(emergencyShield);
// Complex conditions with AND/OR/NOT
var complexCondition = ConditionBuilder
.Where(h => h.FinalValue < 50)
.And(h => h.GetAddModifierValueByTag(new[] { "Buff" }) < 1000000)
.Build();
health.AddConditionalModifier(
complexCondition,
new AdditionNumericModifier(30, Array.Empty<string>(), "ComplexBonus")
);Save and load modifier states:
var health = new Numeric(100);
health += 50;
health *= (150, FractionType.Increase);
// Serialize
var data = health.Serialize();
// Deserialize
var restored = data.Deserialize();
Assert.Equal(health.FinalValue, restored.FinalValue);Supported Modifiers:
- ✅
AdditionNumericModifier - ✅
FractionNumericModifier ⚠️ CustomNumericModifier(contains delegates)⚠️ ConditionalNumericModifier(contains delegates)
For multi-threaded scenarios, use ThreadSafeNumeric:
var safeHealth = new ThreadSafeNumeric(100);
// Thread-safe operations
safeHealth += 50;
safeHealth.AddModifier(new FractionNumericModifier(150, FractionType.Increase));
// Thread-safe read
var value = safeHealth.FinalValue;
// Thread-safe operations with callbacks
safeHealth.Read(numeric =>
{
Debug.Log($"Health: {numeric.FinalValue}");
Debug.Log($"Modifiers: {numeric.GetAllModifiers().Count}");
});
safeHealth.Write(numeric =>
{
numeric += 20;
});Run benchmarks to measure performance:
cd src
dotnet run -c Release --project NumericSystem.Tests -- --filter *NumericBenchmarks*Benchmark categories:
- Basic - Creation, modification, calculation
- Scalability - Large modifier counts (10, 100, 1000)
- Complex - Real-world scenarios
- Fraction - Fraction modifier performance
- Query - Lookup operations
See src/NumericSystem.Tests/Benchmarks/README.md for details.
// Builder pattern
var health = Numeric.Build(100, builder =>
{
builder.AddEquipment(50, "Armor");
builder.AddBuff(30, "Strength");
builder.BoostBase(150, "BaseBoost");
builder.WithMaxLimit(500, "MaxHP");
});
// Extension methods
health.AddEquipment(20, "Helmet");
health.AddBuff(10, "Potion");
health.ClampMax(300);
// Conditional extensions
health.AddIf(h => h.FinalValue < 100, 50, "EmergencyHeal");
health.MultiplyIf(
ConditionBuilder.Where(h => h.FinalValue > 200).Build(),
150,
FractionType.Increase
);var health = new Numeric(100);
health += 50;
health *= (150, FractionType.Increase);
// Get modifier statistics
var stats = health.GetModifierStats();
foreach (var stat in stats)
{
Debug.Log($"{stat.Key}: {stat.Value}");
}
// Dump detailed information
health.Dump("Player Health");
// Check cache status
Debug.Log(health.GetCacheStatus());NumericSystem
├── .gitignore
├── README.md
├── README_CN.md
├── CHANGELOG.md
├── ARCHITECTURE_REFACTORING_PLAN.md
├── package.json
├── src
│ └── NumericSystem
│ ├── Core/ # Core abstractions
│ ├── Chain/ # Responsibility chain pattern
│ ├── Serialization/ # Serialization support
│ ├── FixedPoint.cs
│ ├── Numeric.cs
│ ├── NumericModifier*.cs # Modifier implementations
│ ├── NumericExtensions.cs # Extension methods
│ ├── ThreadSafeNumeric.cs # Thread-safe wrapper
│ └── DiagnosticHelper.cs # Diagnostic tools
│ └── NumericSystem.Tests
│ ├── Benchmarks/ # Performance benchmarks
│ ├── *Tests.cs # Unit tests
└── LICENSEThe project includes comprehensive unit tests:
cd src
dotnet testTest Coverage:
- 129 tests (100% pass rate)
- All major features covered
- Edge cases and error handling
This project is released under the MIT License