creacionesde avatar 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 avatar en tiempo real.Al comprar, estos cuerpos personalizados se guardan directamente en el inventario de Roblox del jugador, permitiendo a los jugadores equipar y usar los avatares personalizados en otras experiencias.

Los propietarios de experiencia que implementan la creación de avatares en experiencia se benefician de comisiones de mercado tanto como creador del artículo de avatar como propietario de experiencia .Si se inspecciona un activo creado en la experiencia, el artículo proporciona un enlace a la experiencia original en la que se creó.

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

Cómo implementar la creacionesen experiencia

Sigue las instrucciones y referencias de código siguientes para crear tu primer proyecto de creación de avatar en 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 siguiendo:

  • Modelos de avatar — La siguiente implementación requiere la importación de un cuerpo base que cumpla con las especificaciones de 15 partes de Roblox.Este Model sirve como base para la personalización y modificación adicional de usuarios.
    • El cuerpo base debe cumplir con las directrices del cuerpo de avatar de Roblox Avatar body guidelines, incluyendo el número mínimo de control de FACS para la riggingfacial.
  • Fichas de creación de avatar — Las experiencias que implementan la creación de avatares requieren al menos una ficha de creación.Estas fichas requieren Robux para comprar y te permiten establecer precios y otras configuraciones de venta para las compras realizadas en la experiencia.
  • Clases de API
    • AvatarCreationService — Maneja la creación del avatar que solicita y valida.
    • EditableImage — Maneja la creación y manipulación de la ejecución de texturas.
    • EditableMesh — Manipula la ejecución de la geometría de malla.
    • WrapDeformer — Maneja la manipulación de tiempo de ejecución de la geometría de la jaula externa invisible 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 recurso personalizado con el 3-D Importer 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 como las 15 MeshPart que constituyen 6 partes del cuerpo: cabeza, torso, brazo izquierdo, pierna izquierda, brazo derecho y pierna derecha, así como otros componentes de avatar como.

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

Implementar API de edición

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

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


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Crear y adjuntar un WrapDeformer a la MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Crea una malla editable para la malla de la jaula del objetivo de envoltura
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Asigna la malla de la jaula al WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Crear una malla editable a partir de la malla original de MeshPart
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Generar una nueva pieza de malla desde la malla editable
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Copiar el tamaño, la posición y la textura de la pieza original de malla
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Aplica la nueva MeshPart de vuelta al original
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Si EditableImage ya existe para este ID de textura, úselo de nuevo en lugar de crear uno nuevo
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Crea una nueva imagen editable y aplícala 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 en función de la presencia de WrapTarget
    -- Si WrapTarget está presente, agregue un hijo de WrapDeformer con un EditableMesh
    -- De lo contrario, aplique EditableMesh a la pieza de malla directamente
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Configurar la imagen editable para la pieza de malla
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Crea EditableImage herramientas que permitan a los jugadores recolorar, dibujar o agregar pegatinas a tu cuerpo base.Puedes aprovechar las API en como DrawImage() , DrawRectangle() , WritePixelsBuffer() .

    • Para transformaciones avanzadas, DrawImageTransformed() te permite especificar posición, rotación y escala al dibujar una imagen editable sobre otra.Del mismo modo, DrawImageProjected() funciona mucho como DrawImage() pero proyecta la imagen dibujada correctamente si la instancia EditableImage se usa 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 = (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 y EditableMesh, crea herramientas para editar deformaciones de malla en tu cuerpo.

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

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

      1. Usa WrapDeformer:SetCageMeshContent() para aplicar la instancia EditableMesh que representa la malla relevante de la jaula 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 aviso de creación

Después de configurar tu cuerpo base y editar API, crea una solicitud para que los usuarios creen y compren de la experiencia usando AvatarCreationService:PromptCreateAvatarAsync() .


export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Carpeta con piezas de malla creadas
}
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 un parámetro HumanoidDescription para representar el avatar destinado a la compra o creaciones.Para la creacionesde avatares, el personaje de HumanoidDescription debe incluir nuevos recursos para crear para cada una de las 6 partes del cuerpo (Head , Torso , RightLeg , LeftLeg , RightArm , LeftArm).Opcionalmente, también puede incluir un nuevo accesorio Hair.

Para apoyar esto, el HumanoidDescription debe incluir 6 BodyPartDescription hijos.Cada propiedad BodyPartDescription.Instance se refiere a una Folder que incluye todas las instancias MeshPart que componen la parte del cuerpo.Por ejemplo, la carpeta LeftArm contiene LeftHand , LeftUpperArm , y LeftLowerArm``Class.MeshPart|MeshParts .La propiedad BodyPartDescription.BodyPart debe establecerse también al Enum.BodyPart relevante.

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

El proporcionado HumanoidDescription no debe incluir ninguna ID de activo preexistente para representar partes del cuerpo o accesorios en la creacionespretendida.Por otro lado, el HumanoidDescription puede incluir las escalas humanoides de BodyTypeScale , HeadScale , HeightScale , WidthScale , y ProportionScale .Tenga en cuenta las escalas con las que se importa un cuerpo base para que coincidan con las escalas proporcionadas al HumanoidDescription.

Incluir accesorios

Si se incluye un accesorio, como el pelo, el HumanoidDescription debe incluir un hijo AccessoryDescription donde:

Generar una ficha de creación de avatar

AvatarCreationService:PromptCreateAvatarAsync() toma un parámetro ID de creación de avatar .Esta ficha 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 la generación de tokens, vea Fichas de creación de avatares.

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

Responder a los jugadores que se unen por atribución

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

Para manejar a los jugadores que se unen a tu experiencia usando este enlazarde atribución, usa Player:GetJoinData() y parsea la tabla devuelta para GameJoinContext .

GameJoinContext incluye los siguientes valores de tabla: