Attributes.

The core guts of an RPG. These values help drive the game, and many of the core systems. For this game, they will help shape, as you would expect, the combat system. That’s the easy part. The hardest part will be the implementation. And if I break it down any further, it’s not really the actual implementation, it’s how I implement it.

I always tell people that when I develop something new, whether it’s a new feature, app, system, etc., 95% of my time is spent on figuring out how to do it, the other 5% is actually coding it. I’m sure it’s the same with other developers.

As I started designing my Attribute system, I only had a couple things that had to be true:

  • Make is as generic as possible
  • Make it a dynamically loaded data driven system

The first point is that I didn’t want to have a single class for every Attribute. I just wanted to have a few classes that would support all Attributes. The second point is that, with a lot of other systems I’ve created (i.e. Tilemap system), I want it to be something that is loaded from data files. There are many benefits to doing this, but I think the ones players would care about is that these files could be exposed to them, allowing them to mod them at will. Want to tweak an attribute on a certain player unit. Go for it. Want to create your own custom Attribute? Knock yourself out. What could you use that custom Attribute on, you ask? Well, I’m not ready to talk about those things yet 🙂

Anyway, with those constraints defined, I set about creating the system. After a lot of brainstorming (remember the 95%!), I sort of settled on the following ideas, which as you can expect, I’d label as version 0.0.1.

As of now, I only need 4 core classes:


AttributeEnums.cs

using UnityEngine;
using System.Collections;

public class AttributeEnums {
	public enum AttributeType {
		EXPERIENCE,
		LEVEL,
		HIT_POINTS,
		ABILITY_POINTS,
		MOVEMENT,
		SPEED,
	}
}

This just defines all attributes in a way that they can be referenced by type anywhere in the code.

Attribute.cs

namespace RoseOfEternity {
	
	public class Attribute {

		private string _name;
		private string _shortName;
		private string _toolTip;
		private float _minimumValue;
		private float _currentValue;

		/// <summary>
		/// Initializes a new instance of the <see cref="RoseOfEternity.Attribute"/> class.
		/// </summary>
		/// <param name="name">Name.</param>
		/// <param name="shortName">Short name.</param>
		/// <param name="toolTip">Tool tip.</param>
		/// <param name="currentValue">Current value.</param>
		/// <param name="minimumValue">Minimum value.</param>
		/// <param name="maximumValue">Maximum value.</param>
		public Attribute(string name, string shortName, string toolTip, float currentValue, float minimumValue, float maximumValue) {
			_name = name;
			_shortName = shortName;
			_toolTip = toolTip;
			_minimumValue = minimumValue;
			MaximumValue = maximumValue;
			CurrentValue = currentValue;
		}

		/// <summary>
		/// Gets or sets the current value. When setting, value will be clamped to the min/max.
		/// </summary>
		/// <value>The current value.</value>
		public float CurrentValue { get { return _currentValue; } set { _currentValue = Mathf.Clamp (value, _minimumValue, MaximumValue); }}

		/// <summary>
		/// Gets or sets the maximum value.
		/// </summary>
		/// <value>The maximum value.</value>
		public float MaximumValue { get; set; }

		/// <summary>
		/// Increment the current value.
		/// </summary>
		/// <param name="incrementValue">Increment value.</param>
		public void Increment(float incrementValue) {
			CurrentValue += incrementValue;
		}

		/// <summary>
		/// Decrement the current value.
		/// </summary>
		/// <param name="decrementValue">Decrement value.</param>
		public void Decrement(float decrementValue) {
			CurrentValue -= decrementValue;
		}
	}
}

This is the main Attribute class. This contains all the data needed for any and all types of attributes. Stuff like attribute name, short name (for use in the GUI), max/min/current value. Again, I’m not re-inventing the wheel here. Just the basic stuff.

There’s also some convenience methods in here, like Increment() and Decrement(), which should be pretty self explanatory.

AttributeCollection.cs

public class AttributeCollection {

	/// <summary>
	/// The attributes.
	/// </summary>
	private Dictionary<AttributeEnums.AttributeType, Attribute> _attributes = new Dictionary<AttributeEnums.AttributeType, Attribute>();

	/// <summary>
	/// Add the specified type and attribute.
	/// </summary>
	/// <param name="type">Type.</param>
	/// <param name="attribute">Attribute.</param>
	public void Add(AttributeEnums.AttributeType type, Attribute attribute) {
		if (!_attributes.ContainsKey(type))
			_attributes.Add (type, attribute);
	}

