Custom Component Markup

Custom components are a powerful feature of DCL-Edit that allows users to create their own reusable components to use in their scenes. They can be implemented, like any other Decentraland Component, but also require a JSON object, that describes their structure.

tl;dr:

Example:
// The Description of the component
/*
#DCECOMP
{
    "class": "Elevator",
    "component": "EntityMover",
    "category": "Custom/Movers",
    "properties": [
        {
            "name": "speed",
            "type": "number",
            "default": 5
        },
        {
            "name": "dimensions",
            "type": "vector3",
            "default": [2, 4, 2]
        },
        {
            "name": "label",
            "type": "string",
            "default": "My Elevator"
        }
    ]
}
*/

// The Component it self
@Component("EntityMover")
export class Elevator {
    // the properties of your component
    public speed: number = 5;
    public dimensions: Vector3 = new Vector3(2, 4, 2);
    public label: string = "My Elevator";

    // optional init funcion, that is called, after the properties are set
    init(entity: Entity) {
        // your init code here
    }
}

Fields in a component definition object:

Filed Description Required / Default
class The name of the component class Required
component The name of the component, as written in the @Component("") decorator Not required, defaults to the class field
import-file The file, the component gets imported from Definition in .dcecomp file: Required. Definition in .ts file: Not required, defaults to its own file
category The category, where the component will be shown in Not required, defaults to “Custom”
properties A list of property objects in the component Not required, defaults to no properties

Fields in a property object:

Filed Description Required / Default
name The name of the property in code Required
type The type of the property. Can be number, string, vector3, bool, rotation, or asset:model Required
default The default value of the property Not required

The Component class

In order to create a custom component in DCL-Edit, you must first create a normal Decentraland component, like you would in the vanilla SDK. This has to follow some guidelines to work with DCL-Edit.

  • You need to have a default constructor. Constructors with parameters are not supported by DCL-Edit. Note: If you do not define a constructor in your custom component, a default constructor will be implicitly created, which is also fine.
  • All properties in a custom component must be declared as public in order to be accessible by DCL-Edit.
  • You can optionally add an init(entity: Entity) function. This function is called, after the properties are set. The entity parameter contains the entity, where the component was added to.

The JSON

The JSON object that describes the custom component must follow a specific structure. It starts with a tag #DCECOMP that marks the beginning of the JSON object. Following this tag, the JSON object must be a valid JSON format, which means it must contain a set of key-value pairs separated by colons and enclosed in curly braces.

This object can either be in a .dcecomp file, or within a comment in a .ts file.

Example:
/*
Comments are fine here
#DCECOMP
{
    ...
}
*/
Example with error:
/*
#DCECOMP // Adding a comment here is not allowed
{
    ...
    <- missing end bracket
*/

A .dcecomp file is a file that contains only the JSON objects describing custom components. This file can be used to define a component that is not in the same file as the class definition. You can use this to organize components into separate files. That way, it is possible to separate the class definition and component definition into different packages.

In .dcecomp files, the JSON object should not be surrounded by a block comment.

class

The class property in the JSON object needs to be the name of the component’s class. Giving it a different name, leads to an error when executing the scene. This is case-sensitive.

Example:
/*
#DCECOMP
{
    "class": "Elevator"
}
*/

@Component("Elevator")
export class Elevator {

}
Example with error:
/*
#DCECOMP
{
    "class": "Elevator"
}
*/

@Component("Door")
export class Door{ // <- "class": "Elevator" does not match "Door"

}

component

The component field in the JSON object specifies the name of the Decentraland component that is provided in the @Component("…") annotation.

If none is provided, it is assumed to be the same as the class. You should always provide the component, when it is different from the class.

Example:
/*
#DCECOMP
{
    "class": "Elevator",
    "component": "EntityMover"
}
*/

@Component("EntityMover")
export class Elevator {

}
/*
#DCECOMP
{
    "class": "Door"
}
*/

@Component("Door")
export class Door{

}
Example with error:
/*
#DCECOMP
{
    "class": "Elevator",
    "component": "EntityMover"
}
*/

