Componentes Personalizados

Crie um componente personalizado para lidar com dados específicos relacionados a uma entidade

Dados sobre uma entidade são armazenados em seu components. O SDK do Decentraland fornece uma série de componentes base que gerenciam diferentes aspectos de uma entidade, como sua posição, forma, material, etc. O engine sabe como interpretar a informação neles e mudará a forma como a entidade é renderizada assim que seus valores forem alterados.

Se a lógica da sua cena requer armazenar informações sobre uma entidade que não são tratadas pelos componentes padrão do SDK, então você pode criar um tipo customizado de componente na sua cena. Você pode então construir systems que verificam mudanças nesses componentes e respondem adequadamente.

Sobre definir componentes

Para definir um novo componente, use engine.defineComponent. Cada componente precisa do seguinte:

  • Um componentName: Um identificador de string único que o SDK usa internamente para identificar esse tipo de componente. Pode ser qualquer string, desde que seja única.

  • A schema: Uma classe que define a estrutura de dados mantida pelo componente.

  • default values (opcional): Um objeto contendo valores padrão para usar ao inicializar uma cópia do componente, quando estes não são fornecidos.

export const WheelSpinComponent = engine.defineComponent('wheelSpinComponent', {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
})
circle-exclamation

Uma vez que você definiu um componente customizado, você pode criar instâncias desse componente, que referenciam entidades na cena. Quando você cria uma instância de um componente, você fornece valores para cada um dos campos no schema do componente. Os valores devem obedecer aos tipos declarados de cada campo.

// Crie entidades
const wheel = engine.addEntity()
const wheel2 = engine.addEntity()

// Crie instâncias do componente
WheelSpinComponent.create(wheel1, {
	spinning: true,
	speed: 10,
})

WheelSpinComponent.create(wheel2, {
	spinning: false,
	speed: 0,
})

Cada entidade que tem o componente adicionado instancia uma nova cópia do componente, contendo dados específicos para essa entidade.

Seu componente customizado também pode executar as outras funções comuns disponíveis em outros componentes:

Sobre o componentName

Cada componente deve ter um nome ou identificador de componente único, que o diferencie internamente. Você não precisará usar esse identificador interno em nenhuma outra parte do seu código. Uma boa prática é usar o mesmo nome que você atribui ao componente, mas começando com uma letra minúscula, porém o que realmente importa é que esse identificador seja único dentro do projeto.

Ao criar componentes que serão compartilhados como parte de uma biblioteca, tenha em mente que os nomes dos componentes na sua biblioteca não devem se sobrepor a quaisquer nomes de componentes no projeto onde ela for usada, ou em outras bibliotecas que também sejam usadas por esse projeto. Para evitar o risco de qualquer sobreposição, a prática recomendada é incluir o nome da biblioteca como parte da componentName string. Você pode seguir esta fórmula: ${packageName}::${componentName}. Por exemplo se você constrói umaMyUtilities biblioteca que inclui um MoveEntity componente, defina o componentName desse componente como MyUtilities::moveEntity.

Componentes como flags

Você pode querer adicionar um componente que simplesmente marca (flag) uma entidade para diferenciá-la das outras, sem usá-lo para armazenar qualquer dado. Para fazer isso, deixe o schema como um objeto vazio.

Isso é especialmente útil quando se usam consultas de componentes. Um componente de flag simples pode ser usado para distinguir entidades das outras, e evitar que o sistema itere sobre mais entidades do que o necessário.

Você então pode criar um sistema que itere sobre todas as entidades com esse componente.

Schemas de Componentes

Um schema descreve a estrutura dos dados dentro de um componente. Um componente pode armazenar quantos campos você quiser, cada um deve ser incluído na estrutura do schema. O schema pode incluir quantos níveis de itens aninhados você precisar.

Cada campo no schema deve incluir uma declaração de tipo. Você só pode usar os tipos especiais de schema fornecidos pelo SDK. Por exemplo, use o tipo Schemas.Boolean em vez do tipo boolean. Digite Schemas. e seu IDE exibirá todas as opções disponíveis.

O exemplo acima define um componente cujo schema contém dois valores, um spinning booleano e um Altere a velocidade com que uma animação é reproduzida alterando a propriedade número de ponto flutuante.

