Vista general
Crear {itemType}
Monetizar

Creación de avatares en la experiencia

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Puedes publicar una experiencia que permita a los jugadores crear, personalizar y comprar cuerpos de avatares en tiempo real. Cuando se compran, estos cuerpos personalizados se guardan directamente en el inventario de Roblox del jugador, permitiendo que los jugadores equipen y usen los avatares personalizados en otras experiencias.

Los propietarios de experiencias que implementen la creación de avatares en la experiencia se benefician de comisiones del mercado tanto como creador del artículo de avatar como propietario de la experiencia. Si un activo creado en la experiencia es inspeccionado, el artículo proporciona un enlace a la experiencia original donde fue creado.

Puedes probar la creación en la experiencia en la demostración de Creador de Avatares de Roblox.

Cómo implementar la creación en la experiencia

Utiliza las siguientes instrucciones y referencias de código para crear tu primer proyecto de creación de avatares en la experiencia. Las siguientes instrucciones utilizan un cuerpo base Model que los jugadores pueden modificar y personalizar antes de publicar.

Antes de comenzar, familiarízate con lo siguiente:

  • Modelos de avatar: La siguiente implementación requiere importar un cuerpo base que cumpla con las especificaciones de 15 partes de Roblox. Este Model sirve como base para una personalización y modificación adicional por parte del usuario.
  • Tokens de creación de avatares — Las experiencias que implementan la creación de avatares requieren al menos un token de creación. Estos tokens requieren Robux para su compra y te permiten establecer precios y otras configuraciones de venta para las compras realizadas en la experiencia.
  • Clases de API
    • AvatarCreationService — Maneja la solicitud y validación de la creación de avatares.
    • EditableImage — Maneja la creación y manipulación en tiempo de ejecución de texturas.
    • EditableMesh — Maneja la manipulación en tiempo de ejecución de la geometría de mallas.
    • WrapDeformer — Maneja la manipulación en tiempo de ejecución de la geometría invisible de la malla externa que permite a los personajes de avatar equipar ropa 3D.

Importar un cuerpo base

El cuerpo base actúa como la base inicial que los usuarios pueden personalizar y editar. Puedes usar tu propio Model, o importar un activo personalizado con el Importador y configurarlo a través de Configuración de Avatar.

Los cuerpos base deben adherirse a las especificaciones de avatar de Roblox y deben incluir componentes tales como las 15 instancias de MeshPart que componen 6 partes del cuerpo: cabeza, torso, brazo izquierdo, pierna izquierda, brazo derecho y pierna derecha, así como otros componentes de avatar.

Para referencias y muestras de cuerpos de avatar configurados correctamente, consulta Referencias de Avatar.

Implementar APIs de edición

Para desarrollar un sistema donde los usuarios puedan editar las instancias de MeshPart en un avatar en tu experiencia para la creación, utiliza EditableImage para la edición de texturas, EditableMesh para la edición de mallas y WrapDeformer para mantener la piel y los datos FACS durante las ediciones de malla.

  1. Después de importar tu cuerpo base, usa el siguiente script para configurar tus EditableImages, EditableMeshes, y WrapDeformers.

    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Crear y adjuntar un WrapDeformer al MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Crear una malla editable para la malla de la jaula del objetivo de envoltura
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Asignar la malla de la jaula al WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Crear una malla editable a partir del MeshPart original
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Generar un nuevo MeshPart a partir de la malla editable
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Copiar tamaño, posición y textura del MeshPart original
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Aplicar el nuevo MeshPart de vuelta al original
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Si ya existe un EditableImage para este TextureID, reutilizalo en lugar de crear uno nuevo
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Crear un nuevo EditableImage y aplicarlo como contenido 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 instancias de EditableImage por ID de textura
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- Configurar MeshPart basado en la presencia de WrapTarget
    -- Si WrapTarget está presente, agregar un hijo WrapDeformer con una EditableMesh
    -- De lo contrario, aplicar EditableMesh al MeshPart directamente
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Configurar el EditableImage para el MeshPart
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Crea herramientas EditableImage que permitan a los jugadores recolorear, dibujar o agregar adhesivos a tu cuerpo base. Puedes aprovechar las APIs como DrawImage(), DrawRectangle(), WritePixelsBuffer().

    • Para transformaciones avanzadas, DrawImageTransformed() te permite especificar posición, rotación y escala al dibujar una EditableImage sobre otra. De manera similar, DrawImageProjected() funciona de manera similar a DrawImage(), pero proyecta la imagen dibujada correctamente si la instancia de EditableImage se utiliza con un 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 y EditableMesh, crea herramientas para editar deformaciones de mallas en tu cuerpo.

    1. WrapDeformer maneja las deformaciones en vivo de la geometría de MeshPart renderizada mientras mantiene los datos subyacentes de skinning y FACS.

    2. EditableMesh te permite modificar la malla de jaula a la que responde el WrapDeformer.

      1. Usa WrapDeformer:SetCageMeshContent() para aplicar la instancia de EditableMesh que representa la malla de jaula relevante al WrapDeformer.
      2. Usa EditableMesh, como SetPosition(), para deformar vértices y editar la forma del 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

