Criar {itemType}
Monetização

Criação de Avatares em Experiências

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Você pode publicar uma experiência que permite que jogadores criem, personalizem e comprem corpos de avatares em tempo real. Quando comprados, esses corpos personalizados são salvos diretamente no inventário do jogador no Roblox, permitindo que os jogadores equipem e usem os avatares personalizados em outras experiências.

Os proprietários de experiências que implementam a criação de avatares em-experiência se beneficiam das comissões do Mercado tanto como criadores do item do avatar quanto como proprietários de experiência. Se um ativo criado na experiência for inspecionado, o item fornece um link para a experiência original em que foi criado.

Você pode testar a criação em-experiência na demonstração do Criador de Avatares do Roblox.

Como implementar a criação em-experiência

Use as seguintes instruções e referências de código para criar seu primeiro projeto de criação de avatares em-experiência. As seguintes instruções usam um corpo base Model que os jogadores podem modificar e personalizar antes de publicar.

Antes de começar, familiarize-se com o seguinte:

  • Modelos de Avatar — A implementação a seguir requer a importação de um corpo base que atende às especificações de 15 partes do Roblox. Este Model serve como base para personalização e modificação adicional do usuário.
  • Tokens de criação de avatares — As experiências que implementam a criação de avatares requerem pelo menos um token de criação. Esses tokens requerem Robux para compra e permitem que você defina preços e outras configurações de venda para compras feitas na experiência.
  • Classes da API
    • AvatarCreationService — Gerencia a solicitação e validação de criação de avatares.
    • EditableImage — Gerencia a criação e manipulação de texturas em tempo de execução.
    • EditableMesh — Gerencia a manipulação da geometria da malha em tempo de execução.
    • WrapDeformer — Gerencia a manipulação em tempo de execução da geometria do invólucro invisível que permite que personagens de avatar equipem roupas 3D.

Importar um corpo base

O corpo base atua como a fundação inicial que os usuários podem personalizar e editar. Você pode usar seu próprio Model ou importar um ativo personalizado com o Importador e configurar através do Configuração do Avatar.

Corpos base devem aderir às especificações de avatar do Roblox e devem incluir componentes como as 15 instâncias de MeshPart que compõem 6 partes do corpo: cabeça, tronco, braço esquerdo, perna esquerda, braço direito e perna direita, bem como outros componentes de avatar.

Para referências e exemplos de corpos de avatar configurados corretamente, consulte Referências de Avatar.

Implementar APIs de edição

