Here is a complete example of what is possible with the API. Don't worry if it looks overwhelming. We explore each part in more detail further below:
C#
// new template for entities describing items in a rpg
var item_template = Session.BeginNewObjectTemplate(ObjectType.Entity);
item_template.DisplayName = "Item";
item_template.TechnicalName = "Item";
// a new feature for our item properties
var item_feature = Session.BeginNewFeature();
item_feature.DisplayName = "Basic Item Properties";
item_feature.TechnicalName = "BasicItem";
// a property for the gold value
var gold_property = item_feature.AddProperty<NumberPropertyBlueprint>();
gold_property.DisplayName = "Cost in gold";
gold_property.TechnicalName = "GoldCost";
gold_property.MinimumValue = 0;
gold_property.MaximumValue = 1000;
gold_property.Precision = 2;
gold_property.Unit = "g";
var item_feature_proxy = Session.EndFeature(item_feature);
// add the item feature to our template
item_template.AddFeature(item_feature_proxy);
Session.EndObjectTemplate(item_template);
// create an item with our new template
var broadsword = Session.CreateEntity(Session.GetSystemFolder(SystemFolderNames.Entities), "Broadsword", "Item");
// change the gold cost of our broad sword
broadsword["BasicItem.GoldCost"] = 100.50;
Blueprint sessions
Whenever you create or modify an element in articy:draft X's Template Design section, you use a dedicated editor session. This session safely tracks all changes while working on the element. Only once applied, the changes are submitted and the project gets updated.
A similar workflow is necessary when working on your Template Design via the MDK:
First, you need to start a dedicated session, which we call a blueprint session in the MDK. There are blueprint sessions for all Template Design types: Property Definition, Feature, and Template. A session is started by calling the appropriate Begin... method on your ApiSession object, which creates the blueprint session object.
Next, you use this blueprint object to specify your changes, just like you would in articy:draft X in the dedicated editor session.
Finally, you apply your changes by invoking the End... method that matches the initially called Begin... method, effectively closing the current blueprint session and updating your project.
Let's consider the following example:
C#
FeatureDescriptorBlueprint sessionFeatureBlueprint = myApiSession.BeginNewFeature();
// work with the blueprint
ObjectProxy myNewFeature = myApiSession.EndFeature(sessionFeatureBlueprint);
We invoke ApiSession.BeginNewFeature to create a FeatureDescriptorBlueprint object. This starts the blueprint session. Once all changes have been made to the sessionFeatureBlueprint object, we apply them by calling EndFeature. It is worth noting that all blueprint objects are ephemeral, which means that they are not persisted as part of your project, but rather temporary helper objects to work on your Template Design during a safe session.
The process is similar for templates and property definitions:
C#
ObjectTemplateBlueprint sessionTemplateBlueprint = myApiSession.BeginNewObjectTemplate(ObjectType.Entity);
// work with the blueprint
ObjectProxy myNewTemplate = myApiSession.EndObjectTemplate(sessionTemplateBlueprint);
Creating a new template requires you to specify the ObjectType that this template should be applicable to.
C#
PropertyDefinitionBlueprint<NumberPropertyBlueprint> sessionPropertyBlueprint = myApiSession.BeginNewPropertyDefinition<NumberPropertyBlueprint>();
// work with the blueprint
ObjectProxy myNewPropertyDefinition = myApiSession.EndPropertyDefinition(sessionPropertyBlueprint);
Creating a new property definition, on the other hand, requires you to specify the type of this property inside your project. This is done by providing a type argument that extends PropertyBlueprint. For example NumberPropertyBlueprint determines that myNewPropertyDefinition will be a number, when used inside of articy:draft.
In all cases, you receive a handle to the actually created / modified object as a ObjectProxy.
Working with features
Let's now have a closer look at specifying new objects. We start with features. Features allow you to group properties into logical sets. These can then be added to templates.
Creating a new feature
Let's create a feature and set the DisplayName and TechnicalName:
C#
var bp_feature = Session.BeginNewFeature();
// lets change the display name and technical name
bp_feature.DisplayName = "My new Feature";
bp_feature.TechnicalName = "My_new_Feature";
Session.EndFeature(bp_feature);
Setting both the DisplayName and TechnicalName is optional. If you leave them empty, articy:draft X will create suitable defaults once the session is ended.
The feature we just created would appear in articy:draft X like so:
Adding properties to a feature
A feature has little use without additional properties. Properties can be added directly via the feature blueprint session object. All property types that exist in articy:draft X are available in the MDK as well. As we are adding properties to our feature via a blueprint session, the properties added are blueprint objects themselves, tracking the changes until the feature blueprint session ends. Only then they are added as actual properties to the new feature.
C#
var bp_feature = Session.BeginNewFeature();
var bp_num = bp_feature.AddProperty<NumberPropertyBlueprint>();
bp_num.DisplayName = "Cost in Gold";
bp_num.TechnicalName = "GoldCost";
bp_num.MinimumValue = 0;
bp_num.MaximumValue = 100;
bp_num.Precision = 2;
bp_num.Unit = "g";
Session.EndFeature(bp_feature);
Below is the result appearing in articy:draft X:
The properties of a property are called Meta Properties and each property has different meta properties depending on its type. Check the corresponding documentation or explore the template editor in articy:draft X to learn more about the individual meta properties. The available property types are:
When adding properties to a feature, the properties are placed onto a grid in the articy:draft X UI. The properties have different dimensions, not only depending on the type, but also based on meta properties (for example, the height of a boolean property depends on the value of the meta property DisplayAsDropDownList). This is important to consider when you manually place the objects on the grid. Luckily, when you call EndFeature the framework will use the built-in LeftToRightTopToBottomLayout as the default layouter and place your properties automatically.
public sealed class MyCustomLayouter : IObjectCustomizationGridLayout
{
/// <inheritdoc/>
public bool AllowPartiallySetPositions
{
get { return false; }
}
public void CalculateGridLayout(FeatureDescriptorBlueprint aTargetFeature)
{
foreach (var prop in aTargetFeature.Properties)
{
// do your calculations
int current_x = ...
int current_y = ...;
prop.SetPosition(current_x, current_y);
}
}
}
Additionally, you can use the method CreateDebugHtmlPropertyGrid(String) to get a visual representation of what your layouter would produce, so you don't have to check in articy:draft X. Other useful types are FeatureDescriptorBlueprint.Tag and PropertyBlueprint.Tag. These payloads are the recommended way to provide additional information to your layouter.
If you wish to manually place properties on the grid, you will find the SetPosition(Int32, Int32) method on a property blueprint.
Aside from creating new features, you can also modify existing ones. When you open an existing feature, all properties and their meta properties are copied into corresponding blueprint objects. That way you can read and modify everything accordingly. Note that the blueprint session object keeps track of what was already there and what was changed or added during the session, so you don't have to worry about that. For more details, check Meta Data.
If you want to modify a property within a feature, you can access the property in one of two ways: You can either access it via the C# property Property ...
C#
// get an existing feature
var feature_proxy = Session.GetObjectByTechName("DefaultBasicCharacterFeature");
// modify it in a blueprint session
var bp_feature = Session.BeginModifyFeature(feature_proxy);
// get the text property Appearance and change the default value
bp_feature.Property<TextPropertyBlueprint>("Appearance").DefaultValue = "The person has an average build with unremarkable features.";
// finish the session to update the articy objects.
Session.EndFeature(bp_feature);
... or iterate over all properties:
C#
// get an existing feature
var feature_proxy = Session.GetObjectByTechName("DefaultBasicCharacterFeature");
// modify it in a blueprint session
var bp_feature = Session.BeginModifyFeature(feature_proxy);
// iterate all properties
foreach (var prop in bp_feature.Properties)
{
}
// finish the session to update the articy objects.
Session.EndFeature(bp_feature);
Deleting a feature can be done in two ways. The first is via the regular object proxy deletion:
C#
var feature_proxy = Session.GetObjectByTechName("DefaultBasicCharacterFeature");
Session.DeleteObject(feature_proxy);
The other is via a blueprint session:
C#
var feature_proxy = Session.GetObjectByTechName("DefaultBasicCharacterFeature");
// modify it in a blueprint session
var bp_feature = Session.BeginModifyFeature(feature_proxy);
// mark the feature for removal
bp_feature.MarkForDeletion();
// finish the session to update the articy objects.
Session.EndFeature(bp_feature);
The result in both cases is the same, but the former is the preferred way as it doesn't come with the overhead of populating the blueprint object.
Working with template objects
Template objects, or templates for short, are a collection of features associated to a valid object type. Consequently, you can modify the template information and the features it contains using a template blueprint session.
Creating a new template
Let's create a template and set the DisplayName and TechnicalName. Additionally, a template allows you to set an icon and a color:
C#
var bp_template = Session.BeginNewObjectTemplate(ObjectType.Entity);
bp_template.TechnicalName = "Item";
bp_template.DisplayName = "Item";
// set the template color
bp_template.Color = (Color)ColorConverter.ConvertFromString("#8A5B04");
// set an icon
bp_template.UsePngFileAsIcon("sword.png");
Session.EndObjectTemplate(bp_template);
Setting both the DisplayName and TechnicalName is optional. If you leave them empty, articy:draft X will create suitable defaults once the session is ended.
This is what you would get in articy:draft X:
Adding features to a template
Similarly to a feature without properties, a template without features is of little use, so let us explore ways to add features:
C#
// get an existing feature
var feature_proxy = Session.GetObjectByTechName("DefaultBasicCharacterFeature");
// start our blueprint session
var bp_template = Session.BeginNewObjectTemplate(ObjectType.Entity);
// and add a reference to this new feature
var bp_feature_usage = bp_template.AddFeature(feature_proxy);
// and finalize the template creation
Session.EndObjectTemplate(bp_template);
Observe the call to AddFeature(ObjectProxy) which adds the referenced feature to our template and returns a FeatureUsageBlueprint, which gives us access to the settings on the feature.
Note
A FeatureUsageBlueprint
and a FeatureDescriptorBlueprint are two different things. A FeatureUsageBlueprint is used inside a template blueprint and is a readonly reference to a Feature, while you can adjust some meta properties on the existing properties, you can't modify the feature itself. On the other hand a FeatureDescriptorBlueprint is the writeable description to an articy:draft feature, used when you create a feature from a blueprint session.
Modifying existing templates
You can edit existing templates by providing the object ID or use an ObjectProxy.
C#
var main_character_template_proxy = Session.GetObjectByTechName("DefaultMainCharacterTemplate");
var bp_template = Session.BeginModifyObjectTemplate(main_character_template_proxy);
bp_template.AddFeature("My_Fancy_Feature");
Session.EndObjectTemplate(bp_template);
You can also modify certain meta-data on properties through the template layer. To do that, access the desired property using the feature's name to qualify your query. In the example below, we access the Appearance property defined in the DefaultBasicCharacterFeature:
C#
var main_character_template_proxy = Session.GetObjectByTechName("DefaultMainCharacterTemplate");
var bp_template = Session.BeginModifyObjectTemplate(main_character_template_proxy);
// now we access the Appearance text property in the DefaultBasicCharacterFeature in our template
var bp_prop_appearance = bp_template.Property<TextPropertyBlueprint>("DefaultBasicCharacterFeature.Appearance");
bp_prop_appearance.InvalidCharacters = "€$@";
Session.EndObjectTemplate(bp_template);
This will override the value of InvalidCharacters for the Appearance property, objects that use this template, other templates referencing the same property are not affected.
When your template has multiple features, you can also reorder your features, by modifying the SortOrder property.
C#
var main_character_template_proxy = Session.GetObjectByTechName("DefaultMainCharacterTemplate");
var bp_template = Session.BeginModifyObjectTemplate(main_character_template_proxy);
bp_template.Features[0].SortOrder = 1;
bp_template.Features[1].SortOrder = 0;
Session.EndObjectTemplate(bp_template);
Assigning templates
While not directly part of the blueprint session and model customization framework, it is worth to revisit how you assign existing templates to your objects.
C#
// our template
var character_template = Session.GetObjectByTechName("DefaultMainCharacterTemplate");
// assign it directly when creating the object
var bob_proxy = Session.CreateEntity(Session.GetSystemFolderId(SystemFolderNames.Entities), "Bob", character_template.GetTemplateTechnicalName());
// create an object without template
var sally_proxy = Session.CreateEntity(Session.GetSystemFolderId(SystemFolderNames.Entities), "Sally");
// assign it later, overriding any previously set templates
// here you can use the ID or TechnicalName and ignore any template color changes
sally_proxy.SetTemplate(character_template.Id, false);
Delete existing templates
Deleting a template can be done in two ways. The first is via the regular object proxy deletion:
C#
var template_proxy = Session.GetObjectByTechName("DefaultSupportingCharacterTemplate");
Session.DeleteObject(template_proxy);
The other is via a blueprint session:
C#
var template_proxy = Session.GetObjectByTechName("DefaultSupportingCharacterTemplate");
// modify it in a blueprint session
var bp_template = Session.BeginModifyObjectTemplate(template_proxy);
// mark the template for removal
bp_template.MarkForDeletion();
// finish the session to update the articy objects.
Session.EndObjectTemplate(bp_template);
The result in both cases is the same, but the former is the preferred way as it doesn't come with the overhead of populating the blueprint object.
Working with property definitions
In most cases it is sufficient to define and modify properties directly in the context of a feature. However, if you want to reuse a property inside one or across several features, you need to create a dedicated Property Definition.
Creating a new property definition
The process is similar to features and templates, but you need to specify the type of this property inside your project. This is done by providing a type argument that extends PropertyBlueprint:
C#
var bp_property_def = Session.BeginNewPropertyDefinition<NumberPropertyBlueprint>();
bp_property_def.Property.DisplayName = "Cost in gold";
bp_property_def.Property.TechnicalName = "GoldPrice";
bp_property_def.Property.Precision = 2;
bp_property_def.Property.Unit = "g";
Session.EndPropertyDefinition(bp_property_def);
Editing an existing property definition works in the same way as features and templates:
C#
var bp_property_def = Session.BeginModifyPropertyDefinition<NumberPropertyBlueprint>(existing_property_definition_proxy);
// modify your property definition here
Session.EndPropertyDefinition(bp_property_def);
Using property definition
After you have created your property definition like above, you can place properties inside features by utilizing the property definition like this:
C#
// get a proxy to our property definition object
var gold_property_definition_proxy = Session.GetObjectByTechName("GoldPrice");
// start a new feature
var bp_feature = Session.BeginNewFeature();
// add properties utilizing our property definition
var bp_gold_stranger = bp_feature.AddPropertyFromDefinition<NumberPropertyBlueprint>(gold_property_definition_proxy);
bp_gold_stranger.DisplayName = "Cost in gold for strangers";
bp_gold_stranger.TechnicalName = "GoldPriceStranger";
var bp_gold_guild_member = bp_feature.AddPropertyFromDefinition<NumberPropertyBlueprint>(gold_property_definition_proxy);
bp_gold_guild_member.DisplayName = "Cost in gold for guild members";
bp_gold_guild_member.TechnicalName = "GoldPriceGuildMember";
// finish the feature and create the articy objects
Session.EndFeature(bp_feature);
Keep in mind that your new properties are inheriting all values from the property definition. In other words, all property values that were not overridden remain the same, like Unit and Precision in the example above.
Note
While you don't have to set a DisplayName and TechnicalName for your properties added from a property definition, it is strongly recommended as the API will give them unique, but generic names. In the above case the second GoldPrice property would be named something like GoldPrice_02. To ensure proper naming, make it a habit of always setting DisplayName and TechnicalName explicitely.
Deleting existing property definition
Deleting a property definition can be done in two ways. The first is via the regular object proxy deletion:
C#
var property_definition_proxy = Session.GetObjectByTechName("My_Property_definition");
Session.DeleteObject(property_definition_proxy);
The other is via a blueprint session:
C#
var property_definition_proxy = Session.GetObjectByTechName("My_Property_definition");
// modify it in a blueprint session
var bp_property_def = Session.BeginModifyPropertyDefinition<NumberPropertyBlueprint>(property_definition_proxy);
// mark the property definition for removal
bp_property_def.MarkForDeletion();
// finish the session to delete the property definition
Session.EndPropertyDefinition(bp_property_def);
The result in both cases is the same, but the former is the preferred way as it doesn't come with the overhead of populating the blueprint object.
Details on properties
Some property types are a bit more involved and benefit from further explanation:
Text and script sizes
In the articy:draft X UI Text and Script property types have dedicated properties for each individual size. In the MDK the different sizes are achieved with a setter on the blueprint object.
C#
var bp_feature = Session.BeginNewFeature();
var bp_name = bp_feature.AddProperty<TextPropertyBlueprint>();
bp_name.DisplayName = "Name";
bp_name.TechnicalName = "Name";
// Small text is just a single line of text
bp_name.Size = TextPropertyBlueprint.TextPropertySizes.Small;
var bp_mouse_hover = bp_feature.AddProperty<ScriptPropertyBlueprint>();
bp_mouse_hover.DisplayName = "OnMouseHover";
bp_mouse_hover.TechnicalName = "OnMouseHover";
// Medium script is a half width script field
bp_mouse_hover.Size = ScriptPropertyBlueprint.ScriptPropertySizes.Medium;
var bp_backstory = bp_feature.AddProperty<TextPropertyBlueprint>();
bp_backstory.DisplayName = "Backstory";
bp_backstory.TechnicalName = "Backstory";
// Large text is a full width text field
bp_backstory.Size = TextPropertyBlueprint.TextPropertySizes.Large;
Session.EndFeature(bp_feature);
The result in articy looks like this
Drop down
A drop-down property, used when you need a predefined set of values, can be created in two distinct ways. The first is "manually":
C#
var bp_feature = Session.BeginNewFeature();
var bp_drop_down = bp_feature.AddProperty<DropDownPropertyBlueprint>();
bp_drop_down.DisplayName = "Hero Classes";
bp_drop_down.TechnicalName = "Classes";
// manually add each entry
bp_drop_down.AddDropDownEntry("The Barbarian", "Barbarian");
bp_drop_down.AddDropDownEntry("Mage");
bp_drop_down.AddDropDownEntry("Rogue");
bp_drop_down.AddDropDownEntry("Bard");
bp_drop_down.AddDropDownEntry("None", "None", 255);
Session.EndFeature(bp_feature);
This list of entries in articy:draft X would look like this:
The second is via an existing C# enum, this is helpful if you need to make a connection from an API to articy:draft X:
C#
var bp_feature = Session.BeginNewFeature();
var bp_drop_down = bp_feature.AddProperty<DropDownPropertyBlueprint>();
// uses the MDK ObjectType enum to populate our drop-down
bp_drop_down.PopulateFromEnum<Articy.Api.ObjectType>();
Session.EndFeature(bp_feature);
Note that the name of the provided enum type will be used as TechnicalName of the DropDownPropertyBlueprint. This is also true for the DisplayName, unless you can add a System.ComponentModel.DescriptionAttribute to your enum type declaration, which allows you to set the property's DisplayName.
Both DisplayName and TechnicalName of the drop-down entries are determined by teh enum member names.
Slot and Strip properties
Slot and Strip properties allow you to exactly specify which articy objects can be placed inside of them. Additionally, you can define which templates, if any, are supported. Here you see some examples how to modify the AllowedObjects property via code:
C#
var bp_feature = Session.BeginNewFeature();
// add a new slot property
var slotProp = bp_feature.AddProperty<SlotPropertyBlueprint>();
// by default, everything is set in the Allowed object types property, if you want to start with everything unset
slotProp.OverrideAndUnsetAll();
// we allow entities with any template
slotProp.ModifyObjectTypeReference(ObjectType.Entity).WithAnyTemplate = true;
// we also allow flow fragments but only ones without a template
slotProp.ModifyObjectTypeReference(ObjectType.FlowFragment).WithAnyTemplate = false;
slotProp.ModifyObjectTypeReference(ObjectType.FlowFragment).WithoutTemplate = true;
// and assets are only allowed with the TextureAsset_Template template.
var texture_template = Session.GetObjectByTechName("TextureAsset_Template");
slotProp.ModifyObjectTypeReference(ObjectType.Asset).SpecificTemplates.Value.Add(texture_template.Id);
// also for completion, we ensure that they are only images
slotProp.SupportedAssetTypes = ObjectTypeAssetReferences.Image;
Session.EndFeature(bp_feature);
And this is how the allowed object list would look in the articy inspector:
Blueprint Session objects and meta properties
Session and change tracking
One thing to familiarize yourself with is the concept of change tracking inside the Blueprint Session objects. All properties from the display name of a feature to the minimum value of a number property inside one of your features, are keeping track of their state throughout a blueprint session. When you are just creating new objects, this doesn't matter much, but when you start modifying existing objects, this can become important. Every meta property is represented with special container types. One of them is ObjectCustomizationPropertyValueTValueType. They are implemented with implicit conversions to their managed type counterpart, but you can also access them directly to check if there were changed. More importantly, you can revert session changes, but more on that later.
A helpful little debug visualization in the Visual Studio debugger will show you the state of these meta properties when inspecting the blueprint objects:
All meta property values use an icon to inidicate their state: unchanged (checkmark), changed (cylcic arrow), or reset (x). Unchanged values will not be written back to the actual object once the session ends, while changed and reset values are. For example, the MinimumValue meta-property had a value when the sessions started, but it was reset during this session, while the MaximumValue was changed to 1000 during this session, while all others remained unchanged.
Multi language text
Some properties and meta properties allow you to store text in multiple languages. This is done via the BlueprintMultiLanguageText type which works similarly to the aforementioned ObjectCustomizationPropertyValueTValueType type. The difference is that it can store multiple strings mapped to a language.
When you store a string directly to it (without providing an explicit language mapping), you effectively set the text of your main language or Content Creation Language (short CCL).
C#
var bp_feature = Session.BeginNewFeature();
// set the displayname in the CCL, Session.GetPrimaryLanguage()
bp_feature.DisplayName = "My new Feature";
bp_feature.DisplayName.Set(Session.GetLanguage("fr"), "Ma nouvelle fonctionnalité";
bp_feature.DisplayName.Set(Session.GetLanguage("de"), "Mein neues Feature");
Session.EndFeature(bp_feature);
Reset Value
The effective value of any object customization value (being the value of a property or the value of metadata for a property) follows a chain of value precedence. So lets say you have a number property definition and you have overwritten the Unit meta data property. When you use this property definition inside a feature, Unit meta property is still the value you'd expect (the one from the number property definition), since the value is taken from there. This precedence, sometimes called Layer, starts at the default value of a property and goes up the chain to the template (for the value even to the model). On every layer you could override the value, indicated by the ResetValue button in the articy:draft template editor, being enabled.
As briefly mentioned before, you can reset a local value in the MDK by accessing the ObjectCustomizationPropertyValueTValueType and calling ResetValue.
C#
// reset the locally override unit and let it fallback to the previous set value
bp_feature.Property<NumberPropertyBlueprint>("GoldCost").Unit.ResetValue();