Skip to main content

2. Concepts

2.1. Blueprint

A UBF Blueprint consists of the variables, logical steps, and execution flow that define the runtime functionality. Each logical step is represented by a Node in the Blueprint, with the flow of information and execution determined by which node inputs and outputs are joined by Connections. Blueprints must have a single entry point, defined by the Entry Node.

To be a valid UBF Blueprint, the JSON structure must contain the version of the standard it was created against, an array of nodes, an array of Connections, an array of variables, called Bindings, and a dictionary of Functions, which represent re-usable sections of a Blueprint. Any files not conforming exactly to the schemas laid out below are not considered valid UBF files.

UBF Blueprint
{
"version": "0.2.0",
"nodes": [],
"connections": [],
"bindings": [],
"fns": {}
}

2.1.1. Nodes

Each Node in a UBF Blueprint must correspond to an Action that is executed in the Interpreter at runtime. It must conform to one of the pre-defined Node structures set out in the Node Reference page. If a Node in a Blueprint has a type that is not listed, or if the inputs/outputs deviate from what is set out, it is not guaranteed to work in the Interpreters (See Custom Nodes).

From here on, Node inputs/outputs will be collectively referred to as 'Ports'.

UBF Node
{
"id": "Example",
"type": "Entry",
"inputs": [],
"outputs": [
{
"id": "Exec",
"type": "exec"
}
]
}
  • id: Must be a unique identifier string. It is unique to the Blueprint, but does not need to be globally unique. Referred to from here on as the Node ID.
  • type: Should be one of the pre-defined Node Types set out in the Node References. This tells the interpreter which Action to run when the Node is executed during runtime.
  • inputs: An array containing expected inputs to the Node. Inputs are fed to the Nodes via Connections to the Outputs of other Nodes. Inputs are defined by the following JSON structure:
Node Input
{
"id": "Example",
"type": "string",
"value": ""
}
  • outputs: An array containing outputs that the Node produces. These can be passed to other Nodes via Connections. Outputs are defined by the following JSON structure:
Node Output
{
"id": "Example",
"type": "float"
}

For both input and output Ports, id is an identifier unique to the Node (referred to from here on as the Port ID), which should be used to get or set the value in the Interpreters at runtime, and the type must be a valid UBF Type. For inputs, value must be a JSON string, number, boolean, or null value, determined by the expected value of the Type.

2.1.2. Connections

Each Connection in a Blueprint describes the flow of either execution (via Exec Ports) or of information. This flow is strictly one way. Each Connection uses a combination of Node ID and Port ID to define which Ports to go to and from.

Node Connection
{
"source": "",
"target": "",
"sourceKey": "",
"targetKey": ""
}
  • source: The Node ID of the Node from which the data is produced.
  • target: The Node ID of the Node to which the data should be passed.
  • sourceKey: The Port ID of the Port that writes the data.
  • targetKey: The Port ID of the Port that reads the data.

The Blueprint must not contain any cyclical Connections. A cyclical Connection is defined as any Connection that, when followed either directly or indirectly, leads back to the Connection's source Node.

2.1.3. Bindings

Blueprint Bindings are variables within the Blueprint. They can either be inputs, outputs, or local to the Blueprint, depending on the scope.

  • input: The value of input Bindings can be set in the Blueprint itself, but typically they would be overwritten by the Interpreters or by a calling Blueprint. This allows experiences to customize the behavior of the runtime execution, without needing a new Blueprint.
  • output: An output variable can be set in the Blueprint, and should be made available to the Interpreter or calling Blueprint once the Blueprint has finished running. This lets experiences use results obtained from the Blueprint for further logic.
  • var: A variable that is local to the Blueprint. It cannot be customized or accessed by interpreters or calling Blueprints. Think of it like a local variable in a function.
Calling Blueprints

A Blueprint can trigger another Blueprint to run via the ExecuteBlueprint Node. The Blueprint that triggers another is refered to as a calling Blueprint.

Blueprint Bindings should be defined like so:

Blueprint Binding
{
"id": "Example",
"scope": "input",
"type": "int",
"value": 0
}
  • id: A unique identifier string within the Blueprint. Should be used to obtain or set the value of the Binding during runtime execution.
  • scope: A string that must be equal to input, output, or var.
  • type: Determines the type of data the Binding contains. Must be a valid UBF Type.
  • value: The value of the Binding. Must be a JSON string, number, boolean, or null value, determined by the expected value of the Type.