Você pode optar por criar o schema inline enquanto define o componente, ou para maior legibilidade você pode criá-lo e então referenciá-lo.

circle-info

💡 Tip: Ao criar uma instância de um componente, as opções de autocompletar do VS Studio sugerirão quais campos você pode adicionar ao componente pressionando Ctrl + Space.

Tipos de Schema padrão

Os seguintes tipos básicos estão disponíveis para uso dentro dos campos de um schema:

  • Schemas.Boolean

  • Schemas.Byte

  • Schemas.Double

  • Schemas.Float

  • Schemas.Int

  • Schemas.Int64

  • Schemas.Number

  • Schemas.Short

  • Schemas.String

  • Schemas.Entity

Os seguintes tipos complexos também existem. Cada um inclui uma série de propriedades aninhadas com valores numéricos.

  • Schemas.Vector3

  • Schemas.Quaternion

  • Schemas.Color3

  • Schemas.Color4

circle-info

💡 Tip: Veja Tipos de geometria e Tipos de cor para mais detalhes sobre como esses tipos de dados são úteis.

Por exemplo, você pode usar esses tipos de schema em um componente como este para rastrear o movimento gradual de uma entidade. Este componente armazena uma posição inicial e uma final como valores Vector3, assim como uma velocidade e fração do caminho completado como números float. Veja Mover entidades para a implementação completa deste exemplo.

Tipos Array

Para definir o tipo de um campo como um array, use Schemas.Array(). Passe o tipo dos elementos do array como uma propriedade.

Tipos de schema aninhados

Para definir o tipo de um campo como um objeto, use Schemas.Map(). Passe o conteúdo deste objeto como uma propriedade. Esse objeto aninhado é essencialmente um schema por si só, aninhado dentro do schema pai.

Alternativamente, para manter as coisas mais legíveis e reutilizáveis, você pode alcançar o mesmo definindo o schema aninhado separadamente e então referenciando-o ao definir o schema pai.

Tipos Enums

Você pode definir o tipo de um campo em um schema como um enum. Enums facilitam selecionar entre um número finito de opções, fornecendo valores legíveis para humanos para cada uma.

Para definir o tipo de um campo como um enum, você deve primeiro definir o enum. Então você pode referenciá-lo usando Schemas.EnumNumber ou Schemas.EnumString, dependendo do tipo de enum. Você deve passar o enum para referenciar entre <>, assim como o tipo como parâmetro (ou Schemas.Int para enums numéricos, ou Schemas.String para enums de string). Você também deve passar um valor padrão para usar para esse campo.

Tipos intercambiáveis

Você pode definir o tipo de um campo em um schema para seguir um oneOf padrão, onde tipos diferentes podem ser aceitos.

Ao criar uma instância do componente, você precisa especificar o tipo selecionado com um $case, por exemplo:

Valores padrão

É frequentemente bom ter valores padrão em seus componentes, para que não seja necessário definir explicitamente cada valor toda vez que você criar uma nova cópia.

O engine.defineComponent() função recebe um terceiro argumento, que permite passar um objeto com valores para usar por padrão. Esse objeto pode incluir alguns ou todos os valores do schema. Valores que não são fornecidos nos defaults precisarão sempre ser fornecidos ao inicializar uma cópia do componente.

O exemplo acima cria um WheelSpinComponent componente que inclui tanto um schema quanto um conjunto de valores padrão para usar. Se você então inicializar uma cópia deste componente sem especificar quaisquer valores, ele usará os definidos no default.

Inscrever-se em mudanças

Um caso de uso comum é executar uma função apenas caso os dados em certo componente mudem. Use a OnChange function para evitar ter que definir um system e ter que comparar explicitamente valores antigos com valores novos.

Construindo systems para usar um componente

Com seu componente definido e adicionado a entidades na sua cena, você pode criar Sistemasarrow-up-right para realizar lógica, fazendo uso desses dados armazenados no componente.

O exemplo acima define um system que itera sobre todas as entidades que incluem o custom wheelSpinComponent, e as rotaciona levemente a cada tick do loop do jogo. A quantidade dessa rotação é proporcional ao Altere a velocidade com que uma animação é reproduzida alterando a propriedade valor armazenado na instância do componente de cada entidade. O exemplo faz uso de consultas de componente para obter apenas as entidades relevantes.

Atualizado