View Format: Multi-Page Single Page
All Docs Documentation Version: Base (Free) Influence System Modifier System

Runtime Usage Guide

Complete guide for using generated attribute systems in your game with correct patterns and best practices.

Critical: Three-Tier Value System

Understanding this is crucial for proper gameplay programming!

Every attribute uses a three-tier value architecture:

totalValue = baseValue + formulaBonus + modifierBonus

baseValue

  • Persistent attribute value
  • Saved with character data
  • Modified by equipment/upgrades
  • Used as input for formulas

formulaBonus

  • Calculated from influence formulas
  • Automatically managed by Ultimate's Influence System
  • DO NOT modify manually
  • Prevents accumulation issues

modifierBonus

  • Temporary effects (buffs/debuffs)
  • Manually managed by gameplay code
  • Not affected by formula system
  • Cleared when effects expire

totalValue

  • Final calculated result
  • Use for ALL gameplay logic
  • Automatically updated
  • Read-only property

Correct Usage Patterns

Reading Values for Gameplay Logic

// ALWAYS use totalValue for gameplay calculations float damage = strength.totalValue * weaponMultiplier; float defense = armor.totalValue; bool canAfford = gold.totalValue >= itemCost; bool canCastSpell = mana.currentValue >= spellCost; // For Vitals, use currentValue vs totalValue if (health.currentValue <= 0) { HandleDeath(); } // Check percentage for UI bars float healthPercent = health.GetPercentage(); // 0-1 range healthBar.fillAmount = healthPercent;

Modifying Attribute Values

// ModifyValue() - For gameplay changes (triggers regeneration) health.ModifyValue(-damageAmount); // Take damage mana.ModifyValue(-spellCost); // Cast spell gold.ModifyValue(questReward); // Add gold experience.ModifyValue(xpGain); // Gain XP // SetValue() - For administrative changes (no regeneration) health.SetValue(100f); // Load save data health.FillToMax(); // Admin heal/cheat level.SetValue(savedLevel); // Load character level // baseValue - For permanent upgrades strength.baseValue += 2; // Permanent stat increase health.baseValue += 50; // Equipment bonus (permanent)
Key Rule: Use ModifyValue() for gameplay changes that should trigger regeneration. Use SetValue() for administrative changes that shouldn't.

Common Mistakes to Avoid

These patterns will cause bugs or missing functionality!
// WRONG - Using baseValue for gameplay (misses bonuses!) float damage = strength.baseValue * weaponMultiplier; // Missing formula + modifier bonuses! // WRONG - Using old 'value' property (doesn't exist) float health = healthAttr.value; // Compile error - use totalValue or currentValue // WRONG - Manually modifying formulaBonus strength.formulaBonus += 10; // Will be overwritten by Ultimate's Influence System! // WRONG - Using GetCurrentValue() for non-Vitals float str = strength.GetCurrentValue(); // Use totalValue instead // WRONG - Direct assignment without proper methods health.currentValue = 100; // Use SetValue() or ModifyValue() instead

Automatic Regeneration System

Regeneration is completely automatic for Vital attributes with canRegenerate = true.

How It Works

1 Damage Occurs: health.ModifyValue(-30f)
2 Delay Starts: Waits for regenerationDelay seconds
3 Regeneration Begins: Heals at regenerationRate per second
4 Auto Stops: When reaching max or taking more damage

Configuration

// Enable and configure regeneration health.canRegenerate = true; health.regenerationRate = 2.5f; // 2.5 HP per second health.regenerationDelay = 3f; // 3 second delay after damage // Check regeneration state bool isRegenerating = health.IsRegenerating; // NO MANUAL CALLS NEEDED - IT'S AUTOMATIC!
Best Practice: Let the system handle regeneration automatically. Just use ModifyValue() for damage and healing.

Practical Examples

Combat System