Reserved Names

The following names in a UBF Blueprint are reserved:

  • exec
  • Graph

This means you should not create any Bindings with these values as IDs, otherwise it will cause issues.

2.1.4. Functions

When creating a Blueprint, you may find yourself repeating a set of nodes multiple times. To save time and space in your Blueprint, Functions let you re-use a set of Nodes in different parts of your Blueprint. Functions essentially act like a mini-Blueprint within your Blueprint, and contain Nodes, Connections, and Bindings just like Blueprints.

Function (Fn)
{
"nodes": [],
"connections": [],
"bindings": []
}

Functions can be called in the top-level Blueprint using the Fn Node, where the inputs and outputs on that Node correspond to the input and output bindings set in the Function.

You can have multiple Functions defined within a Blueprint, but unlike the other properties in a Blueprint which are arrays, Functions must be defined as a dictionary. Here is an example of multiple functions within a Blueprint.

Function (Fn)
{
"version": "0.2.0",
"nodes": [],
"connections": [],
"bindings": [],
"fns": {
"function1": {
"nodes": [],
"connections": [],
"bindings": []
},
"function2": {
"nodes": [],
"connections": [],
"bindings": []
}
}
}

2.2. Catalog

A UBF Catalog is a file, separate to the Blueprint, which defines locations (URIs) for Resources that the Blueprint requires. This should be used during runtime Blueprint execution to find Resources, so that they may be downloaded and used by Node Actions. Each Resource ID used in the Blueprint (See Resource Types) must have a corresponding object in the Catalog, otherwise that Resource will not be loaded at runtime.

The motivation for separating Blueprints and Catalogs is to minimize the amount of duplicate Blueprints needed for a typical game experience. If two Blueprints have the same functionality, but require different resources (e.g. Instantiating a mesh in the scene - the functionality doesn't change, but the intended mesh may), then the same Blueprint should be used, but the Catalog can be swapped out.

Catalogs are optional. If a Blueprint does not contain any Resource IDs, either as Node Ports or Blueprint Bindings, then a Catalog should not be required to run the Blueprint.

The Catalog JSON file contains the standard version that it was created with, and an array of Resources, shown here:

UBF Catalog
{
"version": "0.2.0",
"resources": []
}

2.2.1. Resources

Resources (Textures, Meshes, Blueprints) may be stored on the local machine that the Blueprint is being executed on, or they may be hosted on the internet (Such as with AWS S3). If the Interpreter has access to this location, the Resource will be downloaded at runtime.

Catalog Resource
{
"id": "my-resource",
"uri": "https://example.com/my-resource",
"hash": ""
}
  • id: A unique identifier for the Resource. Should correspond to a Resource ID used in the Blueprint.
  • uri: The location of the Resource.
  • hash: During runtime execution, Blueprint Resources may be cached to improve performance. The hash may be used to index the cache, and if used, should be re-generated every time the Resource is updated.

2.3. Interpreters

A UBF Interpreter is responsible for turning the abstract Nodes, Connections, and Bindings described in the UBF JSON into concrete code that can be executed in a runtime environment. The UBF Standard Specification does not dictate how an Interpreter should function, but the concept is outlined here to provide clarity and context around how the UBF files are intended to work in practice.

At minimum, a UBF Interpreter should:

  • Implement Actions (code) that corresponds to each Node type defined in the Node References.
  • Be able to deserialize the JSON data from Blueprints and Catalogs, into data structures that can be used at runtime.
  • Be able to download resources at runtime based on the URIs provided by the Catalogs.
  • Iterate through each Node in the order defined by the Blueprint Connections, and run the Action for each one sequentially.

The goal of a UBF Interpreter should be to carry out the functionality described by the UBF Blueprints and Catalogs in a deterministic manner, with results that mirror as closely as possible the intentions set out by the files.

2.4. Custom Nodes

It is possible to make custom Nodes using the UBF system. If a Node defines a type that is not listed in the Node Reference, it may still execute in an Interpreter if that Interpreter has been created with - or extended to provide - an Action corresponding to the custom Node.

However, it is important to understand that custom Nodes are NOT part of the UBF Standard Specification, and as such, cannot be expected to run interoperably across all Interpreters.