	/// <summary>
	/// Get the specified type.
	/// </summary>
	/// <param name="type">Type.</param>
	public Attribute Get(AttributeEnums.AttributeType type) {
		if (_attributes.ContainsKey (type))
			return _attributes [type];
		return null;
	}

	/// <summary>
	/// Returns attribute count.
	/// </summary>
	public int Count() {
		return _attributes.Count;
	}
}

The idea behind this class is to group attributes into collections, which are then given to units. For instance, a player unit may need Experience, Level, Hitpoints, while maybe an enemy unit needs all of those except for Experience. So instead of just giving every unit every attribute, and defaulting the unused ones to 0 or something, each unit only gets what it needs.

Now of course there has to be logic to determine the attributes in each collection for each unit, and that of course will come from data files.

AttributeLoader.cs

using RoseOfEternity;

public class AttributeLoader {
	
	/// <summary>
	/// Gets the loaded attributes.
	/// </summary>
	/// <returns>The loaded attributes.</returns>
	public static AttributeCollection GetLoadedAttributes() {
		
		AttributeCollection attributes = new AttributeCollection ();

		// Experience
		AddNewAttribute(attributes, AttributeEnums.AttributeType.EXPERIENCE, "Experience", "XP", "Points until you reach the next level", 0, 0, 100);

		// Level
		AddNewAttribute(attributes, AttributeEnums.AttributeType.LEVEL, "Level", "LVL", "Current level", 1, 1, 100);

		// Hit Points
		AddNewAttribute(attributes, AttributeEnums.AttributeType.HIT_POINTS, "Hit Points", "HP", "Current hit points", 0, 0, 1000);

		// Ability Points
		AddNewAttribute(attributes, AttributeEnums.AttributeType.ABILITY_POINTS, "Ability Points", "AB", "Current ability points", 0, 0, 1000);

		// Movement
		AddNewAttribute(attributes, AttributeEnums.AttributeType.MOVEMENT, "Movement", "MOV", "Number of tiles unit can move to", 0, 0, 1000);

		// Speed
		AddNewAttribute(attributes, AttributeEnums.AttributeType.SPEED, "Speed", "SPD", "Determines how often unit can perform an action", 0, 0, 100);

		return attributes;
	}

	/// <summary>
	/// Adds the new attribute.
	/// </summary>
	/// <param name="attributes">Attributes.</param>
	/// <param name="type">Type.</param>
	/// <param name="name">Name.</param>
	/// <param name="shortName">Short name.</param>
	/// <param name="toolTip">Tool tip.</param>
	/// <param name="currentValue">Current value.</param>
	/// <param name="minimumValue">Minimum value.</param>
	/// <param name="maximumValue">Maximum value.</param>
	private static void AddNewAttribute(
		AttributeCollection attributes,
		AttributeEnums.AttributeType type,
		string name,
		string shortName,
		string toolTip,
		float currentValue,
		float minimumValue,
		float maximumValue)
	{
		Attribute attribute = new Attribute (name, shortName, toolTip, currentValue, minimumValue, maximumValue);
		attributes.Add (type, attribute);
	}
}

So, this file is a little bit of a hack at the moment. Instead of reading a file, it’s just creating an empty AttributeCollection, creating a bunch of different instances of Attribute, added them to the collection, and returning it (to be saved on a unit). But because of how I’ve separated all these class thus far, once I change the logic of this one, the others won’t know/care. They just care about getting a collection of attributes, and that’s what this class will continue to do, no matter the method.


After creating this (I need to stress, very early system), I refactored existing code to stop using these fake attributes I’ve had for the past few months, and use these real ones. Switching things out was pretty easy, and things look cleaner already.

I was even able to start creating all the logic around how experience points are handed out in the game, through the creation of a new class, ExperienceManager. I’m not going to go into that much detail surrounding this, because it’s very, very early and basic. There’s a base XP value that gets modified depending on the level  difference of the attacker/defender. From there, there is a bonus if the attack is a death blow. And that’s pretty much it.

Best part of this was the fact that all of this was done using the new Attribute system. So yeah, very happy about that.

Next on my agenda is some UI changes. As I’m still prototyping, I’m not going too nuts, but there are some things I need to do to make some of these new attributes more visible to the player.

Till tomorrow…