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.
number
→ 0
string
→ ""
bool
→ false
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