@Component("Door") // <- "component": "EntityMover" does not match "Door"
export class Elevator {
    // ...
}

import-file

The import-file field in the JSON object specifies the path where the component will be imported from.

import {MyComponent} from "src/components/my-component"
//                        `-----    This part    -----´

This is only required when defining a component in a .dcecomp file.

In a .ts file, dcl-edit assumes, that the Component is in the same file as its definition.

Use the full path, from the project root on. Relative paths can not be resolved.

Example:

"import-file": "src/components/elevator"

Example with error:

"import-file": "../components/elevator"

No relative paths

Example with error:

"import-file": "C:/Users/Username/Documents/repos/MyProject/src/components/elevator"

Not the full path from the file system root

Note: When you make a component, that you want to ship as a npm package, you might need to include node_modules/package_name/…

Example:

File: src/components/elevator.ts

@Component("Elevator")
export class Elevator {

}

File: src/components/definitions/elevator.dcecomp

#DCECOMP
{
    "class": "Elevator",
    "import-file": "src/components/elevator"
}

category

The category field in the JSON object specifies the category where the component will be shown in. This field is optional and defaults to “Custom”. It is useful to define categories to differentiate the components in the component picker.

Custom components can be categorized into subcategories by separating them with a forward slash. For example, "category": "Custom/Movers" will put the component under a “Custom” category with a subcategory of “Movers”.

Example:
/*
#DCECOMP
{
    "class": "Elevator",
    "category": "Some/Category"
}
*/

properties

The properties field in the JSON object specifies a list of property objects. This field is optional, and if it is not provided, it is assumed that the custom component does not have any properties.

Example:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            ...
        },
        {
            ...
        }
    ]
}
*/
Example with error:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": {   <- Not a list of objects
        ...
    }
}
*/

properties→name

The name field specifies the name of the property. This is required. The name must exactly match the name of the property in the component class, and it is case-sensitive.

Example:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "height",
            ...
        }
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public height: number = 5;
}
Example with error:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "height",
            ...
        },
        {
            "name": "speed",
            ...
        },
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public Height: number = 5; // <- capital H
                               // <- speed property does not exist
}

properties→type

The type field specifies the type of the property. This field is required. Each type has a corresponding Typescript type, that the property in the class has to have.

The allowed types are:

Property type TypeScript type
number number
string string
bool boolean
vector3 Vector3
rotation Quaternion
asset:model GltfShape
Example:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "height",
            "type": "number"
        }
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public height: number = 5;
}
Example with error:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "height",
            "type": "float", // <- not an allowed type
        }
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public height: number = 5;
}
Example with error:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "height",
            "type": "rotation"
        }
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public height: Vector3 = 5; // <- This type needs to be `Quaternion`
}

properties→default

The default field specifies the default value of the property.

The value must match the type specified in the type field.

string: Just write a string. E.g.: "some value"

number: Just write a number. E.g.: 1000

bool: Either true or false

vector3: An array of numbers. E.g.: [1, 2, 3]

rotation:

Euler angles: Write a string starting with “E”, followed by three numbers in parentheses.

E.g.: E (45, 0, 45)

Quaternion: Write a string starting with “Q”, followed by four numbers in parentheses.

E.g.: Q (0.5, 0.5, 0.5, 0.5)

asset: A string with a Guid. E.g.: “2a8c3e61-3b9e-4f9c-8c9c-1c9c3e61b9e4”

This field is optional. If it is not provided, the default value will be determined by type.

number0

string""

boolfalse

vector3[0, 0, 0]

rotation → no rotation

asset → no asset

Example:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "dimensions",
            "type": "vector3",
            "default": [2, 2, 4]
        }
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public height: Vector3 = new Vector3(2, 2, 4);
}
Example with error:
/*
#DCECOMP
{
    "class": "Elevator",
    "properties": [
        {
            "name": "height",
            "type": "number",
            "default": "five" // <- type is not a number
        }
    ]
}
*/

@Component("Elevator")
export class Elevator {
    public height: number = 5;
}

Note: The initial values of the properties in the class are ignored. All properties will be initialized by DCL-Edit