Crear un aviso de creación

Después de configurar tu cuerpo base y las APIs de edición, crea un aviso para que los usuarios creen y compren desde la experiencia usando AvatarCreationService:PromptCreateAvatarAsync().

export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance -- Carpeta con MeshParts creados
}
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("Cargado con éxito con BundleId: ", bundleIdOrErrorMessage)
print("Cargado con éxito con OutfitId: ", outfitId)
else
print("No se pudo cargar con el mensaje de error:", bundleIdOrErrorMessage)
end
else
print("Creación de avatar no exitosa")
end
end

AvatarCreationService:PromptCreateAvatarAsync() toma un parámetro de HumanoidDescription para representar el avatar destinado a la compra o creación. Para la creación de avatares, la HumanoidDescription del personaje debe incluir nuevos activos que se crearán para cada una de las 6 partes del cuerpo (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). Opcionalmente, también puede incluir un accesorio nuevo de tipo Hair.

Para apoyar esto, la HumanoidDescription debería incluir 6 hijos de BodyPartDescription. Cada propiedad BodyPartDescription.Instance hace referencia a una Folder que incluye todas las instancias de MeshPart que componen la parte del cuerpo. Por ejemplo, la carpeta LeftArm contiene LeftHand, LeftUpperArm y LeftLowerArm MeshParts. La propiedad BodyPartDescription.BodyPart también debe establecerse en el Enum.BodyPart relevante.

Cada una de las 15 partes del cuerpo MeshPart debe incluir:

La HumanoidDescription proporcionada no debe incluir ningún ID de activos preexistentes para representar partes del cuerpo o accesorios en la creación destinada. Por otro lado, la HumanoidDescription puede incluir las escalas humanoides de BodyTypeScale, HeadScale, HeightScale, WidthScale, y ProportionScale. Ten en cuenta las escalas con las que se importa un cuerpo base para que coincidan con las escalas proporcionadas a la HumanoidDescription.

Incluir accesorios

Si incluyes un accesorio, como cabello, la HumanoidDescription debe incluir un hijo AccessoryDescription donde:

Generar un token de creación de avatar

AvatarCreationService:PromptCreateAvatarAsync() toma un parámetro de ID de Token de Creación de Avatar. Este token es la clave para las creaciones de tu universo, y es lo que puedes usar para establecer el precio de la creación de avatares desde tu experiencia. Para instrucciones y detalles adicionales sobre cómo generar tokens, consulta Tokens de Creación de Avatars.

Después de comprar y generar tu token, puedes inspeccionar el token en el Creator Hub para encontrar el ID que luego puedes usar para el API AvatarCreationService:PromptCreateAvatarAsync().

Responder a los jugadores que se unan mediante atribución

Los paquetes de avatares creados en la experiencia incluyen un enlace de atribución a la experiencia original donde se creó el avatar. Si el avatar es inspeccionado por otro jugador, se muestra un aviso que proporciona una opción para visitar la experiencia donde se creó el avatar.

Para manejar a los jugadores que se unan a tu experiencia utilizando este enlace de atribución, utiliza Player:GetJoinData() y analiza la tabla devuelta para GameJoinContext.

GameJoinContext incluye los siguientes valores de tabla:

©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.