Você pode publicar uma experiência que permite aos jogadores criar, personalizar e comprar corpos de avatar 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 dentro da experiência se beneficiam das comissões do Marketplace como criador do item de avatar e proprietário da 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 dentro da experiência na demonstração do Avatar Creator.
Como implementar a criação dentro da experiência
Use as seguintes instruções e referências de código para criar seu primeiro projeto de criação de avatar. 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 atenda às especificações de 15 peças do Roblox. Este Model serve como base para personalização e modificação adicional pelo usuário.
- O corpo base deve atender às diretrizes de corpo de avatar do Roblox, incluindo o número mínimo de controles FACS para rigging facial.
- Tokens de criação de avatar — Experiências que implementam a criação de avatar exigem 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 dentro da experiência.
- Classes de API
- AvatarCreationService — Manipula a solicitação e validação da criação de avatares.
- EditableImage — Manipula a criação e manipulação em tempo de execução de texturas.
- EditableMesh — Manipula a manipulação em tempo de execução da geometria de malha.
- WrapDeformer — Manipula a manipulação em tempo de execução da geometria da gaiola externa invisível que permite que os 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 Importer e configurar através do Avatar Setup.
Os corpos base devem atender às especificações de avatar do Roblox e 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 amostras de corpos de avatar corretamente configurados, consulte Referências de Avatar.
Implementar APIs de edição
Para desenvolver um sistema onde os usuários podem editar as instâncias de MeshPart em um avatar em sua experiência para criação, use EditableImage para edição de textura, EditableMesh para edição de malha e WrapDeformer para manter os dados de skinning e FACS durante as edições de malha.
Após 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)-- Criar e anexar um WrapDeformer ao MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Criar uma malha editável para a malha da gaiola do wrap targetlocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Atribuir a malha da gaiola ao WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Criar uma malha editável a partir do MeshPart originallocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Gerar um novo MeshPart a partir da malha editávellocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Copiar tamanho, posição e textura do MeshPart originalnewMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- Aplicar o novo MeshPart de volta ao originalmeshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- Se EditableImage já existir para este TextureID, reutilize-o em vez de criar um novoif textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- Criar uma nova EditableImage e aplicá-la como conteúdo da texturalocal editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- Mapa para reutilizar instâncias de EditableImage por ID de texturalocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- Configurar o MeshPart com base na presença do WrapTarget-- Se WrapTarget estiver presente, adicione uma criança WrapDeformer com um EditableMesh-- Caso contrário, aplique EditableMesh diretamente ao MeshPartlocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- Configurar a EditableImage para o MeshPartsetupMeshTexture(descendant, textureIdToEditableImageMap)endendCrie ferramentas EditableImage que permitem que os jogadores re-coloram, desenhem ou adicionem 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 de maneira semelhante à DrawImage() mas projeta a imagem desenhada corretamente se a instância de 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)endlocal 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)endlocal 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).Unitlocal 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
Usando WrapDeformer e EditableMesh, crie ferramentas para editar deformaçõe de malha em seu corpo.
WrapDeformer lida com as deformaçõe ao vivo da geometria renderizada do MeshPart enquanto mantém os dados de skinning e FACS subjacentes.
EditableMesh permite modificar a malha da gaiola à qual o WrapDeformer responde.
- Use WrapDeformer:SetCageMeshContent() para aplicar a instância de EditableMesh que representa a malha da gaiola relevante ao WrapDeformer.
local function deformBodyPart(meshPart: MeshPart,controlPointCenter: Vector3,controlPointRadius: number,controlPointDeformation: Vector3)local wrapTarget = meshPart:FindFirstChildWhichIsA("WrapTarget")local cageMeshId = wrapTarget.CageMeshIdlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPartlocal cageEditableMesh = AssetService:CreateEditableMeshAsync(cageMeshId)local verticesWithinSphere =cageEditableMesh:FindVerticesWithinSphere(controlPointCenter, controlPointRadius)for _, vertexId in verticesWithinSphere dolocal vertexPosition = cageEditableMesh:GetPosition(vertexId)cageEditableMesh:SetPosition(vertexId, vertexPosition + controlPointDeformation)endwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))end
Criar solicitação de criação
Após configurar seu corpo base e 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("Carregado com sucesso com BundleId: ", bundleIdOrErrorMessage)
print("Carregado com sucesso com OutfitId: ", outfitId)
else
print("Carregado sem sucesso com mensagem de erro:", bundleIdOrErrorMessage)
end
else
print("Criação de avatar não bem-sucedida")
end
endAvatarCreationService:PromptCreateAvatarAsync() leva um parâmetro HumanoidDescription para representar o avatar pretendido para compra ou criação. Para a criação de avatar, a HumanoidDescription do personagem deve incluir novos ativos a serem criados para cada uma das 6 partes do corpo (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). Opcionalmente, também pode incluir um novo acessório do tipo Hair.
Para apoiar isso, a HumanoidDescription deve incluir 6 filhos 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 LeftArm contém LeftHand, LeftUpperArm e LeftLowerArm MeshParts. A propriedade BodyPartDescription.BodyPart também deve ser configurada para o relevante Enum.BodyPart.
Cada uma das 15 partes do corpo do MeshPart deve incluir:
- Uma EditableImage.
- Um WrapDeformer com um EditableMesh.
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 BodyTypeScale, HeadScale, HeightScale, WidthScale, e ProportionScale. Preste atenção nas escalas com as quais um corpo base é importado para que elas correspondam às escalas fornecidas à HumanoidDescription.

Incluir acessórios
Se incluir um acessório, como cabelo, a HumanoidDescription deve incluir uma criança AccessoryDescription onde:
- A propriedade AccessoryDescription.Instance referencia a instância de Accessory.
- A propriedade AccessoryDescription.AccessoryType está configurada para o relevante Enum.AccessoryType.
- No caso de incluir Enum.AccessoryType.Hair em suas criações, o MeshPart deve incluir uma EditableImage. No entanto, não deve incluir uma criança WrapDeformer, mas deve incluir uma EditableMesh configurada diretamente no MeshPart.
Gerar um token de criação de avatar
AvatarCreationService:PromptCreateAvatarAsync() leva um parâmetro 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 de avatar a partir de sua experiência. Para instruções e detalhes adicionais sobre como gerar tokens, consulte Tokens de Criação de Avatar.
Depois de comprar e gerar seu token, você pode inspecionar o token no Creator Hub para encontrar o ID que você pode usar para a API AvatarCreationService:PromptCreateAvatarAsync().

Responder a jogadores que entram por atribuição
Pacotes de avatar criados dentro da 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 oferecendo uma opção para visitar a experiência em que o avatar foi criado.

Para lidar com jogadores que entram em 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:
- JoinSource — Enum.JoinSource
- Um Player se juntando à sua experiência por meio deste link de atribuição terá Enum.JoinSource.CreatedItemAttribution para indicar entrada de um item criado.
- ItemType — opcional Enum.AvatarItemType
- AssetId — opcional string
- OutfitId — opcional string
- AssetType — opcional Enum.AssetType