public class CombatSystem : MonoBehaviour { [Header("Cached References")] private SimpleRuntimeAttribute health; private SimpleRuntimeAttribute mana; private SimpleRuntimeAttribute strength; private SimpleRuntimeAttribute defense; void Start() { var attributes = GetComponent<CharacterAttributes>(); // Cache references for performance health = attributes.health; mana = attributes.mana; strength = attributes.strength; defense = attributes.defense; } public void PerformAttack(CombatSystem target) { // Calculate damage using totalValue (includes all bonuses) float baseDamage = strength.totalValue * 2f; float targetDefense = target.defense.totalValue; float finalDamage = Mathf.Max(1f, baseDamage - targetDefense); // Deal damage - triggers regeneration automatically target.health.ModifyValue(-finalDamage); Debug.Log($"Dealt {finalDamage:F1} damage"); // Check for death if (target.health.IsAtMin()) { HandleDeath(target); } } public void CastSpell(float manaCost, float spellPower) { // Check if can cast if (mana.currentValue < manaCost) { Debug.Log("Not enough mana!"); return; } // Consume mana mana.ModifyValue(-manaCost); // Spell effects based on totalValue float finalPower = spellPower * intelligence.totalValue; // ... perform spell } private void HandleDeath(CombatSystem target) { Debug.Log($"{target.name} died!"); // Death logic } }

Buff/Debuff System

public class EffectSystem : MonoBehaviour { private CharacterAttributes attributes; private List<ActiveEffect> activeEffects = new List<ActiveEffect>(); void Start() { attributes = GetComponent<CharacterAttributes>(); } public void ApplyBuff(string attributeName, float amount, float duration) { var attribute = attributes.GetRuntimeAttribute(attributeName); if (attribute == null) return; // Apply temporary modifier attribute.modifierBonus += amount; // Track for removal var effect = new ActiveEffect(attribute, amount, duration); activeEffects.Add(effect); Debug.Log($"Applied +{amount} {attributeName} for {duration}s"); } void Update() { // Update active effects for (int i = activeEffects.Count - 1; i >= 0; i--) { var effect = activeEffects[i]; effect.timeLeft -= Time.deltaTime; if (effect.timeLeft <= 0) { // Remove effect effect.attribute.modifierBonus -= effect.amount; activeEffects.RemoveAt(i); Debug.Log($"Effect expired on {effect.attribute.attributeName}"); } } } } [System.Serializable] public class ActiveEffect { public SimpleRuntimeAttribute attribute; public float amount; public float timeLeft; public ActiveEffect(SimpleRuntimeAttribute attr, float amt, float duration) { attribute = attr; amount = amt; timeLeft = duration; } }

UI Update System

public class AttributeUI : MonoBehaviour { [Header("UI Elements")] public Slider healthBar; public Text healthText; public Text strengthText; public Text statusText; [Header("Performance")] public float updateInterval = 0.1f; // Limit UI update frequency private CharacterAttributes attributes; private float lastUIUpdate; void Start() { attributes = GetComponent<CharacterAttributes>(); // Subscribe to events for immediate updates attributes.health.OnValueChanged += UpdateHealthUI; attributes.strength.OnValueChanged += UpdateStrengthUI; } void Update() { // Throttled UI updates for performance if (Time.time - lastUIUpdate >= updateInterval) { UpdateAllUI(); lastUIUpdate = Time.time; } } private void UpdateHealthUI() { var health = attributes.health; // Update bar and text healthBar.value = health.GetPercentage(); healthText.text = $"{health.currentValue:F0}/{health.totalValue:F0}"; // Show regeneration status if (health.IsRegenerating) { statusText.text = "Regenerating..."; statusText.color = Color.green; } else if (health.IsAtMin()) { statusText.text = "Critical!"; statusText.color = Color.red; } else { statusText.text = ""; } } private void UpdateStrengthUI() { var str = attributes.strength; // Show value with bonus breakdown string text = str.totalValue.ToString("F0"); if (str.formulaBonus != 0 || str.modifierBonus != 0) { text += " ("; text += str.baseValue.ToString("F0"); if (str.formulaBonus != 0) text += $"+{str.formulaBonus:F0}"; if (str.modifierBonus != 0) text += $"+{str.modifierBonus:F0}"; text += ")"; } strengthText.text = $"STR: {text}"; } private void UpdateAllUI() { // Clear dirty flags after update foreach (var attr in attributes.GetAllRuntimeAttributes().Values) { if (attr.IsDirty) { attr.ClearDirtyFlag(); } } } }

Save/Load System

[System.Serializable] public class SaveData { public Dictionary<string, float> baseValues = new Dictionary<string, float>(); public Dictionary<string, float> currentValues = new Dictionary<string, float>(); } public class SaveSystem : MonoBehaviour { public SaveData CreateSaveData() { var data = new SaveData(); var attributes = GetComponent<CharacterAttributes>(); foreach (var kvp in attributes.GetAllRuntimeAttributes()) { var attr = kvp.Value; // Save base values (persistent) data.baseValues[attr.attributeName] = attr.baseValue; // Save current values for Vitals if (attr.behaviorType == SimpleBehaviorType.Vital) { data.currentValues[attr.attributeName] = attr.currentValue; } } return data; } public void LoadSaveData(SaveData data) { var attributes = GetComponent<CharacterAttributes>(); // Load base values foreach (var kvp in data.baseValues) { var attr = attributes.GetRuntimeAttribute(kvp.Key); if (attr != null) { attr.baseValue = kvp.Value; // Use SetValue() to avoid regeneration } } // Load current values for Vitals foreach (var kvp in data.currentValues) { var attr = attributes.GetRuntimeAttribute(kvp.Key); if (attr != null && attr.behaviorType == SimpleBehaviorType.Vital) { attr.SetValue(kvp.Value); // Use SetValue() to avoid triggering regen } } // Recalculate formulas if using Ultimate's Influence System var orchestrator = GetComponent<InfluenceSystem>(); if (orchestrator != null) { orchestrator.RecalculateAll(); } Debug.Log("Game loaded successfully!"); } }

Debug and Testing

Inspector Testing

Generated components include testing tools:

// Context menu methods available in editor [ContextMenu("Log All Attributes")] [ContextMenu("Debug Active Regeneration")] [ContextMenu("Set Attribute Value")] // Uses inspector test values

Runtime Debugging

// Check attribute state public void DebugAttribute(string name) { var attr = attributes.GetRuntimeAttribute(name); if (attr != null) { Debug.Log($"{attr.attributeName}:"); Debug.Log($" Base: {attr.baseValue}"); Debug.Log($" Formula: {attr.formulaBonus}"); Debug.Log($" Modifier: {attr.modifierBonus}"); Debug.Log($" Total: {attr.totalValue}"); if (attr.behaviorType == SimpleBehaviorType.Vital) { Debug.Log($" Current: {attr.currentValue}"); Debug.Log($" Regenerating: {attr.IsRegenerating}"); } } } // Monitor events void Start() { attributes.OnAnyAttributeChanged += (name, value) => { Debug.Log($"Attribute {name} changed to {value}"); }; }

Performance Tips

Cache References

// Cache attribute references private SimpleRuntimeAttribute health; void Start() { health = attributes.health; } // Don't lookup every time void Update() { var health = attributes.GetRuntimeAttribute("Health"); }

Throttle UI Updates

// Update UI at intervals public float uiUpdateInterval = 0.1f; private float lastUpdate; void Update() { if (Time.time - lastUpdate > uiUpdateInterval) { UpdateUI(); lastUpdate = Time.time; } }

Use Events Wisely

// Subscribe to specific attributes health.OnValueChanged += UpdateHealthBar; // Don't subscribe to everything attributes.OnAnyAttributeChanged += UpdateAllUI;

Batch Operations

// Batch multiple changes health.baseValue += 50; strength.baseValue += 10; // Formulas recalculate automatically // Separate modifications ModifyMultipleAttributes();