Numeric Component
Note
The content of this document about the numeric component mostly comes from the numeric component section of ET Book.
Introduction
The numeric component is an important functional component in the ET framework, providing a complete numeric value management solution. The main features include:
- Basic arithmetic operations for numeric values
- Event listening and distribution for numeric value changes
Design Approach
Note
In complex game systems (such as MMORPG, MOBA), skill systems need a flexible numeric structure for support. A character may include multiple attributes: movement speed, strength, rage, energy, focus, magic, health, etc. These attributes interact with each other and can be affected by buffs (absolute value increases, percentage increases, etc.).
Traditional Solutions and Limitations
class Numeric
{
public int Hp;
public int MaxHp;
public int Speed;
public int Energy;
public int MaxEnergy;
public int Mp;
public int MaxMp;
}
This design has the following problems:
- Different classes may use different attribute systems (mages use magic value, rogues use energy value)
- Inheritance schemes lead to complex class hierarchies
- Attribute calculations need to consider multiple influencing factors
For example, speed calculation needs to consider:
class Numeric
{
public int Speed; // Final speed value
public int SpeedInit; // Initial speed value
public int SpeedAdd; // Speed increase value
public int SpeedPct; // Speed percentage increase value
}
Calculation formula:
Speed = (SpeedInit + SpeedAdd) * (100 + SpeedPct) / 100
The main problems with this approach:
- Bulky class structure
- Repeated calculation formulas
- Inflexible attribute configuration
ET Framework Solution
The ET framework uses a Key-Value form to store numeric attributes:
public enum NumericType
{
Max = 10000,
Speed = 1000,
SpeedBase = Speed * 10 + 1,
SpeedAdd = Speed * 10 + 2,
SpeedPct = Speed * 10 + 3,
SpeedFinalAdd = Speed * 10 + 4,
SpeedFinalPct = Speed * 10 + 5,
Hp = 1001,
HpBase = Hp * 10 + 1,
MaxHp = 1002,
MaxHpBase = MaxHp * 10 + 1,
MaxHpAdd = MaxHp * 10 + 2,
MaxHpPct = MaxHp * 10 + 3,
MaxHpFinalAdd = MaxHp * 10 + 4,
MaxHpFinalPct = MaxHp * 10 + 5,
}
Core implementation:
public class NumericComponent: Component
{
public readonly Dictionary<int, int> NumericDic = new Dictionary<int, int>();
public void Update(NumericType numericType)
{
if (numericType > NumericType.Max)
{
return;
}
int final = (int) numericType / 10;
int bas = final * 10 + 1;
int add = final * 10 + 2;
int pct = final * 10 + 3;
int finalAdd = final * 10 + 4;
int finalPct = final * 10 + 5;
// final = (((base + add) * (100 + pct) / 100) + finalAdd) * (100 + finalPct) / 100;
this.NumericDic[final] = ((this.GetByKey(bas) + this.GetByKey(add)) * (100 + this.GetByKey(pct)) / 100 + this.GetByKey(finalAdd)) * (100 + this.GetByKey(finalPct)) / 100;
Game.EventSystem.Run(EventIdType.NumbericChange, this.Entity.Id, numericType, final);
}
}
Advantages:
- Flexible attribute management: Different classes can have different attributes
- Unified calculation formula: All attributes use the same calculation logic
- Event system support: Attribute changes can trigger corresponding events
Practical Application Example
Monitor HP value changes:
namespace MH
{
[NumericWatcher(SceneType.Main, NumericType.Hp)]
public class NumericWatcher_HpChange : INumericWatcher
{
public void Run(Unit unit, NumbericChange args)
{
if (unit == null || unit.IsDisposed)
return;
var numericComponent = unit.GetComponent<NumericComponent>();
var currentHp = numericComponent[NumericType.Hp];
var maxHp = numericComponent[NumericType.MaxHp];
if (currentHp > maxHp)
numericComponent.SetNoEvent(NumericType.Hp, maxHp);
if (currentHp < 0)
numericComponent.SetNoEvent(NumericType.Hp, 0);
var hpBarComponent = unit.GetComponent<HpBarComponent>();
hpBarComponent.SetHpSliderHandler(unit);
}
}
}
Technical Support
Get Help
- 💬 Join QQ group for discussion
(ET Framework Group)
: 474643097 - ⭐ Follow the project on GitHub for the latest updates