Para desenvolver um sistema onde os usuários possam editar as instâncias de MeshPart em um avatar em sua experiência para criação, use EditableImage para edição de texturas, EditableMesh para edição de malhas e WrapDeformer para manter a skinning e dados FACS durante as edições de malha.

  1. Depois de importar seu corpo base, use o seguinte script para configurar suas EditableImages, EditableMeshes, e WrapDeformers.


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Cria e anexa um WrapDeformer ao MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Cria uma malha editável para a malha da gaiola do alvo
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Atribui a malha da gaiola ao WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Cria uma malha editável a partir do MeshPart original
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Gera um novo MeshPart a partir da malha editável
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Copia tamanho, posição e textura do MeshPart original
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Aplica o novo MeshPart de volta ao original
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Se EditableImage já existir para este TextureID, reutilize-o em vez de criar um novo
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Cria uma nova EditableImage e aplica como conteúdo da textura
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- Mapeia para reutilizar instâncias de EditableImage por ID de textura
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- Configura MeshPart com base na presença de WrapTarget
    -- Se WrapTarget estiver presente, adiciona um filho WrapDeformer com uma EditableMesh
    -- Caso contrário, aplica EditableMesh diretamente ao MeshPart
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Configura a EditableImage para o MeshPart
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Crie ferramentas de EditableImage que permitam aos jogadores recolorir, desenhar ou adicionar adesivos ao seu corpo base. Você pode aproveitar APIs como DrawImage(), DrawRectangle(), WritePixelsBuffer().

    • Para transformações avançadas, DrawImageTransformed() permite que você especifique posição, rotação e escala ao desenhar uma EditableImage sobre outra. Da mesma forma, DrawImageProjected() funciona muito semelhante a DrawImage(), mas projeta a imagem desenhada corretamente se a instância EditableImage for usada com um MeshPart.


      local function recolorTexture(
      meshPart: MeshPart,
      color: Color3
      )
      local bodyPartTexture = AssetService:CreateEditableImageAsync(meshPart.TextureID)
      meshPart.TextureContent = Content.fromObject(bodyPartTexture)
      bodyPartTexture:DrawRectangle(
      Vector2.new(0, 0),
      bodyPartTexture.Size,
      color,
      0,
      Enum.ImageCombineType.Overwrite
      )
      end
      local function applySticker(
      meshPart: MeshPart,
      textureCoordinate: Vector2,
      stickerId: TextureId
      )
      local bodyPartTexture = AssetService:CreateEditableImageAsync(meshPart.TextureID)
      meshPart.TextureContent = Content.fromObject(bodyPartTexture)
      local stickerTexture = AssetService:CreateEditableImageAsync(stickerId)
      bodyPartTexture:DrawImage(textureCoordinate, stickerTexture, Enum.ImageCombineType.BlendSourceOver)
      end
      local function applyStickerProjected(
      meshPart: MeshPart,
      targetMesh: EditableMesh,
      stickerId: TextureId,
      raycastHitPos: Vector3
      )
      local bodyPartTexture = AssetService:CreateEditableImageAsync(meshPart.TextureID)
      local relativePos = meshPart.CFrame:PointToWorldSpace(raycastHitPos)
      local direction = (workspace.CurrentCamera.CFrame.Position - relativePos).Unit
      local projectionParams: ProjectionParams = {
      Direction = meshPart.CFrame:VectorToObjectSpace(direction),
      Position = meshPart.CFrame:PointToObjectSpace(relativePos),
      Size = Vector3.new(1, 1, 1),
      Up = meshPart.CFrame:VectorToObjectSpace(Vector3.new(0, 1, 0)),
      }
      local stickerTexture = AssetService:CreateEditableImageAsync(stickerId)
      local localBrushConfig: BrushConfig = {
      Decal = stickerTexture,
      ColorBlendType = Enum.ImageCombineType.BlendSourceOver,
      AlphaBlendType = Enum.ImageAlphaType.Default,
      BlendIntensity = 1,
      FadeAngle = 90.0
      }
      bodyPartTexture:DrawImageProjected(targetMesh, projectionParams, localBrushConfig)
      end
  3. Usando WrapDeformer e EditableMesh, crie ferramentas para editar deformações de malha em seu corpo.

    1. WrapDeformer lida com as deformações ao vivo da geometria renderizada do MeshPart, mantendo os dados de skinning e FACS subjacentes.

    2. EditableMesh permite que você modifique a malha da gaiola à qual o WrapDeformer responde.

      1. Use WrapDeformer:SetCageMeshContent() para aplicar a instância EditableMesh que representa a malha da gaiola relevante ao WrapDeformer.
      2. Use EditableMesh, como SetPosition(), para deformar vértices e editar a forma do MeshPart.

      local function deformBodyPart(
      meshPart: MeshPart,
      controlPointCenter: Vector3,
      controlPointRadius: number,
      controlPointDeformation: Vector3
      )
      local wrapTarget = meshPart:FindFirstChildWhichIsA("WrapTarget")
      local cageMeshId = wrapTarget.CageMeshId
      local wrapDeformer = Instance.new("WrapDeformer")
      wrapDeformer.Parent = meshPart
      local cageEditableMesh = AssetService:CreateEditableMeshAsync(cageMeshId)
      local verticesWithinSphere =
      cageEditableMesh:FindVerticesWithinSphere(controlPointCenter, controlPointRadius)
      for _, vertexId in verticesWithinSphere do
      local vertexPosition = cageEditableMesh:GetPosition(vertexId)
      cageEditableMesh:SetPosition(vertexId, vertexPosition + controlPointDeformation)
      end
      wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
      end

Criar prompt de criação

Após configurar seu corpo base e as APIs de edição, crie um prompt para os usuários criarem e comprarem a partir da experiência usando AvatarCreationService:PromptCreateAvatarAsync().


