Criação de avatar na criações

*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 os jogadores Criar, personalizare comprem corpos de avatar em tempo real.Ao comprar, 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ência que implementam a criação de avatar na experiência se beneficiam de comissões do Mercado como tanto criador do item de avatar e proprietário da experiência .Se um recurso 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 na experiência na demonstração do Criador de Avatar do Roblox.

Como implementar criaçõesna experiência

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

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

  • Modelos de avatar — A seguinte implementação requer a importação de um corpo base que atenda às especificações de 15 partes da Roblox.Este Model serve como base para personalização e modificação adicional do usuário.
    • O corpo base deve atender às diretrizes de corpo de avatar do Roblox Avatar body guidelines, incluindo o número mínimo de controle de FACS para riggingfacial.
  • Tokens de criação de avatar — Experiências que implementam a criação de avatar requerem pelo menos um token de criação.Esses tokens requerem Robux para comprar 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 criação do avatar solicitando e validando.
    • EditableImage — Gerencia a criação e manipulação de texturas em tempo de execução.
    • EditableMesh — Gerencia a manipulação de tempo de execução da geometria de malha.
    • WrapDeformer — Manipulação de tempo de execução de geometria de gaiola externa invisível que permite que os personagens de avatar equipem roupas 3D.

Importar um corpo base

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

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

Para referências e amostras de corpos de avatar devidamente configurados, veja Referências de avatar.

Implementar APIs de edição

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

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


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Crie e anexe um WrapDeformer ao MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Crie uma malha editável para a malha da gaiola do alvo do envoltório
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Atribua a malha da gaiola ao WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Crie uma malha editável a partir da MeshPart original
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Gerar uma nova MeshPart a partir do malhaeditável
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Copiar o tamanho, a posição e a textura da peça original MeshPart
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Aplique a nova MeshPart de volta ao original
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Se a Imagem Editável já existe para esse ID de Textura, reutilize-a ao invés de fazer uma nova
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Crie uma nova Imagem Editável e aplique-a como conteúdo de textura
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- Mapa para reutilizar instâncias de EditableImage pela ID da textura
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- Configurar MeshPart com base na presença do WrapTarget
    -- Se o WrapTarget estiver presente, adicione um filho WrapDeformer com um EditableMesh
    -- Caso contrário, aplique EditableMesh diretamente ao MeshPart
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Configurar a Imagem Editável para a Peça de Modelo
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Crie EditableImage ferramentas que permitam que os jogadores re-coloram, desenhem ou adicionem adesivos ao seu corpo base.Você pode aproveitar APIs em como DrawImage() , DrawRectangle() , WritePixelsBuffer() .

    • Para transformações avançadas, DrawImageTransformed() permite que você especifique posição, rotação e escala ao desenhar uma Imagem Editável sobre outra.Da mesma forma, DrawImageProjected() funciona muito como 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 = (game.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 no seu corpo.

    1. WrapDeformer lida com as deformações ao vivo da geometria renderizada MeshPart enquanto mantém os dados de skinning e FACS subjacentes.

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

      1. Use WrapDeformer:SetCageMeshContent() para aplicar a instância EditableMesh que representa a malha de 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

Depois de configurar seu corpo base e editar APIs, crie um prompt para os usuários criarem e comprarem 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("Successfully uploaded with BundleId: ", bundleIdOrErrorMessage)
print("Successfully uploaded with OutfitId: ", outfitId)
else
print("Unsuccessfully uploaded with error message:", bundleIdOrErrorMessage)
end
else
print("Avatar creation unsuccessful")
end
end

AvatarCreationService:PromptCreateAvatarAsync() toma um parâmetro HumanoidDescription para representar o avatar destinado à compra ou criações.Para criaçõesde avatar, o personagem deve incluir novos recursos a serem criados para cada uma das 6 partes do corpo ( , , , , , ).Opcionalmente, também pode incluir um novo Hair acessório.

Para suportar isso, o HumanoidDescription deve incluir 6 BodyPartDescription crianças.Cada propriedade BodyPartDescription.Instance referencia um Folder que inclui todas as instâncias MeshPart que compõem a parte do corpo.Por exemplo, o diretório contém , e .A propriedade BodyPartDescription.BodyPart deve ser definida também para o relevante Enum.BodyPart.

Cada uma das 15 MeshPart partes do corpo deve incluir:

O fornecido HumanoidDescription não deve incluir quaisquer IDs de ativos pré-existentes para representar partes do corpo ou acessórios na criaçõespretendida.Por outro lado, o HumanoidDescription pode incluir as escalas humanóides de BodyTypeScale , HeadScale , HeightScale , WidthScale e ProportionScale.Tenha cuidado com as escalas com as quais um corpo base é importado para que elas correspondam às escalas fornecidas ao HumanoidDescription.

Incluir acessórios

Se incluir um acessório, como cabelo, o HumanoidDescription deve incluir uma criança AccessoryDescription onde:

Gerar um token de criação de avatar

AvatarCreationService:PromptCreateAvatarAsync() toma um parâmetro ID 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 de avatar da sua experiência.Para instruções e detalhes adicionais sobre a geração de tokens, veja Tokens de Criação de Avatar.

Depois de comprar e gerar seu token, você pode inspecionar o token no Hub do Criador para encontrar o ID que você pode então usar para a API (Interface de Programação para Aplicações)AvatarCreationService:PromptCreateAvatarAsync().

Responder a jogadores que se juntam por atribuição

Pacotes de avatar criados na 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 aviso será exibido fornecendo uma opção para visitar a experiência onde o avatar foi criado.

Para lidar com jogadores que se juntam à sua experiência usando esse víncular / conectarde atribuição, use Player:GetJoinData() e analise a tabela retornada para GameJoinContext .

GameJoinContext inclui os seguintes valores da tabela: