-
Notifications
You must be signed in to change notification settings - Fork 14
Entity Blueprints
Clearly, creating all game entities from code is not the way to go. We want our designers to be able to be creative, to invent awesome game mechanics and tweak each and every single component value. We’re going to need two additional concepts in order to achieve this.
Component values, such as the initial health of a knight in our role-playing game, are stored in attribute tables. In each of these tables, we’ll associate a key composed of the component name and the attribute name, with the respective component value. In most programming languages, these keys will be unique by language design.
Now, we can create blueprints, which are composed of a list of entity component types and an attribute table with values to initialize these components. Our fellow knight would have a component list containing a PositionComponent, a MovementComponent, a HealthComponent and an AttackComponent. His attribute table will contain his movement speed, initial health and attack damage.
Blueprints can be serialized to any arbitrary data format and made available for designers with custom editor tools. The designers can use these tools to create new blueprints, add components, and change all component values – without the need to re-compile the game!
<BlueprintManager>
<Entry>
<Id>Knight</Id>
<Blueprint>
<AttributeTable>
<Attribute keyType="System.String" valueType="System.Int32">
<Key>ArmorComponent.Armor</Key>
<Value>1</Value>
</Attribute>
<Attribute keyType="System.String" valueType="System.Int32">
<Key>AttackComponent.Damage</Key>
<Value>12</Value>
</Attribute>
<Attribute keyType="System.String" valueType="System.Int32">
<Key>HealthComponent.Health</Key>
<Value>40</Value>
</Attribute>
<Attribute keyType="System.String" valueType="System.Int32">
<Key>MovementComponent.Speed</Key>
<Value>35</Value>
</Attribute>
</AttributeTable>
<ComponentTypes>
<ComponentType>Logic.Components.ArmorComponent</ComponentType>
<ComponentType>Logic.Components.AttackComponent</ComponentType>
<ComponentType>Logic.Components.HealthComponent</ComponentType>
<ComponentType>Logic.Components.MovementComponent</ComponentType>
</ComponentTypes>
</Blueprint>
</Entry>
</BlueprintManager>
These blueprints can be loaded once when the game is started:
// Access blueprint file.
var blueprintFile = new FileInfo("Blueprints.xml");
using (var fileStream = blueprintFile.OpenRead())
{
// Deserialize blueprints.
var blueprintManagerSerializer = new XmlSerializer(typeof(BlueprintManager));
game.BlueprintManager =
(BlueprintManager)blueprintManagerSerializer.Deserialize(fileStream);
}
Given these blueprints, game systems can create the corresponding entities at run-time, for example at the start of each level:
// Get knight blueprint.
var knightBlueprint = game.BlueprintManager.GetBlueprint("Knight");
// Create knight entity.
var knightEntity = game.EntityManager.CreateEntity(knightBlueprint);
Make sure to include the Slash.ECS.Blueprints.Extensions namespace to get some useful extension methods from the EntityManagerExtensions class which let the blueprints work together with the entity manager in a beautiful way 👍
using Slash.ECS.Blueprints.Extensions;
Finally, we are able to further configure our entities using hierarchical attribute tables. Say our level contains a whole army of knights. One of them is very unlucky and has been wounded in a previous battle. In the level editor, we create the wounded knight by adding an entity with the Knight blueprint. After that, we add an additional attribute table containing the new initial health value. This attribute table overrides the blueprint attribute table, replacing its values where applicable. We call this composition of a blueprint with an additional attribute table an entity configuration.
Then, each game has a blueprint file with common game data, while each level consists of a list of entity configurations, making up the specific game entities of that level.
// Assume this is read from an XML level file, for example.
var knightConfiguration = new EntityConfiguration();
// Get knight blueprint.
var knightBlueprint =
game.BlueprintManager.GetBlueprint(knightConfiguration.BlueprintId);
// Create wounded knight entity.
var knightEntity = game.EntityManager.CreateEntity
(knightBlueprint, knightConfiguration.Configuration);
Game designers love the new flexibility: A building that’s considered a tree? Don’t panic, just add the TreeComponent, and you’re done!