export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Pasta com MeshParts Criados
}
export type BodyPartList = {BodyPartInfo}
local function publishAvatar(bodyPartInstances: BodyPartList, player: Player, tokenId: string)
local humanoidDescription = Instance.new("HumanoidDescription")
for _, bodyPartInfo in bodyPartInstances do
local bodyPartDescription = Instance.new("BodyPartDescription")
bodyPartDescription.Instance = bodyPartInfo.instance
bodyPartDescription.BodyPart = bodyPartInfo.bodyPart
bodyPartDescription.Parent = humanoidDescription
end
local success, result, bundleIdOrErrorMessage, outfitId = pcall(function()
return AvatarCreationService:PromptCreateAvatarAsync(tokenId, player, humanoidDescription)
end)
if success then
if result == Enum.PromptCreateAvatarResult.Success then
print("Upload realizado com sucesso com BundleId: ", bundleIdOrErrorMessage)
print("Upload realizado com sucesso com OutfitId: ", outfitId)
else
print("Upload não realizado com sucesso com a mensagem de erro:", bundleIdOrErrorMessage)
end
else
print("Criação de avatar não foi bem-sucedida")
end
end

AvatarCreationService:PromptCreateAvatarAsync() recebe um parâmetro HumanoidDescription para representar o avatar destinado à compra ou criação. Para a criação do avatar, a HumanoidDescription do personagem deve incluir novos ativos a serem criados para cada uma das 6 partes do corpo (Cabeça, Tronco, PernaDireita, PernaEsquerda, BraçoDireito, BraçoEsquerdo). Opcionalmente, também pode incluir um acessório novo do tipo Cabelo.

Para suportar isso, a HumanoidDescription deve incluir 6 filhos de BodyPartDescription. Cada propriedade BodyPartDescription.Instance referencia uma Folder que inclui todas as instâncias de MeshPart que compõem a parte do corpo. Por exemplo, a pasta BraçoEsquerdo contém MãoEsquerda, BraçoSuperiorEsquerdo e BraçoInferiorEsquerdo MeshParts. A propriedade BodyPartDescription.BodyPart também deve ser definida para o Enum.BodyPart relevante.

Cada uma das 15 partes do corpo MeshPart deve incluir:

A HumanoidDescription fornecida não deve incluir nenhum ID de ativo pré-existente para representar partes do corpo ou acessórios na criação pretendida. Por outro lado, a HumanoidDescription pode incluir as escalas humanoides de EscalaTipoCorporal, EscalaCabeça, EscalaAltura, EscalaLargura, e EscalaProporção. Tenha cuidado com as escalas com que um corpo base é importado para que correspondam às escalas fornecidas para a HumanoidDescription.

Incluir acessórios

Se incluir um acessório, como cabelo, a HumanoidDescription deve incluir um filho AccessoryDescription onde:

Gerar um token de criação de avatar

AvatarCreationService:PromptCreateAvatarAsync() recebe um parâmetro de ID de Token de Criação de Avatar. Este token é a chave para criações do seu universo, e é o que você pode usar para definir o preço da criação do avatar da sua experiência. Para instruções e detalhes adicionais sobre geração de tokens, consulte Tokens de Criação de Avatar.

Após comprar e gerar seu token, você pode inspecionar o token no Creator Hub para encontrar o ID que você pode então usar para a API AvatarCreationService:PromptCreateAvatarAsync().

Responder a jogadores que entram por meio de atribuição

Pacotes de avatares criados em-experiência incluem um link de atribuição para a experiência original em que o avatar foi criado. Se o avatar for inspecionado por outro jogador, um prompt é exibido fornecendo uma opção para visitar a experiência onde o avatar foi criado.

Para lidar com os jogadores que entram na sua experiência usando este link de atribuição, use Player:GetJoinData() e analise a tabela retornada para GameJoinContext.

GameJoinContext inclui os seguintes valores de tabela:

©2026 Roblox Corporation, Roblox, o logotipo Roblox e Powering Imagination estão entre nossas marcas registradas e não registradas nos EUA e em outros países.