CharacterAttributes.cs (MonoBehaviour component — this is what you put on your GameObject)
CharacterAttributeData.cs + CharacterAttributeData.asset (a ScriptableObject data source, created automatically after compilation)
Note: the MonoBehaviour component is self-contained — your attributes are baked into it directly. The generated ScriptableObject is a separate, optional data container (it implements ISimpleAttributeDataSource) for systems that want to read attribute definitions without a scene component. You do not need to wire the asset into the component.
Step 2: Add Essential Attributes
Health (Vital)
Name: Health
Code: HP
Base Value: 100
Min/Max: 0/100
Can Regenerate:
Regen Rate: 5/sec
Regen Delay: 3 sec
Mana (Vital)
Name: Mana
Code: MP
Base Value: 50
Min/Max: 0/50
Can Regenerate:
Regen Rate: 2/sec
Regen Delay: 2 sec
Strength (Basic)
Name: Strength
Code: STR
Base Value: 10
Min/Max: 1/100
Can Regenerate:
Intelligence (Basic)
Name: Intelligence
Code: INT
Base Value: 10
Min/Max: 1/100
Can Regenerate:
Gold (Resource)
Name: Gold
Code: GLD (codes are 2-4 uppercase characters)
Base Value: 0
Min/Max: 0/999999
Force Integer:
Level (Resource)
Name: Level
Code: LVL
Base Value: 1
Min/Max: 1/100
Force Integer:
Step 3: Generate and Integrate
1Generate Code: Click "Generate" on the final step. Unity writes the scripts, compiles them, and creates the ScriptableObject asset automatically. The wizard keeps your data through the recompile — when it finishes, the "Finish" button becomes available.
2Create GameObject: Create a new GameObject named "Player"
3Add Component: Add the generated "Character Attributes" component. That is all — the component is self-contained, with your attributes already baked in. There is no Data Asset field to fill; it works the moment you add it.
Tip — save your JSON to re-edit later. Before you finish, use the Import/Export controls to save your configuration as a JSON file (named after your data, e.g. CharacterAttributes.json). Once code is generated the wizard does not reload your setup automatically — to make changes later, reopen the wizard and import that JSON. Keep it next to your generated files so you always know which JSON matches which system.
Using Your Attributes in Code
Basic Player Controller
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private CharacterAttributes attributes;
void Start()
{
// Get the generated attribute component
attributes = GetComponent<CharacterAttributes>();
// Subscribe to health events
attributes.health.OnReachedZero += HandleDeath;
attributes.health.OnValueChanged += UpdateHealthUI;
Debug.Log("Player initialized with " + attributes.health.totalValue + " HP");
}
void Update()
{
// Test damage with spacebar
if (Input.GetKeyDown(KeyCode.Space))
{
TakeDamage(10f);
}
// Test heal with H key
if (Input.GetKeyDown(KeyCode.H))
{
Heal(15f);
}
// Add gold with G key
if (Input.GetKeyDown(KeyCode.G))
{
AddGold(50);
}
}
public void TakeDamage(float amount)
{
// Use ModifyValue for gameplay changes (triggers regeneration)
attributes.health.ModifyValue(-amount);
Debug.Log($"Took {amount} damage. Health: {attributes.health.currentValue}/{attributes.health.totalValue}");
}
public void Heal(float amount)
{
attributes.health.ModifyValue(amount);
Debug.Log($"Healed {amount} HP. Health: {attributes.health.currentValue}/{attributes.health.totalValue}");
}
public void AddGold(int amount)
{
attributes.gold.ModifyValue(amount);
Debug.Log($"Gained {amount} gold. Total: {attributes.gold.GetIntValue()}");
}
public void LevelUp()
{
attributes.level.ModifyValue(1);
// Permanent stat increases
attributes.strength.baseValue += 2;
attributes.intelligence.baseValue += 1;
attributes.health.baseValue += 20;
// Fill health on level up
attributes.health.FillToMax();
Debug.Log($"Level up! Now level {attributes.level.GetIntValue()}");
}
private void HandleDeath()
{
Debug.Log("Player died!");
// Handle death logic
}
private void UpdateHealthUI()
{
// Update your health bar UI here
float healthPercent = attributes.health.GetPercentage();
// healthBar.fillAmount = healthPercent;
}
}
Simple Combat System
public class SimpleCombat : MonoBehaviour
{
private CharacterAttributes playerAttributes;
private CharacterAttributes enemyAttributes;
void Start()
{
playerAttributes = GameObject.FindWithTag("Player").GetComponent<CharacterAttributes>();
enemyAttributes = GetComponent<CharacterAttributes>();
}
public void PlayerAttack()
{
// Calculate damage using totalValue (includes all bonuses)
float damage = playerAttributes.strength.totalValue * 1.5f;
// Deal damage to enemy
enemyAttributes.health.ModifyValue(-damage);
Debug.Log($"Player deals {damage:F1} damage!");
if (enemyAttributes.health.IsAtMin())
{
HandleEnemyDeath();
}
}
public void EnemyAttack()
{
float damage = enemyAttributes.strength.totalValue * 1.2f;
playerAttributes.health.ModifyValue(-damage);
Debug.Log($"Enemy deals {damage:F1} damage!");
}
private void HandleEnemyDeath()
{
// Give rewards
playerAttributes.gold.ModifyValue(Random.Range(10, 50));
Debug.Log("Enemy defeated! Gold gained.");
Destroy(gameObject);
}
}
Key Features Demonstrated
Automatic Regeneration
Health and Mana automatically regenerate after taking damage. No manual coroutines needed!
// Just deal damage - regeneration is automatic
health.ModifyValue(-30f);
// System handles delay and healing automatically
Three-Tier Values
Each attribute has baseValue, formulaBonus, modifierBonus, and calculated totalValue.
// Always use totalValue for gameplay
float damage = strength.totalValue * multiplier;
// Modify baseValue for permanent changes
strength.baseValue += 5; // Upgrade
Type-Safe Access
Generated enums and properties provide compile-time safety and IntelliSense support.
// Direct property access
var health = attributes.health;
// Enum-based access
var attr = attributes.GetAttributeByEnum(CharacterAttributeEnum.Health);
Event System
Rich event system for UI updates and gameplay reactions.
public class CharacterAttributes : MonoBehaviour, ISimpleAttributeDataSource
{
[Header("Vital Attributes")]
public SimpleRuntimeAttribute health;
public SimpleRuntimeAttribute mana;
[Header("Basic Attributes")]
public SimpleRuntimeAttribute strength;
public SimpleRuntimeAttribute intelligence;
[Header("Resource Attributes")]
public SimpleRuntimeAttribute gold;
public SimpleRuntimeAttribute level;
// Automatic regeneration management
// Component-wide events
// ISimpleAttributeDataSource implementation
// Type-safe access methods
}
Testing Your Setup
Runtime Testing
The generated component includes testing tools accessible via the inspector:
Context Menu: Right-click component → "Log All Attributes"
Test Values: Set inspector values and use "Set Attribute Value" context menu
Debug Regeneration: Use "Debug Active Regeneration" to see active coroutines
Play Mode Testing
// Test in Update() or with UI buttons
void Update()
{
if (Input.GetKeyDown(KeyCode.D))
{
// Test damage
attributes.health.ModifyValue(-20f);
Debug.Log("Took damage - watch regeneration!");
}
if (Input.GetKeyDown(KeyCode.S))
{
// Test stat increase
attributes.strength.baseValue += 1;
Debug.Log($"STR increased to {attributes.strength.totalValue}");
}
}
Next Steps
Learn Runtime Usage
Master the three-tier value system and proper gameplay patterns.
Congratulations! You now have a fully functional attribute system. The examples above demonstrate the key patterns you'll use in your game. Remember to always use totalValue for gameplay logic and let the system handle regeneration automatically.